From bf0d77d7d448e964e9716d5af67c48f3d014f090 Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Sun, 1 Mar 2026 01:03:39 +0100 Subject: Scaffold basic project tree, implement benchmarking logic Implement unit testing guardian --- .clang-format | 31 ++++++++ .clang-tidy | 144 +++++++++++++++++++++++++++++++++ .editorconfig | 22 ++++++ .gitignore | 3 + Dockerfile | 22 ++++++ Makefile | 48 +++++++++++ app/main.c | 25 ++++++ benchmark/benchmark.c | 101 +++++++++++++++++++++++ benchmark/benchmark.h | 76 ++++++++++++++++++ benchmark/resource_usage.c | 17 ++++ benchmark/resource_usage.h | 6 ++ include/syntax_essentials.h | 81 +++++++++++++++++++ src/syntax_essentials.c | 176 +++++++++++++++++++++++++++++++++++++++++ tests/test_syntax_essentials.c | 125 +++++++++++++++++++++++++++++ 14 files changed, 877 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 app/main.c create mode 100644 benchmark/benchmark.c create mode 100644 benchmark/benchmark.h create mode 100644 benchmark/resource_usage.c create mode 100644 benchmark/resource_usage.h create mode 100644 include/syntax_essentials.h create mode 100644 src/syntax_essentials.c create mode 100644 tests/test_syntax_essentials.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..670c9d1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,31 @@ +Language: C +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Linux +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerAlignment: false +IndentCaseLabels: true +IndentWidth: 8 +KeepEmptyLinesAtTheStartOfBlocks: false +PointerAlignment: Left +SpacesBeforeTrailingComments: 1 +TabWidth: 8 +UseTab: Always diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..ecb3675 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,144 @@ +--- +Checks: '-*, +bugprone-argument-comment, +bugprone-assert-side-effect, +bugprone-bad-signal-to-kill-thread, +bugprone-branch-clone, +bugprone-copy-constructor-init, +bugprone-dangling-handle, +bugprone-dynamic-static-initializers, +bugprone-fold-init-type, +bugprone-forward-declaration-namespace, +bugprone-forwarding-reference-overload, +bugprone-inaccurate-erase, +bugprone-incorrect-roundings, +bugprone-integer-division, +bugprone-lambda-function-name, +bugprone-macro-parentheses, +bugprone-macro-repeated-side-effects, +bugprone-misplaced-operator-in-strlen-in-alloc, +bugprone-misplaced-pointer-arithmetic-in-alloc, +bugprone-misplaced-widening-cast, +bugprone-move-forwarding-reference, +bugprone-multiple-statement-macro, +bugprone-no-escape, +bugprone-parent-virtual-call, +bugprone-posix-return, +bugprone-reserved-identifier, +bugprone-sizeof-container, +bugprone-sizeof-expression, +bugprone-spuriously-wake-up-functions, +bugprone-string-constructor, +bugprone-string-integer-assignment, +bugprone-string-literal-with-embedded-nul, +bugprone-suspicious-enum-usage, +bugprone-suspicious-include, +bugprone-suspicious-memset-usage, +bugprone-suspicious-missing-comma, +bugprone-suspicious-semicolon, +bugprone-suspicious-string-compare, +bugprone-suspicious-memory-comparison, +bugprone-suspicious-realloc-usage, +bugprone-swapped-arguments, +bugprone-terminating-continue, +bugprone-throw-keyword-missing, +bugprone-too-small-loop-variable, +bugprone-undefined-memory-manipulation, +bugprone-undelegated-constructor, +bugprone-unhandled-self-assignment, +bugprone-unused-raii, +bugprone-unused-return-value, +bugprone-use-after-move, +bugprone-virtual-near-miss, +cert-dcl21-cpp, +cert-dcl58-cpp, +cert-err34-c, +cert-err52-cpp, +cert-err60-cpp, +cert-flp30-c, +cert-msc50-cpp, +cert-msc51-cpp, +cert-str34-c, +cppcoreguidelines-interfaces-global-init, +cppcoreguidelines-narrowing-conversions, +cppcoreguidelines-pro-type-member-init, +cppcoreguidelines-pro-type-static-cast-downcast, +cppcoreguidelines-slicing, +google-default-arguments, +google-runtime-operator, +hicpp-exception-baseclass, +hicpp-multiway-paths-covered, +misc-misplaced-const, +misc-new-delete-overloads, +misc-non-copyable-objects, +misc-throw-by-value-catch-by-reference, +misc-unconventional-assign-operator, +misc-uniqueptr-reset-release, +modernize-avoid-bind, +modernize-concat-nested-namespaces, +modernize-deprecated-headers, +modernize-deprecated-ios-base-aliases, +modernize-loop-convert, +modernize-make-shared, +modernize-make-unique, +modernize-pass-by-value, +modernize-raw-string-literal, +modernize-redundant-void-arg, +modernize-replace-auto-ptr, +modernize-replace-disallow-copy-and-assign-macro, +modernize-replace-random-shuffle, +modernize-return-braced-init-list, +modernize-shrink-to-fit, +modernize-unary-static-assert, +modernize-use-auto, +modernize-use-bool-literals, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-equals-delete, +modernize-use-nodiscard, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-override, +modernize-use-transparent-functors, +modernize-use-uncaught-exceptions, +mpi-buffer-deref, +mpi-type-mismatch, +openmp-use-default-none, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +portability-simd-intrinsics, +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-size-empty, +readability-convert-member-functions-to-static, +readability-delete-null-pointer, +readability-deleted-default, +readability-inconsistent-declaration-parameter-name, +readability-make-member-function-const, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-non-const-parameter, +readability-redundant-control-flow, +readability-redundant-declaration, +readability-redundant-function-ptr-dereference, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-redundant-string-init, +readability-simplify-subscript-expr, +readability-static-accessed-through-instance, +readability-static-definition-in-anonymous-namespace, +readability-string-compare, +readability-uniqueptr-delete-release, +readability-use-anyofallof' \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..00c8da6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig dla Embedded C Project +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 + +[*.c] +indent_style = tab +indent_size = 8 + +[*.h] +indent_style = tab +indent_size = 8 + +[Makefile] +indent_style = tab +indent_size = 8 +max_line_length = 120 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2299d85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +.env +.idea/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..30aceae --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# ================= BUILD STAGE ================= +FROM alpine:3.19 AS builder +WORKDIR /project + +# Instalacja gcc/make (tylko build stage) +RUN apk add --no-cache gcc g++ make build-base bash + +COPY . . + +# Budowa statycznego hostowego binarza +RUN make clean && \ + CC="gcc" CFLAGS="-Wall -Wextra -Werror -std=c18 -Iinclude -Ibenchmark -static" make host + +# ================= FINAL STAGE ================= +FROM scratch +WORKDIR /project + +# Skopiuj statyczny binarz +COPY --from=builder /project/build/syntax_essentials_tests_host /syntax_essentials_tests_host + +# ENTRYPOINT +ENTRYPOINT ["/syntax_essentials_tests_host"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4d0bb31 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# ================= COMPILER SETTINGS ================= +CC ?= gcc +CFLAGS ?= -Wall -Wextra -Werror -std=c18 -Iinclude -Ibenchmark +LDFLAGS ?= + +SRC_DIR := src +TEST_DIR := tests +BENCH_DIR := benchmark +BUILD_DIR := build + +SRC := $(SRC_DIR)/syntax_essentials.c benchmark/benchmark.c benchmark/resource_usage.c +TESTS := $(wildcard $(TEST_DIR)/*.c) + +OUT_HOST := $(BUILD_DIR)/syntax_essentials_tests_host + +# ================= TARGETS ================= +all: host + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# ---------------- HOST BUILD (static) ---------------- +host: $(BUILD_DIR) $(SRC) $(TESTS) + $(CC) $(CFLAGS) -static $(SRC) $(TESTS) -o $(OUT_HOST) $(LDFLAGS) + @echo "[INFO] Host binary built: $(OUT_HOST)" + +# ---------------- ESP32 SIM ---------------- +esp32_sim: host + @echo "[INFO] Running ESP32-S3 simulation in minimal Docker..." + docker build -t esp32-s3-sim . + docker run --rm --memory=6m --cpus=0.2 esp32-s3-sim + +# ---------------- CLEAN ---------------- +clean: + rm -rf $(BUILD_DIR) + +# ---------------- INFO ---------------- +info: + @echo "Host compiler: $(CC)" + @echo "Binary: $(OUT_HOST)" + +# ---------------- HELP ---------------- +help: + @echo "Available targets:" + @echo " all / host - Build host binary (statically linked)" + @echo " esp32_sim - Build host binary + run ESP32-S3 Docker simulation" + @echo " clean - Remove build artifacts" + @echo " info - Show compiler and binary info" diff --git a/app/main.c b/app/main.c new file mode 100644 index 0000000..dd93e39 --- /dev/null +++ b/app/main.c @@ -0,0 +1,25 @@ +#include "gatekeeper.h" +#include + +int main(void) { + char name[50]; + char index[20]; + + printf("=== Embedded Gatekeeper ===\n"); + + printf("Enter name: "); + scanf("%49s", name); + + printf("Enter index (6 digits): "); + scanf("%19s", index); + + int result = register_student(name, index); + + if (result == 0) { + printf("\nAccess granted.\n"); + } else { + printf("\nAccess denied (error code %d).\n", result); + } + + return 0; +} diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c new file mode 100644 index 0000000..2fe3163 --- /dev/null +++ b/benchmark/benchmark.c @@ -0,0 +1,101 @@ +#define _POSIX_C_SOURCE 199309L +#include "benchmark.h" +#include "resource_usage.h" +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +/* ANSI color codes */ +const char* COLOR_RED = "\033[31m"; +const char* COLOR_GREEN = "\033[32m"; +const char* COLOR_YELLOW = "\033[33m"; +const char* COLOR_RESET = "\033[0m"; + +/* Maximum allowed average execution time per call in milliseconds */ +const double MAX_ALLOWED_MS = 0.05; + +/* Default benchmark iteration counts */ +const size_t DEFAULT_BENCHMARKS = 100; +const size_t ITERATIONS_FAST = 1000; +const size_t ITERATIONS_SLOW = 10; + +/* Volatile sink to prevent compiler optimizations removing the function call */ +static volatile int sink; + +/** + * @brief Cross-platform monotonic time in milliseconds + */ +static double now_ms(void) +{ +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER freq, counter; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + return (double)counter.QuadPart * 1000.0 / (double)freq.QuadPart; +#else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6; +#endif +} + +double benchmark_func(const function_to_benchmark func, + const char* arg, + size_t benchmarks) +{ + if (!func) + return 0.0; + if (benchmarks == 0) + benchmarks = DEFAULT_BENCHMARKS; + size_t warmup = benchmarks / 10; + if (warmup > 1000) + warmup = 1000; + for (size_t i = 0; i < warmup; ++i) + sink = func(arg); + const double start = now_ms(); + for (size_t i = 0; i < benchmarks; ++i) + sink = func(arg); + const double end = now_ms(); + return (end - start) / benchmarks; +} + +void report_result(const char* test_name, + const int passed, + const double avg_ms, + const double max_allowed_ms) +{ + const int is_pass = passed && (avg_ms <= max_allowed_ms); + const char* status_color = is_pass ? COLOR_GREEN : COLOR_RED; + const char* status_text = is_pass ? "PASS" : "FAIL"; + + if (!is_pass && avg_ms > max_allowed_ms) + status_text = "SLOW"; + + printf("[avg %.6f ms] %s%s: %s%s\n", + avg_ms, + status_color, + status_text, + test_name, + COLOR_RESET); + + if (!is_pass && avg_ms > max_allowed_ms) { + printf(" %sReason:%s exceeded %.3f ms limit\n", + COLOR_YELLOW, + COLOR_RESET, + max_allowed_ms); + } +} + +void benchmark_func_with_resources(const function_to_benchmark func, + const char* arg, + const size_t benchmarks, + const char* label) +{ + const double avg_ms = benchmark_func(func, arg, benchmarks); + printf("[Benchmark] %s avg %.6f ms\n", label, avg_ms); + print_resource_usage(label); +} diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h new file mode 100644 index 0000000..a47e2a6 --- /dev/null +++ b/benchmark/benchmark.h @@ -0,0 +1,76 @@ +#pragma once +/** + * @file benchmark.h + * @brief High-resolution benchmark helper for embedded IoT/student functions. + * + * Provides functions to measure average execution time, report results + * with colors, and mark slow tests. + * + * PASS CRITERIA: + * - The function returns expected result + * - Average execution time <= MAX_ALLOWED_MS + * + * WCET STRATEGY: + * - Average latency is measured; for worst-case, run representative + * worst-case inputs, measure maximum, and apply safety margin. + * + * CROSS-PLATFORM: + * - Linux / POSIX: uses clock_gettime(CLOCK_MONOTONIC) + * - Windows: uses QueryPerformanceCounter + */ + +#include + +/** ANSI color codes for terminal output */ +extern const char *COLOR_RED; +extern const char *COLOR_GREEN; +extern const char *COLOR_YELLOW; +extern const char *COLOR_RESET; + +/** Default maximum allowed average execution time in milliseconds */ +extern const double MAX_ALLOWED_MS; + +/** Default benchmark iteration counts */ +extern const size_t DEFAULT_BENCHMARKS; +extern const size_t ITERATIONS_FAST; +extern const size_t ITERATIONS_SLOW; + +/** Function pointer type for benchmarked functions */ +typedef int (*function_to_benchmark)(const char *arg); + +/** + * @brief Measure average execution time of a function. + * + * Runs the function `benchmarks` times, with warm-up, and returns + * average latency in milliseconds. + * + * @param func Function to benchmark. + * @param arg Argument to pass to function. + * @param benchmarks Number of iterations; 0 = DEFAULT_BENCHMARKS. + * @return Average latency per call in milliseconds. + */ +double benchmark_func(function_to_benchmark func, const char *arg, + size_t benchmarks); + +/** + * @brief Print benchmark result with colors and performance threshold. + * + * If passed = 0 OR avg_ms > max_allowed_ms, result is marked FAIL/SLOW. + * + * @param test_name Name of test. + * @param passed Function correctness result (1 = ok, 0 = fail) + * @param avg_ms Measured average latency + * @param max_allowed_ms Threshold for marking slow + */ +void report_result(const char *test_name, int passed, double avg_ms, + double max_allowed_ms); + +/** + * @brief + * @param func + * @param arg + * @param benchmarks + * @param label + */ +void benchmark_func_with_resources(function_to_benchmark func, const char *arg, + size_t benchmarks, const char *label); diff --git a/benchmark/resource_usage.c b/benchmark/resource_usage.c new file mode 100644 index 0000000..04f7195 --- /dev/null +++ b/benchmark/resource_usage.c @@ -0,0 +1,17 @@ +#include "resource_usage.h" +#include +#include +#include + +void print_resource_usage(const char *label) { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + long mem_kb = usage.ru_maxrss; + double user_sec = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6; + double sys_sec = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6; + printf("[%s] CPU user: %.3f s, system: %.3f s, peak memory: %ld KB\n", + label, user_sec, sys_sec, mem_kb); + } else { + printf("[%s] Resource usage not available\n", label); + } +} diff --git a/benchmark/resource_usage.h b/benchmark/resource_usage.h new file mode 100644 index 0000000..7e2015c --- /dev/null +++ b/benchmark/resource_usage.h @@ -0,0 +1,6 @@ +#pragma once +/** + * + * @param label + */ +void print_resource_usage(const char *label); diff --git a/include/syntax_essentials.h b/include/syntax_essentials.h new file mode 100644 index 0000000..181cb99 --- /dev/null +++ b/include/syntax_essentials.h @@ -0,0 +1,81 @@ +#pragma once + +/** + * + * TASK 1: validate_first_name + * --------------------------- + * Check if the first name is valid. + * - Cannot be NULL or empty + * - Only letters (a-z, A-Z) + * - Minimum length: 2 + */ +int validate_first_name(const char *first_name); + +/** + * TASK 2: validate_last_name + * -------------------------- + * Check if the last name is valid. + * - Cannot be NULL or empty + * - Only letters (a-z, A-Z) + * - Minimum length: 2 + */ +int validate_last_name(const char *last_name); + +/** + * TASK 3: validate_full_name + * -------------------------- + * Check if a full name "First Last" is valid. + * Use TASK 1 and TASK 2 internally. + */ +int validate_full_name(const char *full_name); + +/** + * TASK 4: validate_index + * ---------------------- + * Check if a student index is valid. + * - Exactly 6 digits + */ +int validate_index(const char *index); + +/** + * TASK 5: register_student + * ------------------------ + * Register a student given full name and index. + * Return codes: + * 0 -> valid + * 1 -> invalid name + * 2 -> invalid index + */ +int register_student(const char *full_name, const char *index); + +/** + * TASK 6: name_length + * ------------------ + * Return length of the string (do not use strlen). + */ +int name_length(const char *name); + +/** + * TASK 7: student struct + * --------------------- + * Simple student structure + */ +typedef struct { + char full_name[64]; + char index[16]; +} Student; + +/** + * TASK 8: add_student + * ------------------- + * Add a student to an array of Student. Return 0 if added. + */ +int add_student(Student *array, int max_size, const char *name, + const char *index); + +/** + * TASK 9: get_error_message + * ------------------------ + * Return error message string for code returned by register_student + */ +const char *get_error_message(int code); diff --git a/src/syntax_essentials.c b/src/syntax_essentials.c new file mode 100644 index 0000000..419996a --- /dev/null +++ b/src/syntax_essentials.c @@ -0,0 +1,176 @@ +#include "syntax_essentials.h" +#include + +/** + * + * TASK 1: validate_first_name + * Check if the first name is valid. + * - Cannot be NULL or empty + * - Only letters (a-z, A-Z) + * - Minimum length: 2 + * + * @param first_name + * @return + */ +int validate_first_name(const char* first_name) +{ + if (!first_name) + return 0; + + int length = 0; + for (const char* p = first_name; *p; p++) { + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) + length++; + else + return 0; + } + return length >= 2; +} + +/** + * + * TASK 2: validate_last_name + * Check if the last name is valid. + * - Cannot be NULL or empty + * - Only letters (a-z, A-Z) + * - Minimum length: 2 + * + * @param last_name + * @return + */ +int validate_last_name(const char* last_name) +{ + if (!last_name) + return 0; + + int length = 0; + + for (const char* p = last_name; *p; p++) { + if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) + length++; + else + return 0; + } + + return length >= 2; +} + +/** + * + * TASK 3: validate_full_name + * Check if a full name "First Last" is valid. + * Use TASK 1 and TASK 2 internally. + * @param full_name + * @return + */ +int validate_full_name(const char* full_name) +{ + if (!full_name) + return 0; + + char first_name[64], last_name[64]; + if (sscanf(full_name, "%63s %63s", first_name, last_name) != 2) + return 0; + + return validate_first_name(first_name) && validate_last_name(last_name); +} + +/** + * + * @param index + * @return + */ +int validate_index(const char* index) +{ + if (!index) + return 0; + + int length = 0; + for (const char* p = index; *p; p++) { + if (*p >= '0' && *p <= '9') + length++; + else + return 0; + } + + return length == 6; +} + +/** + * + * @param full_name + * @param index + * @return + */ +int register_student(const char* full_name, const char* index) +{ + if (!validate_full_name(full_name)) + return 1; + if (!validate_index(index)) + return 2; + return 0; +} + +/** + * + * @param name + * @return + */ +int name_length(const char* name) +{ + if (!name) + return 0; + + int length = 0; + while (*name++) + length++; + return length; +} + +/** + * + * @param array + * @param max_size + * @param name + * @param index + * @return + */ +int add_student(Student* array, + const int max_size, + const char* name, + const char* index) +{ + if (!name || !index || !array) + return 2; + + int len = 0; + while (len < max_size && array[len].full_name[0] != '\0') + len++; + + if (len >= max_size) + return 1; + + snprintf( + array[len].full_name, sizeof(array[len].full_name), "%s", name); + snprintf(array[len].index, sizeof(array[len].index), "%s", index); + return 0; +} + +/** + * + * @param code + * @return + */ +const char* get_error_message(const int code) +{ + switch (code) { + case 0: + return "Success"; + case 1: + return "Invalid name"; + case 2: + return "Invalid index"; + default: + return "Unknown error"; + } +} diff --git a/tests/test_syntax_essentials.c b/tests/test_syntax_essentials.c new file mode 100644 index 0000000..e0f0744 --- /dev/null +++ b/tests/test_syntax_essentials.c @@ -0,0 +1,125 @@ +#include "benchmark.h" +#include "syntax_essentials.h" +#include + +static int total_tests = 0; +static int passed_tests = 0; +static int any_failure = 0; + +/** + * + * @param name + * @param func + * @param arg + * @param expected + * @param iterations + */ +static void run_test(const char* name, + const function_to_benchmark func, + const char* arg, + const int expected, + const size_t iterations) +{ + total_tests++; + + const double avg_ms = benchmark_func(func, arg, iterations); + const int result = func(arg); + + const int logic_ok = (result == expected); + const int perf_ok = (avg_ms <= MAX_ALLOWED_MS); + const int ok = logic_ok && perf_ok; + + if (ok) + passed_tests++; + else + any_failure = 1; + + report_result(name, ok, avg_ms, MAX_ALLOWED_MS); +} + +/** + * + */ +static void test_names(void) +{ + run_test("validate_first_name: valid", + validate_first_name, + "Jan", + 1, + ITERATIONS_FAST); + run_test("validate_first_name: too short", + validate_first_name, + "J", + 0, + ITERATIONS_FAST); + run_test("validate_last_name: valid", + validate_last_name, + "Kowalski", + 1, + ITERATIONS_FAST); + run_test("validate_last_name: invalid char", + validate_last_name, + "Kowalski3", + 0, + ITERATIONS_FAST); +} + +/** + * + */ +static void test_full_name(void) +{ + run_test("validate_full_name: valid", + validate_full_name, + "Jan Kowalski", + 1, + ITERATIONS_SLOW); + run_test("validate_full_name: invalid first", + validate_full_name, + "J Kowalski", + 0, + ITERATIONS_SLOW); +} + +/** + * + */ +static void test_index(void) +{ + run_test("validate_index: valid", + validate_index, + "123456", + 1, + ITERATIONS_FAST); + run_test("validate_index: too short", + validate_index, + "12345", + 0, + ITERATIONS_FAST); +} + +/** + * + * @return + */ +int main(void) +{ + printf("=== Embedded Syntax Essentials Test Suite ===\n"); + + test_names(); + test_full_name(); + test_index(); + + printf("\n====================================\n"); + printf("Tests passed: %d / %d\n", passed_tests, total_tests); + + if (!any_failure) { + printf( + "%sALL CRITERIA MET — PASS%s\n", COLOR_GREEN, COLOR_RESET); + return 0; + } + printf("%sSUITE FAILED — performance or logic violation%s\n", + COLOR_RED, + COLOR_RESET); + return 1; +} -- cgit v1.2.3