summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Wandzio <contact@philw.dev>2026-03-01 01:03:39 +0100
committerFilip Wandzio <contact@philw.dev>2026-03-01 01:03:39 +0100
commitbf0d77d7d448e964e9716d5af67c48f3d014f090 (patch)
treee55f1e91a8c20cd737dfb01dc12a954c25711e01
downloadembedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.tar.gz
embedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.zip
Scaffold basic project tree, implement benchmarking logic
Implement unit testing guardian
Diffstat (limited to '')
-rw-r--r--.clang-format31
-rw-r--r--.clang-tidy144
-rw-r--r--.editorconfig22
-rw-r--r--.gitignore3
-rw-r--r--Dockerfile22
-rw-r--r--Makefile48
-rw-r--r--app/main.c25
-rw-r--r--benchmark/benchmark.c101
-rw-r--r--benchmark/benchmark.h76
-rw-r--r--benchmark/resource_usage.c17
-rw-r--r--benchmark/resource_usage.h6
-rw-r--r--include/syntax_essentials.h81
-rw-r--r--src/syntax_essentials.c176
-rw-r--r--tests/test_syntax_essentials.c125
14 files changed, 877 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..670c9d1
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,31 @@
1Language: C
2BasedOnStyle: LLVM
3AccessModifierOffset: -4
4AlignAfterOpenBracket: Align
5AlignConsecutiveAssignments: false
6AlignConsecutiveDeclarations: false
7AlignEscapedNewlines: Left
8AlignOperands: true
9AlignTrailingComments: true
10AllowAllParametersOfDeclarationOnNextLine: false
11AllowShortCaseLabelsOnASingleLine: false
12AllowShortFunctionsOnASingleLine: InlineOnly
13AllowShortIfStatementsOnASingleLine: false
14AllowShortLoopsOnASingleLine: false
15AlwaysBreakAfterReturnType: None
16BinPackArguments: false
17BinPackParameters: false
18BreakBeforeBinaryOperators: NonAssignment
19BreakBeforeBraces: Linux
20BreakBeforeTernaryOperators: true
21BreakConstructorInitializersBeforeComma: true
22ColumnLimit: 80
23ConstructorInitializerAllOnOneLineOrOnePerLine: true
24DerivePointerAlignment: false
25IndentCaseLabels: true
26IndentWidth: 8
27KeepEmptyLinesAtTheStartOfBlocks: false
28PointerAlignment: Left
29SpacesBeforeTrailingComments: 1
30TabWidth: 8
31UseTab: 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 @@
1---
2Checks: '-*,
3bugprone-argument-comment,
4bugprone-assert-side-effect,
5bugprone-bad-signal-to-kill-thread,
6bugprone-branch-clone,
7bugprone-copy-constructor-init,
8bugprone-dangling-handle,
9bugprone-dynamic-static-initializers,
10bugprone-fold-init-type,
11bugprone-forward-declaration-namespace,
12bugprone-forwarding-reference-overload,
13bugprone-inaccurate-erase,
14bugprone-incorrect-roundings,
15bugprone-integer-division,
16bugprone-lambda-function-name,
17bugprone-macro-parentheses,
18bugprone-macro-repeated-side-effects,
19bugprone-misplaced-operator-in-strlen-in-alloc,
20bugprone-misplaced-pointer-arithmetic-in-alloc,
21bugprone-misplaced-widening-cast,
22bugprone-move-forwarding-reference,
23bugprone-multiple-statement-macro,
24bugprone-no-escape,
25bugprone-parent-virtual-call,
26bugprone-posix-return,
27bugprone-reserved-identifier,
28bugprone-sizeof-container,
29bugprone-sizeof-expression,
30bugprone-spuriously-wake-up-functions,
31bugprone-string-constructor,
32bugprone-string-integer-assignment,
33bugprone-string-literal-with-embedded-nul,
34bugprone-suspicious-enum-usage,
35bugprone-suspicious-include,
36bugprone-suspicious-memset-usage,
37bugprone-suspicious-missing-comma,
38bugprone-suspicious-semicolon,
39bugprone-suspicious-string-compare,
40bugprone-suspicious-memory-comparison,
41bugprone-suspicious-realloc-usage,
42bugprone-swapped-arguments,
43bugprone-terminating-continue,
44bugprone-throw-keyword-missing,
45bugprone-too-small-loop-variable,
46bugprone-undefined-memory-manipulation,
47bugprone-undelegated-constructor,
48bugprone-unhandled-self-assignment,
49bugprone-unused-raii,
50bugprone-unused-return-value,
51bugprone-use-after-move,
52bugprone-virtual-near-miss,
53cert-dcl21-cpp,
54cert-dcl58-cpp,
55cert-err34-c,
56cert-err52-cpp,
57cert-err60-cpp,
58cert-flp30-c,
59cert-msc50-cpp,
60cert-msc51-cpp,
61cert-str34-c,
62cppcoreguidelines-interfaces-global-init,
63cppcoreguidelines-narrowing-conversions,
64cppcoreguidelines-pro-type-member-init,
65cppcoreguidelines-pro-type-static-cast-downcast,
66cppcoreguidelines-slicing,
67google-default-arguments,
68google-runtime-operator,
69hicpp-exception-baseclass,
70hicpp-multiway-paths-covered,
71misc-misplaced-const,
72misc-new-delete-overloads,
73misc-non-copyable-objects,
74misc-throw-by-value-catch-by-reference,
75misc-unconventional-assign-operator,
76misc-uniqueptr-reset-release,
77modernize-avoid-bind,
78modernize-concat-nested-namespaces,
79modernize-deprecated-headers,
80modernize-deprecated-ios-base-aliases,
81modernize-loop-convert,
82modernize-make-shared,
83modernize-make-unique,
84modernize-pass-by-value,
85modernize-raw-string-literal,
86modernize-redundant-void-arg,
87modernize-replace-auto-ptr,
88modernize-replace-disallow-copy-and-assign-macro,
89modernize-replace-random-shuffle,
90modernize-return-braced-init-list,
91modernize-shrink-to-fit,
92modernize-unary-static-assert,
93modernize-use-auto,
94modernize-use-bool-literals,
95modernize-use-emplace,
96modernize-use-equals-default,
97modernize-use-equals-delete,
98modernize-use-nodiscard,
99modernize-use-noexcept,
100modernize-use-nullptr,
101modernize-use-override,
102modernize-use-transparent-functors,
103modernize-use-uncaught-exceptions,
104mpi-buffer-deref,
105mpi-type-mismatch,
106openmp-use-default-none,
107performance-faster-string-find,
108performance-for-range-copy,
109performance-implicit-conversion-in-loop,
110performance-inefficient-algorithm,
111performance-inefficient-string-concatenation,
112performance-inefficient-vector-operation,
113performance-move-const-arg,
114performance-move-constructor-init,
115performance-no-automatic-move,
116performance-noexcept-move-constructor,
117performance-trivially-destructible,
118performance-type-promotion-in-math-fn,
119performance-unnecessary-copy-initialization,
120performance-unnecessary-value-param,
121portability-simd-intrinsics,
122readability-avoid-const-params-in-decls,
123readability-const-return-type,
124readability-container-size-empty,
125readability-convert-member-functions-to-static,
126readability-delete-null-pointer,
127readability-deleted-default,
128readability-inconsistent-declaration-parameter-name,
129readability-make-member-function-const,
130readability-misleading-indentation,
131readability-misplaced-array-index,
132readability-non-const-parameter,
133readability-redundant-control-flow,
134readability-redundant-declaration,
135readability-redundant-function-ptr-dereference,
136readability-redundant-smartptr-get,
137readability-redundant-string-cstr,
138readability-redundant-string-init,
139readability-simplify-subscript-expr,
140readability-static-accessed-through-instance,
141readability-static-definition-in-anonymous-namespace,
142readability-string-compare,
143readability-uniqueptr-delete-release,
144readability-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 @@
1# EditorConfig dla Embedded C Project
2root = true
3
4[*]
5charset = utf-8
6end_of_line = lf
7insert_final_newline = true
8trim_trailing_whitespace = true
9max_line_length = 80
10
11[*.c]
12indent_style = tab
13indent_size = 8
14
15[*.h]
16indent_style = tab
17indent_size = 8
18
19[Makefile]
20indent_style = tab
21indent_size = 8
22max_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 @@
1build/
2.env
3.idea/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..30aceae
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,22 @@
1# ================= BUILD STAGE =================
2FROM alpine:3.19 AS builder
3WORKDIR /project
4
5# Instalacja gcc/make (tylko build stage)
6RUN apk add --no-cache gcc g++ make build-base bash
7
8COPY . .
9
10# Budowa statycznego hostowego binarza
11RUN make clean && \
12 CC="gcc" CFLAGS="-Wall -Wextra -Werror -std=c18 -Iinclude -Ibenchmark -static" make host
13
14# ================= FINAL STAGE =================
15FROM scratch
16WORKDIR /project
17
18# Skopiuj statyczny binarz
19COPY --from=builder /project/build/syntax_essentials_tests_host /syntax_essentials_tests_host
20
21# ENTRYPOINT
22ENTRYPOINT ["/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 @@
1# ================= COMPILER SETTINGS =================
2CC ?= gcc
3CFLAGS ?= -Wall -Wextra -Werror -std=c18 -Iinclude -Ibenchmark
4LDFLAGS ?=
5
6SRC_DIR := src
7TEST_DIR := tests
8BENCH_DIR := benchmark
9BUILD_DIR := build
10
11SRC := $(SRC_DIR)/syntax_essentials.c benchmark/benchmark.c benchmark/resource_usage.c
12TESTS := $(wildcard $(TEST_DIR)/*.c)
13
14OUT_HOST := $(BUILD_DIR)/syntax_essentials_tests_host
15
16# ================= TARGETS =================
17all: host
18
19$(BUILD_DIR):
20 mkdir -p $(BUILD_DIR)
21
22# ---------------- HOST BUILD (static) ----------------
23host: $(BUILD_DIR) $(SRC) $(TESTS)
24 $(CC) $(CFLAGS) -static $(SRC) $(TESTS) -o $(OUT_HOST) $(LDFLAGS)
25 @echo "[INFO] Host binary built: $(OUT_HOST)"
26
27# ---------------- ESP32 SIM ----------------
28esp32_sim: host
29 @echo "[INFO] Running ESP32-S3 simulation in minimal Docker..."
30 docker build -t esp32-s3-sim .
31 docker run --rm --memory=6m --cpus=0.2 esp32-s3-sim
32
33# ---------------- CLEAN ----------------
34clean:
35 rm -rf $(BUILD_DIR)
36
37# ---------------- INFO ----------------
38info:
39 @echo "Host compiler: $(CC)"
40 @echo "Binary: $(OUT_HOST)"
41
42# ---------------- HELP ----------------
43help:
44 @echo "Available targets:"
45 @echo " all / host - Build host binary (statically linked)"
46 @echo " esp32_sim - Build host binary + run ESP32-S3 Docker simulation"
47 @echo " clean - Remove build artifacts"
48 @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 @@
1#include "gatekeeper.h"
2#include <stdio.h>
3
4int main(void) {
5 char name[50];
6 char index[20];
7
8 printf("=== Embedded Gatekeeper ===\n");
9
10 printf("Enter name: ");
11 scanf("%49s", name);
12
13 printf("Enter index (6 digits): ");
14 scanf("%19s", index);
15
16 int result = register_student(name, index);
17
18 if (result == 0) {
19 printf("\nAccess granted.\n");
20 } else {
21 printf("\nAccess denied (error code %d).\n", result);
22 }
23
24 return 0;
25}
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 @@
1#define _POSIX_C_SOURCE 199309L
2#include "benchmark.h"
3#include "resource_usage.h"
4#include <stdio.h>
5
6#if defined(_WIN32) || defined(_WIN64)
7#include <windows.h>
8#else
9#include <time.h>
10#endif
11
12/* ANSI color codes */
13const char* COLOR_RED = "\033[31m";
14const char* COLOR_GREEN = "\033[32m";
15const char* COLOR_YELLOW = "\033[33m";
16const char* COLOR_RESET = "\033[0m";
17
18/* Maximum allowed average execution time per call in milliseconds */
19const double MAX_ALLOWED_MS = 0.05;
20
21/* Default benchmark iteration counts */
22const size_t DEFAULT_BENCHMARKS = 100;
23const size_t ITERATIONS_FAST = 1000;
24const size_t ITERATIONS_SLOW = 10;
25
26/* Volatile sink to prevent compiler optimizations removing the function call */
27static volatile int sink;
28
29/**
30 * @brief Cross-platform monotonic time in milliseconds
31 */
32static double now_ms(void)
33{
34#if defined(_WIN32) || defined(_WIN64)
35 LARGE_INTEGER freq, counter;
36 QueryPerformanceFrequency(&freq);
37 QueryPerformanceCounter(&counter);
38 return (double)counter.QuadPart * 1000.0 / (double)freq.QuadPart;
39#else
40 struct timespec ts;
41 clock_gettime(CLOCK_MONOTONIC, &ts);
42 return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;
43#endif
44}
45
46double benchmark_func(const function_to_benchmark func,
47 const char* arg,
48 size_t benchmarks)
49{
50 if (!func)
51 return 0.0;
52 if (benchmarks == 0)
53 benchmarks = DEFAULT_BENCHMARKS;
54 size_t warmup = benchmarks / 10;
55 if (warmup > 1000)
56 warmup = 1000;
57 for (size_t i = 0; i < warmup; ++i)
58 sink = func(arg);
59 const double start = now_ms();
60 for (size_t i = 0; i < benchmarks; ++i)
61 sink = func(arg);
62 const double end = now_ms();
63 return (end - start) / benchmarks;
64}
65
66void report_result(const char* test_name,
67 const int passed,
68 const double avg_ms,
69 const double max_allowed_ms)
70{
71 const int is_pass = passed && (avg_ms <= max_allowed_ms);
72 const char* status_color = is_pass ? COLOR_GREEN : COLOR_RED;
73 const char* status_text = is_pass ? "PASS" : "FAIL";
74
75 if (!is_pass && avg_ms > max_allowed_ms)
76 status_text = "SLOW";
77
78 printf("[avg %.6f ms] %s%s: %s%s\n",
79 avg_ms,
80 status_color,
81 status_text,
82 test_name,
83 COLOR_RESET);
84
85 if (!is_pass && avg_ms > max_allowed_ms) {
86 printf(" %sReason:%s exceeded %.3f ms limit\n",
87 COLOR_YELLOW,
88 COLOR_RESET,
89 max_allowed_ms);
90 }
91}
92
93void benchmark_func_with_resources(const function_to_benchmark func,
94 const char* arg,
95 const size_t benchmarks,
96 const char* label)
97{
98 const double avg_ms = benchmark_func(func, arg, benchmarks);
99 printf("[Benchmark] %s avg %.6f ms\n", label, avg_ms);
100 print_resource_usage(label);
101}
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 @@
1#pragma once
2/**
3 * @file benchmark.h
4 * @brief High-resolution benchmark helper for embedded IoT/student functions.
5 *
6 * Provides functions to measure average execution time, report results
7 * with colors, and mark slow tests.
8 *
9 * PASS CRITERIA:
10 * - The function returns expected result
11 * - Average execution time <= MAX_ALLOWED_MS
12 *
13 * WCET STRATEGY:
14 * - Average latency is measured; for worst-case, run representative
15 * worst-case inputs, measure maximum, and apply safety margin.
16 *
17 * CROSS-PLATFORM:
18 * - Linux / POSIX: uses clock_gettime(CLOCK_MONOTONIC)
19 * - Windows: uses QueryPerformanceCounter
20 */
21
22#include <stddef.h>
23
24/** ANSI color codes for terminal output */
25extern const char *COLOR_RED;
26extern const char *COLOR_GREEN;
27extern const char *COLOR_YELLOW;
28extern const char *COLOR_RESET;
29
30/** Default maximum allowed average execution time in milliseconds */
31extern const double MAX_ALLOWED_MS;
32
33/** Default benchmark iteration counts */
34extern const size_t DEFAULT_BENCHMARKS;
35extern const size_t ITERATIONS_FAST;
36extern const size_t ITERATIONS_SLOW;
37
38/** Function pointer type for benchmarked functions */
39typedef int (*function_to_benchmark)(const char *arg);
40
41/**
42 * @brief Measure average execution time of a function.
43 *
44 * Runs the function `benchmarks` times, with warm-up, and returns
45 * average latency in milliseconds.
46 *
47 * @param func Function to benchmark.
48 * @param arg Argument to pass to function.
49 * @param benchmarks Number of iterations; 0 = DEFAULT_BENCHMARKS.
50 * @return Average latency per call in milliseconds.
51 */
52double benchmark_func(function_to_benchmark func, const char *arg,
53 size_t benchmarks);
54
55/**
56 * @brief Print benchmark result with colors and performance threshold.
57 *
58 * If passed = 0 OR avg_ms > max_allowed_ms, result is marked FAIL/SLOW.
59 *
60 * @param test_name Name of test.
61 * @param passed Function correctness result (1 = ok, 0 = fail)
62 * @param avg_ms Measured average latency
63 * @param max_allowed_ms Threshold for marking slow
64 */
65void report_result(const char *test_name, int passed, double avg_ms,
66 double max_allowed_ms);
67
68/**
69 * @brief
70 * @param func
71 * @param arg
72 * @param benchmarks
73 * @param label
74 */
75void benchmark_func_with_resources(function_to_benchmark func, const char *arg,
76 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 @@
1#include "resource_usage.h"
2#include <stdio.h>
3#include <sys/resource.h>
4#include <sys/time.h>
5
6void print_resource_usage(const char *label) {
7 struct rusage usage;
8 if (getrusage(RUSAGE_SELF, &usage) == 0) {
9 long mem_kb = usage.ru_maxrss;
10 double user_sec = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6;
11 double sys_sec = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6;
12 printf("[%s] CPU user: %.3f s, system: %.3f s, peak memory: %ld KB\n",
13 label, user_sec, sys_sec, mem_kb);
14 } else {
15 printf("[%s] Resource usage not available\n", label);
16 }
17}
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 @@
1#pragma once
2/**
3 *
4 * @param label
5 */
6void 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 @@
1#pragma once
2
3/**
4 *
5 * TASK 1: validate_first_name
6 * ---------------------------
7 * Check if the first name is valid.
8 * - Cannot be NULL or empty
9 * - Only letters (a-z, A-Z)
10 * - Minimum length: 2
11 */
12int validate_first_name(const char *first_name);
13
14/**
15 * TASK 2: validate_last_name
16 * --------------------------
17 * Check if the last name is valid.
18 * - Cannot be NULL or empty
19 * - Only letters (a-z, A-Z)
20 * - Minimum length: 2
21 */
22int validate_last_name(const char *last_name);
23
24/**
25 * TASK 3: validate_full_name
26 * --------------------------
27 * Check if a full name "First Last" is valid.
28 * Use TASK 1 and TASK 2 internally.
29 */
30int validate_full_name(const char *full_name);
31
32/**
33 * TASK 4: validate_index
34 * ----------------------
35 * Check if a student index is valid.
36 * - Exactly 6 digits
37 */
38int validate_index(const char *index);
39
40/**
41 * TASK 5: register_student
42 * ------------------------
43 * Register a student given full name and index.
44 * Return codes:
45 * 0 -> valid
46 * 1 -> invalid name
47 * 2 -> invalid index
48 */
49int register_student(const char *full_name, const char *index);
50
51/**
52 * TASK 6: name_length
53 * ------------------
54 * Return length of the string (do not use strlen).
55 */
56int name_length(const char *name);
57
58/**
59 * TASK 7: student struct
60 * ---------------------
61 * Simple student structure
62 */
63typedef struct {
64 char full_name[64];
65 char index[16];
66} Student;
67
68/**
69 * TASK 8: add_student
70 * -------------------
71 * Add a student to an array of Student. Return 0 if added.
72 */
73int add_student(Student *array, int max_size, const char *name,
74 const char *index);
75
76/**
77 * TASK 9: get_error_message
78 * ------------------------
79 * Return error message string for code returned by register_student
80 */
81const 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 @@
1#include "syntax_essentials.h"
2#include <stdio.h>
3
4/**
5 *
6 * TASK 1: validate_first_name
7 * Check if the first name is valid.
8 * - Cannot be NULL or empty
9 * - Only letters (a-z, A-Z)
10 * - Minimum length: 2
11 *
12 * @param first_name
13 * @return
14 */
15int validate_first_name(const char* first_name)
16{
17 if (!first_name)
18 return 0;
19
20 int length = 0;
21 for (const char* p = first_name; *p; p++) {
22 if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))
23 length++;
24 else
25 return 0;
26 }
27 return length >= 2;
28}
29
30/**
31 *
32 * TASK 2: validate_last_name
33 * Check if the last name is valid.
34 * - Cannot be NULL or empty
35 * - Only letters (a-z, A-Z)
36 * - Minimum length: 2
37 *
38 * @param last_name
39 * @return
40 */
41int validate_last_name(const char* last_name)
42{
43 if (!last_name)
44 return 0;
45
46 int length = 0;
47
48 for (const char* p = last_name; *p; p++) {
49 if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))
50 length++;
51 else
52 return 0;
53 }
54
55 return length >= 2;
56}
57
58/**
59 *
60 * TASK 3: validate_full_name
61 * Check if a full name "First Last" is valid.
62 * Use TASK 1 and TASK 2 internally.
63 * @param full_name
64 * @return
65 */
66int validate_full_name(const char* full_name)
67{
68 if (!full_name)
69 return 0;
70
71 char first_name[64], last_name[64];
72 if (sscanf(full_name, "%63s %63s", first_name, last_name) != 2)
73 return 0;
74
75 return validate_first_name(first_name) && validate_last_name(last_name);
76}
77
78/**
79 *
80 * @param index
81 * @return
82 */
83int validate_index(const char* index)
84{
85 if (!index)
86 return 0;
87
88 int length = 0;
89 for (const char* p = index; *p; p++) {
90 if (*p >= '0' && *p <= '9')
91 length++;
92 else
93 return 0;
94 }
95
96 return length == 6;
97}
98
99/**
100 *
101 * @param full_name
102 * @param index
103 * @return
104 */
105int register_student(const char* full_name, const char* index)
106{
107 if (!validate_full_name(full_name))
108 return 1;
109 if (!validate_index(index))
110 return 2;
111 return 0;
112}
113
114/**
115 *
116 * @param name
117 * @return
118 */
119int name_length(const char* name)
120{
121 if (!name)
122 return 0;
123
124 int length = 0;
125 while (*name++)
126 length++;
127 return length;
128}
129
130/**
131 *
132 * @param array
133 * @param max_size
134 * @param name
135 * @param index
136 * @return
137 */
138int add_student(Student* array,
139 const int max_size,
140 const char* name,
141 const char* index)
142{
143 if (!name || !index || !array)
144 return 2;
145
146 int len = 0;
147 while (len < max_size && array[len].full_name[0] != '\0')
148 len++;
149
150 if (len >= max_size)
151 return 1;
152
153 snprintf(
154 array[len].full_name, sizeof(array[len].full_name), "%s", name);
155 snprintf(array[len].index, sizeof(array[len].index), "%s", index);
156 return 0;
157}
158
159/**
160 *
161 * @param code
162 * @return
163 */
164const char* get_error_message(const int code)
165{
166 switch (code) {
167 case 0:
168 return "Success";
169 case 1:
170 return "Invalid name";
171 case 2:
172 return "Invalid index";
173 default:
174 return "Unknown error";
175 }
176}
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 @@
1#include "benchmark.h"
2#include "syntax_essentials.h"
3#include <stdio.h>
4
5static int total_tests = 0;
6static int passed_tests = 0;
7static int any_failure = 0;
8
9/**
10 *
11 * @param name
12 * @param func
13 * @param arg
14 * @param expected
15 * @param iterations
16 */
17static void run_test(const char* name,
18 const function_to_benchmark func,
19 const char* arg,
20 const int expected,
21 const size_t iterations)
22{
23 total_tests++;
24
25 const double avg_ms = benchmark_func(func, arg, iterations);
26 const int result = func(arg);
27
28 const int logic_ok = (result == expected);
29 const int perf_ok = (avg_ms <= MAX_ALLOWED_MS);
30 const int ok = logic_ok && perf_ok;
31
32 if (ok)
33 passed_tests++;
34 else
35 any_failure = 1;
36
37 report_result(name, ok, avg_ms, MAX_ALLOWED_MS);
38}
39
40/**
41 *
42 */
43static void test_names(void)
44{
45 run_test("validate_first_name: valid",
46 validate_first_name,
47 "Jan",
48 1,
49 ITERATIONS_FAST);
50 run_test("validate_first_name: too short",
51 validate_first_name,
52 "J",
53 0,
54 ITERATIONS_FAST);
55 run_test("validate_last_name: valid",
56 validate_last_name,
57 "Kowalski",
58 1,
59 ITERATIONS_FAST);
60 run_test("validate_last_name: invalid char",
61 validate_last_name,
62 "Kowalski3",
63 0,
64 ITERATIONS_FAST);
65}
66
67/**
68 *
69 */
70static void test_full_name(void)
71{
72 run_test("validate_full_name: valid",
73 validate_full_name,
74 "Jan Kowalski",
75 1,
76 ITERATIONS_SLOW);
77 run_test("validate_full_name: invalid first",
78 validate_full_name,
79 "J Kowalski",
80 0,
81 ITERATIONS_SLOW);
82}
83
84/**
85 *
86 */
87static void test_index(void)
88{
89 run_test("validate_index: valid",
90 validate_index,
91 "123456",
92 1,
93 ITERATIONS_FAST);
94 run_test("validate_index: too short",
95 validate_index,
96 "12345",
97 0,
98 ITERATIONS_FAST);
99}
100
101/**
102 *
103 * @return
104 */
105int main(void)
106{
107 printf("=== Embedded Syntax Essentials Test Suite ===\n");
108
109 test_names();
110 test_full_name();
111 test_index();
112
113 printf("\n====================================\n");
114 printf("Tests passed: %d / %d\n", passed_tests, total_tests);
115
116 if (!any_failure) {
117 printf(
118 "%sALL CRITERIA MET — PASS%s\n", COLOR_GREEN, COLOR_RESET);
119 return 0;
120 }
121 printf("%sSUITE FAILED — performance or logic violation%s\n",
122 COLOR_RED,
123 COLOR_RESET);
124 return 1;
125}