diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 127df2c..1a34a00 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ collector_create (PROJECT_LIB_TESTS "${CMAKE_CURRENT_SOURCE_DIR}") +collector_create (PROJECT_LIB_API_TEST "${CMAKE_CURRENT_SOURCE_DIR}") add_subdirectory (system) @@ -7,6 +8,8 @@ collect (PROJECT_LIB_HEADERS metal-test.h) collect (PROJECT_LIB_TESTS version.c) collect (PROJECT_LIB_TESTS metal-test.c) +collect (PROJECT_LIB_API_TEST test-all.c) + collector_list (_hdirs PROJECT_INC_DIRS) include_directories (${_hdirs} ${CMAKE_CURRENT_SOURCE_DIR}) @@ -67,5 +70,49 @@ else (WITH_ZEPHYR) list (APPEND _flist "${CMAKE_CURRENT_BINARY_DIR}/${_f}.c") endforeach (INCLUDE) add_library (metal-headers STATIC ${_flist}) + + collector_list(_src_all PROJECT_LIB_API_TEST) + + set (_tests_all + ${PROJECT_SOURCE_DIR}/test/alloc.c + ${PROJECT_SOURCE_DIR}/test/assert.c + ${PROJECT_SOURCE_DIR}/test/cache.c + ${PROJECT_SOURCE_DIR}/test/config.c + ${PROJECT_SOURCE_DIR}/test/cpu.c + ${PROJECT_SOURCE_DIR}/test/init.c + ${PROJECT_SOURCE_DIR}/test/log.c + ${PROJECT_SOURCE_DIR}/test/mutex.c + ${PROJECT_SOURCE_DIR}/test/shmem.c + ${PROJECT_SOURCE_DIR}/test/sleep.c + ${PROJECT_SOURCE_DIR}/test/spinlock.c + ${PROJECT_SOURCE_DIR}/test/utilities.c + ) + + if (WITH_SHARED_LIB) + get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS) + set (_lib ${PROJECT_NAME}-shared) + add_executable(test-all-${_lib} ${_src_all} ${_tests_all}) + target_link_libraries(test-all-${_lib} ${PROJECT_NAME}-shared) + target_compile_options(test-all-${_lib} PUBLIC ${_ec_flgs}) + add_dependencies(test-all-${_lib} ${PROJECT_NAME}-shared) + install(TARGETS test-all-${_lib} RUNTIME DESTINATION bin) + if (WITH_TESTS_EXEC) + add_test(test-all-${_lib} test-all-${_lib}) + endif() + endif() + + if (WITH_STATIC_LIB) + get_property (_linker_options GLOBAL PROPERTY TEST_LINKER_OPTIONS) + set (_lib ${PROJECT_NAME}-static) + add_executable (test-all-${_lib} ${_src_all} ${_tests_all}) + target_link_libraries (test-all-${_lib} -Wl,-Map=test-all-${_lib}.map ${_linker_options} -Wl,--start-group ${_lib} ${_deps} -Wl,--end-group) + install (TARGETS test-all-${_lib} RUNTIME DESTINATION bin) + target_compile_options (test-all-${_lib} PUBLIC ${_ec_flgs}) + add_dependencies (test-all-${_lib} ${PROJECT_NAME}-static) + if (WITH_TESTS_EXEC) + add_test (test-all-${_lib} test-all-${_lib}) + endif (WITH_TESTS_EXEC) + endif (WITH_STATIC_LIB) + endif (WITH_ZEPHYR) diff --git a/test/alloc.c b/test/alloc.c new file mode 100644 index 0000000..cae5815 --- /dev/null +++ b/test/alloc.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file alloc.c + * @brief Cross-platform tests for alloc interface + */ + +#include +#include +#include + +int test_alloc(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int *mem_ptr; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + mem_ptr = metal_allocate_memory(4 * sizeof(int)); + if (!mem_ptr) { + metal_err("metal_allocate_memory(16) returned null\n"); + return 1; + } + + metal_dbg("Writing -2147483648, -1, 0, 2147483647 to allocated memory\n"); + + mem_ptr[0] = -2147483648; + mem_ptr[1] = -1; + mem_ptr[2] = 0; + mem_ptr[3] = 2147483647; + + if (mem_ptr[0] != -2147483648 || mem_ptr[1] != -1 || + mem_ptr[2] != 0 || mem_ptr[3] != 2147483647) { + + metal_err("Allocated memory has values %d, %d, %d, %d instead of -2147483648, -1 , 0, 2147483647\n", + mem_ptr[0], mem_ptr[1], mem_ptr[2], mem_ptr[3]); + return 1; + } + + metal_free_memory(mem_ptr); + + metal_finish(); + return 0; +} diff --git a/test/assert.c b/test/assert.c new file mode 100644 index 0000000..2f84b22 --- /dev/null +++ b/test/assert.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file assert.c + * @brief Cross-platform tests for assert interface + */ + +#include + +int test_assert(void) +{ + metal_assert(1); + + return 0; +} diff --git a/test/cache.c b/test/cache.c new file mode 100644 index 0000000..b0ed361 --- /dev/null +++ b/test/cache.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file cache.c + * @brief Cross-platform tests for cache interface + */ + +#include +#include +#include + +int test_cache(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + metal_dbg("Flushing entire DCache\n"); + metal_cache_flush(NULL, 0); + + metal_dbg("Invalidating entire DCache\n"); + metal_cache_invalidate(NULL, 0); + + metal_finish(); + return 0; +} diff --git a/test/config.c b/test/config.c new file mode 100644 index 0000000..3202551 --- /dev/null +++ b/test/config.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file config.c + * @brief Cross-platform tests for config interface + */ + +#include +#include +#include + +int test_config(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_INFO); + + metal_info("Metal version: %d.%d.%d\n", METAL_VER_MAJOR, METAL_VER_MINOR, METAL_VER_PATCH); + metal_info("In string form: %s\n", METAL_VER); + + metal_info("Metal system identifies as: %s\n", METAL_SYSTEM); + metal_info("Metal processor identifies as: %s\n", METAL_PROCESSOR); + metal_info("Metal machine identifies as: %s\n", METAL_MACHINE); + + metal_finish(); + return 0; +} diff --git a/test/cpu.c b/test/cpu.c new file mode 100644 index 0000000..735095b --- /dev/null +++ b/test/cpu.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file cpu.c + * @brief Cross-platform tests for cpu interface + */ + +#include +#include +#include + +int test_cpu(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + metal_dbg("CPU Yielding\n"); + metal_cpu_yield(); + + metal_finish(); + return 0; +} diff --git a/test/init.c b/test/init.c new file mode 100644 index 0000000..2fe8404 --- /dev/null +++ b/test/init.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file init.c + * @brief Cross-platform tests for init interface + */ + +#include +#include + +int test_init(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int init_ret; + + init_ret = metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + if (init_ret) + metal_err("metal_init() returned %d\n", init_ret); + + metal_finish(); + return init_ret; +} diff --git a/test/log.c b/test/log.c new file mode 100644 index 0000000..2debc65 --- /dev/null +++ b/test/log.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file log.c + * @brief Cross-platform tests for log interface + */ + +#include +#include + +int test_log(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_WARNING); + + metal_dbg("Metal log level is: %d\n", metal_get_log_level()); + + for (int i = 0; i <= METAL_LOG_DEBUG; i++) + metal_log((enum metal_log_level)i, "Level %d log example\n", i); + + metal_finish(); + return 0; +} diff --git a/test/mutex.c b/test/mutex.c new file mode 100644 index 0000000..df749e7 --- /dev/null +++ b/test/mutex.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file mutex.c + * @brief Cross-platform tests for mutex interface + * + * Simple test that creates an array with values [1, ARRAY_SIZE] and + * sums their values using threads and libmetal's mutex interface + */ + +#include +#include +#include +#include +#include "metal-test.h" + +#define ARRAY_SIZE 100 + +static int sum_m; +static metal_mutex_t mut; +static int idx; + +static int array[ARRAY_SIZE]; + +void *array_sum_m(void *a) +{ + metal_unused(a); + while (1) { + metal_mutex_acquire(&mut); + if (idx >= ARRAY_SIZE) { + metal_mutex_release(&mut); + break; + } + sum_m += array[idx]; + metal_dbg("Sum is now %d after incrementing by %d\n", sum_m, array[idx]); + idx++; + metal_mutex_release(&mut); + } + + return NULL; +} + +int test_mutex(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int expected = 0; + + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = i + 1; + } + + idx = 0; + + metal_init(&metal_param); + metal_mutex_init(&mut); + + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + metal_test_run(2, array_sum_m, NULL); + + metal_info("Sum = %d returned for array with values [%d, %d]\n", + sum_m, array[0], array[ARRAY_SIZE - 1]); + + expected = (ARRAY_SIZE * (array[0] + array[ARRAY_SIZE - 1])) / 2; + if (sum_m != expected) { + /*Sum of array should be n/2 * (first + last numbers)*/ + metal_err("Array sum is %d instead of %d elements\n", sum_m, expected); + return 1; + } + + metal_mutex_deinit(&mut); + metal_finish(); + + return 0; +} diff --git a/test/shmem.c b/test/shmem.c new file mode 100644 index 0000000..31fefa4 --- /dev/null +++ b/test/shmem.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file shmem.c + * @brief Cross-platform tests for shmem interface + */ + +#include +#include +#include + +int test_shmem(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + struct metal_io_region **res = NULL; + int ret = 0; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + ret = metal_shmem_open("/foo", 128, res); + if (ret < 0) { + metal_err("metal_shmem_open failed with code: %d\n", ret); + return ret; + } + + metal_finish(); + return 0; +} diff --git a/test/sleep.c b/test/sleep.c new file mode 100644 index 0000000..d0fe050 --- /dev/null +++ b/test/sleep.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file sleep.c + * @brief Cross-platform tests for sleep and time interfaces + */ + +#include +#include +#include +#include + +#define SLEEP_SECONDS 3 +#define SLEEP_USECONDS (SLEEP_SECONDS * 1000000) + +int test_sleep(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int ret = 0; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + metal_info("Time is currently %llu, sleeping for %llu seconds\n", + metal_get_timestamp(), SLEEP_SECONDS); + + ret = metal_sleep_usec(SLEEP_USECONDS); + + if (ret) { + metal_err("Function metal_sleep_usec returned %d\n", ret); + return 1; + } + + metal_dbg("Time is now %llu\n", metal_get_timestamp()); + + metal_finish(); + + return 0; +} diff --git a/test/spinlock.c b/test/spinlock.c new file mode 100644 index 0000000..bf6fe34 --- /dev/null +++ b/test/spinlock.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file spinlock.c + * @brief Cross-platform tests for spinlock interface + * + * Simple test that creates an array with values [1, ARRAY_SIZE] and + * sums their values using threads and libmetal's spinlock interface + */ + +#include +#include +#include +#include +#include "metal-test.h" + +#define ARRAY_SIZE 100 + +static int sum_s; +static struct metal_spinlock slock; +static int idx; + +static int array[ARRAY_SIZE]; + +void *array_sum_s(void *a) +{ + metal_unused(a); + while (1) { + metal_spinlock_acquire(&slock); + if (idx >= ARRAY_SIZE) { + metal_spinlock_release(&slock); + break; + } + sum_s += array[idx]; + metal_dbg("Sum is now %d after incrementing by %d\n", sum_s, array[idx]); + idx++; + metal_spinlock_release(&slock); + } + + return NULL; +} + +int test_spinlock(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int expected = 0; + + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = i + 1; + } + + idx = 0; + + metal_init(&metal_param); + metal_spinlock_init(&slock); + + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + metal_test_run(2, array_sum_s, NULL); + + metal_info("Sum = %d returned for array with values [%d, %d]\n", + sum_s, array[0], array[ARRAY_SIZE - 1]); + + expected = (ARRAY_SIZE * (array[0] + array[ARRAY_SIZE - 1])) / 2; + if (sum_s != expected) { + /*Sum of array should be n/2 * (first + last numbers)*/ + metal_err("Array sum is %d instead of %d elements\n", sum_s, expected); + return 1; + } + + metal_finish(); + + return 0; +} diff --git a/test/system/freertos/CMakeLists.txt b/test/system/freertos/CMakeLists.txt index 7639048..07717fa 100644 --- a/test/system/freertos/CMakeLists.txt +++ b/test/system/freertos/CMakeLists.txt @@ -6,6 +6,8 @@ collect (PROJECT_LIB_TESTS mutex.c) collect (PROJECT_LIB_TESTS atomic.c) collect (PROJECT_LIB_TESTS sleep.c) +collect (PROJECT_LIB_API_TEST threads.c) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) add_subdirectory(${PROJECT_MACHINE}) endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) diff --git a/test/system/generic/CMakeLists.txt b/test/system/generic/CMakeLists.txt index 996dafe..a3ce25d 100644 --- a/test/system/generic/CMakeLists.txt +++ b/test/system/generic/CMakeLists.txt @@ -3,6 +3,9 @@ collect (PROJECT_LIB_TESTS alloc.c) collect (PROJECT_LIB_TESTS irq.c) collect (PROJECT_LIB_TESTS mutex.c) collect (PROJECT_LIB_TESTS atomic.c) +collect (PROJECT_LIB_TESTS threads.c) + +collect (PROJECT_LIB_TEST_ALL threads.c) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) add_subdirectory(${PROJECT_MACHINE}) diff --git a/test/system/linux/CMakeLists.txt b/test/system/linux/CMakeLists.txt index 1e259d8..e8d03c7 100644 --- a/test/system/linux/CMakeLists.txt +++ b/test/system/linux/CMakeLists.txt @@ -8,6 +8,8 @@ collect (PROJECT_LIB_TESTS spinlock.c) collect (PROJECT_LIB_TESTS alloc.c) collect (PROJECT_LIB_TESTS irq.c) +collect (PROJECT_LIB_API_TEST threads.c) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) add_subdirectory(${PROJECT_MACHINE}) endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) diff --git a/test/system/zephyr/CMakeLists.txt b/test/system/zephyr/CMakeLists.txt index 43f1955..dac400c 100644 --- a/test/system/zephyr/CMakeLists.txt +++ b/test/system/zephyr/CMakeLists.txt @@ -2,6 +2,9 @@ collect (PROJECT_LIB_TESTS main.c) collect (PROJECT_LIB_TESTS alloc.c) collect (PROJECT_LIB_TESTS mutex.c) collect (PROJECT_LIB_TESTS atomic.c) +collect (PROJECT_LIB_TESTS threads.c) + +collect (PROJECT_LIB_API_TEST threads.c) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}) add_subdirectory(${PROJECT_MACHINE}) diff --git a/test/test-all.c b/test/test-all.c new file mode 100644 index 0000000..342e132 --- /dev/null +++ b/test/test-all.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file test_all.c + * @brief Main cross-platform testing execution and debug printing + */ + +#include + +extern int test_alloc(void); +extern int test_assert(void); +extern int test_cache(void); +extern int test_config(void); +extern int test_cpu(void); +extern int test_init(void); +extern int test_log(void); +extern int test_mutex(void); +extern int test_shmem(void); +extern int test_sleep(void); +extern int test_spinlock(void); +extern int test_utilities(void); + +struct test_entry { + char name[20]; + int (*test)(void); + int ret; +}; + +struct test_entry tests[] = { + {"alloc.c", test_alloc, 0}, + {"assert.c", test_assert, 0}, + {"cache.c", test_cache, 0}, + {"config.c", test_config, 0}, + {"cpu.c", test_cpu, 0}, + {"init.c", test_init, 0}, + {"log.c", test_log, 0}, + {"mutex.c", test_mutex, 0}, + {"shmem.c", test_shmem, 0}, + {"sleep.c", test_sleep, 0}, + {"spinlock.c", test_spinlock, 0}, + {"utilities.c", test_utilities, 0} +}; + +#define NUM_TESTS (sizeof(tests) / sizeof(tests[0])) + +int main(void) +{ + unsigned long int i; + char pass[] = "\033[0;32m [PASS] \033[0m"; + char fail[] = "\033[0;31m [FAIL] \033[0m"; + + for (i = 0; i < NUM_TESTS; i++) { + printf("------%s------\n", tests[i].name); + tests[i].ret = tests[i].test(); + } + for (i = 0; i < NUM_TESTS; i++) { + if (tests[i].ret == 0) + printf("%-12s %s\n", tests[i].name, pass); + else + printf("%-12s %s (code %d)\n", tests[i].name, fail, tests[i].ret); + } + return 0; +} diff --git a/test/utilities.c b/test/utilities.c new file mode 100644 index 0000000..93a112f --- /dev/null +++ b/test/utilities.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025, CARV ICS FORTH. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * @file utilities.c + * @brief Cross-platform tests for utilities interface + */ + +#include +#include +#include + +struct example { + int num; + char c[4]; + int *ptr; +}; + +int test_utilities(void) +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + int array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + char c_array[7] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + int error = 0; + + metal_init(&metal_param); + metal_set_log_handler(metal_default_log_handler); + metal_set_log_level(METAL_LOG_ERROR); + + if (MB != 1048576 || GB != 1073741824) { + error = 1; + metal_err("1 MiB is %ld instead of 1048576, 1 GiB is %ld instead of 1073741824\n", + MB, GB); + } + if (metal_dim(array) != 10) { + error = 2; + metal_err("Array has %d instead of 10 elements\n", metal_dim(array)); + } + if (metal_dim(c_array) != 7) { + error = 3; + metal_err("Character array has %d instead of 7 elements\n", metal_dim(c_array)); + } + if (metal_min(10, 4) != 4 || metal_max(198, 2) != 198) { + error = 4; + metal_err("Minimum between 10 and 4 is %d instead of 4 and maximum between 198 and 2 is %d instead of 198\n", + metal_min(10, 4), metal_max(198, 2)); + } + if (metal_min(-10, -4) != -10 || metal_max(-198, -2) != -2) { + error = 5; + metal_err("Minimum between -10 and -4 is %d instead of -10 and maximum between -198 and -2 is %d instead of -2\n", + metal_min(-10, -4), metal_max(-198, -2)); + } + if (metal_align_down(10, 4) != 8 || metal_align_up(10, 4) != 12) { + error = 6; + metal_err("10 aligned down to ^4 is %d instead of 8, up to ^4 is %d instead of 12\n", + metal_align_down(10, 4), metal_align_up(10, 4)); + } + if (metal_div_round_down(10, 3) != 3 || metal_div_round_up(10, 3) != 4) { + error = 7; + metal_err("10 divided by 3 rounded down is %d instead of 3 and rounded up is %d instead of 4\n", + metal_div_round_down(10, 3), metal_div_round_up(10, 3)); + } + if (metal_div_round_down(-10, 3) != -4 || metal_div_round_up(-10, 3) != -3) { + error = 8; + metal_err("-10 divided by 3 rounded down is %d instead of -4 and rounded up is %d instead of -3\n", + metal_div_round_down(-10, 3), metal_div_round_up(-10, 3)); + } + if (metal_offset_of(struct example, ptr) != 8) { + error = 9; + metal_err("Offset of test struct is %d instead of 8\n", + metal_offset_of(struct example, ptr)); + } + metal_finish(); + return error; +}