Add cross-platform tests for libmetal

To-be-expanded suite of tests for libmetal using
system-agnostic API instead of system specific tests.

Signed-off-by: Lefteris Ntafotis <lntaf@ics.forth.gr>
This commit is contained in:
Lefteris Ntafotis
2025-05-19 13:25:07 +03:00
committed by Arnaud Pouliquen
parent a0797a23a8
commit 1c3410be76
18 changed files with 662 additions and 0 deletions

View File

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

50
test/alloc.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/alloc.h>
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;
}

19
test/assert.c Normal file
View File

@@ -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 <metal/assert.h>
int test_assert(void)
{
metal_assert(1);
return 0;
}

32
test/cache.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/cache.h>
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;
}

33
test/config.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/config.h>
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;
}

29
test/cpu.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/cpu.h>
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;
}

29
test/init.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
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;
}

31
test/log.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
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;
}

80
test/mutex.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/mutex.h>
#include <metal/utilities.h>
#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;
}

34
test/shmem.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/shmem.h>
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;
}

44
test/sleep.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/time.h>
#include <metal/sleep.h>
#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;
}

79
test/spinlock.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/spinlock.h>
#include <metal/utilities.h>
#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;
}

View File

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

View File

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

View File

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

View File

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

67
test/test-all.c Normal file
View File

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

78
test/utilities.c Normal file
View File

@@ -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 <metal/sys.h>
#include <metal/log.h>
#include <metal/utilities.h>
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;
}