diff --git a/include/libc/mr_sprintf.h b/include/libc/mr_sprintf.h index baf0b8e..a83c4a1 100644 --- a/include/libc/mr_sprintf.h +++ b/include/libc/mr_sprintf.h @@ -29,8 +29,8 @@ extern "C" { #if defined(MR_USE_SPRINTF_STD) #define mr_vsnprintf(_b, _s, _f, _a) vsnprintf(_b, _s, _f, _a) #define mr_vsprintf(_b, _f, _a) vsprintf(_b, _f, _a) -#define mr_snprintf(_b, _s, _f, ...) snprintf(_b, _s, _f, __VA_ARGS__) -#define mr_sprintf(_b, _f, ...) sprintf(_b, _f, __VA_ARGS__) +#define mr_snprintf(_b, _s, _f, ...) snprintf(_b, _s, _f, ##__VA_ARGS__) +#define mr_sprintf(_b, _f, ...) sprintf(_b, _f, ##__VA_ARGS__) #else /** * @brief This function prints a formatted string to a buffer. diff --git a/include/libc/mr_string.h b/include/libc/mr_string.h index 9b33de2..0fb6d8a 100644 --- a/include/libc/mr_string.h +++ b/include/libc/mr_string.h @@ -37,6 +37,7 @@ extern "C" { #define mr_strcpy(_d, _s) strcpy(_d, _s) #define mr_strerror(_e) strerror(_e) #define mr_strlen(_s) strlen(_s) +#define mr_strnlen(_s, _sz) strnlen(_s, _sz) #define mr_strncmp(_s1, _s2, _sz) strncmp(_s1, _s2, _sz) #define mr_strncpy(_d, _s, _sz) strncpy(_d, _s, _sz) #define mr_strchr(_s, _c) strchr(_s, _c) diff --git a/include/mr-X/mr_list.h b/include/mr-X/mr_list.h index c043413..1f27769 100644 --- a/include/mr-X/mr_list.h +++ b/include/mr-X/mr_list.h @@ -105,29 +105,6 @@ MR_INLINE void mr_list_replace(mr_list_t *new, mr_list_t *old) { old->next = old->prev = old; } -/** - * @brief This function swaps two elements in the list. - * - * @param entry1 The first element. - * @param entry2 The second element. - */ -MR_INLINE void mr_list_swap(mr_list_t *entry1, mr_list_t *entry2) { - mr_list_t *tmp; - - /* Remove from the list */ - tmp = entry2->prev; - mr_list_del(entry2); - - /* Replace in the list */ - mr_list_replace(entry1, entry2); - if (tmp == entry1) { - tmp = entry2; - } - - /* Add to the list */ - mr_list_add(tmp, entry1); -} - /** * @brief This function moves an element to the head of the list. * diff --git a/include/test/mr_check.h b/include/test/mr_check.h index 8f64c83..5ccdc89 100644 --- a/include/test/mr_check.h +++ b/include/test/mr_check.h @@ -31,28 +31,39 @@ extern mr_uint32_t __mr_test_status; /* Test pass magic number definition */ #define __MR_TEST_MAGIC_PASS (0x70617373U) -/** - * @brief This macro function checks if the condition is true. - * - * @param _a The first operand. - * @param _cond The condition. - * @param _b The second operand. - * @param _type The operand type. - * @param _fmt The format string for the operand. - */ +/* Test printf macro definition */ +#define __MR_TEST_PRINTF(_desc, _a_str, _a, _b_str, _b, _fmt) \ + do { \ + mr_printf("%s:%d\n", __FILE__, __LINE__); \ + mr_printf("Expectation failed: %s\n", _desc); \ + mr_printf(" %s = "_fmt \ + "\n", \ + (_a_str), (_a)); \ + mr_printf(" %s = "_fmt \ + "\n", \ + (_b_str), (_b)); \ + } while (0) + +/* Test check macro definition */ #define __MR_TEST_CHECK(_a, _cond, _b, _type, _fmt) \ do { \ _type __a = (_type)(_a); \ _type __b = (_type)(_b); \ if (!(__a _cond __b)) { \ - mr_printf("%s:%d: Test Failure\n", __FILE__, __LINE__); \ - mr_printf("Expectation failed: %s %s %s\n", #_a, #_cond, #_b); \ - mr_printf(" %s = "_fmt \ - "\n", \ - #_a, __a); \ - mr_printf(" %s = "_fmt \ - "\n", \ - #_b, __b); \ + __MR_TEST_PRINTF(#_a " " #_cond " " #_b, #_a, __a, #_b, __b, \ + _fmt); \ + __mr_test_status = ~__MR_TEST_MAGIC_PASS; \ + } \ + } while (0) +#define __MR_TEST_CHECK_NEAR(_a, _b, _e, _type, _fmt) \ + do { \ + _type __a = (_type)(_a); \ + _type __b = (_type)(_b); \ + _type __e = (_type)(_e); \ + _type __c = (__a > __b) ? (__a - __b) : (__b - __a); \ + if (!(__c <= __e)) { \ + __MR_TEST_PRINTF("| " #_a " - " #_b " | <= " #_e, #_a, __a, #_b, \ + __b, _fmt); \ __mr_test_status = ~__MR_TEST_MAGIC_PASS; \ } \ } while (0) @@ -70,15 +81,34 @@ extern mr_uint32_t __mr_test_status; __MR_TEST_CHECK(_a, >, _b, _type, _fmt) #define __MR_TEST_GE(_a, _b, _type, _fmt) \ __MR_TEST_CHECK(_a, >=, _b, _type, _fmt) +#define __MR_TEST_NEAR(_a, _b, _e, _type, _fmt) \ + __MR_TEST_CHECK_NEAR(_a, _b, _e, _type, _fmt) + +/* Test condition macros definition */ +#define __MR_TEST_COND_INT(_a, _cond, _b) \ + __MR_TEST_##_cond(_a, _b, mr_int64_t, "%lld") +#define __MR_TEST_COND_UINT(_a, _cond, _b) \ + __MR_TEST_##_cond(_a, _b, mr_uint64_t, "%llu") +#define __MR_TEST_COND_PTR(_a, _cond, _b) \ + __MR_TEST_##_cond(_a, _b, mr_ptr_t, "%p") +#define __MR_TEST_COND_FLOAT(_a, _cond, _b) \ + __MR_TEST_##_cond(_a, _b, mr_f32_t, "%f") +#define __MR_TEST_COND_DOUBLE(_a, _cond, _b) \ + __MR_TEST_##_cond(_a, _b, mr_f64_t, "%lf") +#define __MR_TEST_COND_NEAR(_a, _b, _e) \ + __MR_TEST_NEAR(_a, _b, _e, mr_f64_t, "%lf") /* Test expect and assert macros definition */ #define __MR_TEST_EXPECT(_check) _check #define __MR_TEST_ASSERT(_check) \ do { \ + mr_uint32_t __s = __mr_test_status; \ + __mr_test_status = __MR_TEST_MAGIC_PASS; \ _check; \ if (__mr_test_status == (~__MR_TEST_MAGIC_PASS)) { \ return; \ } \ + __mr_test_status = __s; \ } while (0) #endif /* defined(MR_USE_TEST) */ diff --git a/include/test/mr_test.h b/include/test/mr_test.h index 1b19dcb..eb359a7 100644 --- a/include/test/mr_test.h +++ b/include/test/mr_test.h @@ -26,135 +26,141 @@ extern "C" { #if defined(MR_USE_TEST) /* Test expect and assert equality macros definition */ #define MR_TEST_EXPECT_EQ_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_EQ(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, EQ, _b)) #define MR_TEST_EXPECT_EQ_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_EQ(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, EQ, _b)) #define MR_TEST_EXPECT_EQ_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_EQ(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, EQ, _b)) #define MR_TEST_EXPECT_EQ_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_EQ(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_NEAR(_a, _b, 1e-6)) #define MR_TEST_EXPECT_EQ_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_EQ(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_NEAR(_a, _b, 1e-8)) #define MR_TEST_ASSERT_EQ_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_EQ(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, EQ, _b)) #define MR_TEST_ASSERT_EQ_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_EQ(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, EQ, _b)) #define MR_TEST_ASSERT_EQ_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_EQ(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, EQ, _b)) #define MR_TEST_ASSERT_EQ_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_EQ(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_NEAR(_a, _b, 1e-6)) #define MR_TEST_ASSERT_EQ_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_EQ(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_NEAR(_a, _b, 1e-8)) /* Test expect and assert inequality macros definition */ #define MR_TEST_EXPECT_NE_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_NE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, NE, _b)) #define MR_TEST_EXPECT_NE_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_NE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, NE, _b)) #define MR_TEST_EXPECT_NE_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_NE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, NE, _b)) #define MR_TEST_EXPECT_NE_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_NE(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_FLOAT(_a, NE, _b)) #define MR_TEST_EXPECT_NE_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_NE(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_DOUBLE(_a, NE, _b)) #define MR_TEST_ASSERT_NE_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_NE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, NE, _b)) #define MR_TEST_ASSERT_NE_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_NE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, NE, _b)) #define MR_TEST_ASSERT_NE_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_NE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, NE, _b)) #define MR_TEST_ASSERT_NE_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_NE(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_FLOAT(_a, NE, _b)) #define MR_TEST_ASSERT_NE_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_NE(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_DOUBLE(_a, NE, _b)) /* Test expect and assert less than macros definition */ #define MR_TEST_EXPECT_LT_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LT(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, LT, _b)) #define MR_TEST_EXPECT_LT_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LT(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, LT, _b)) #define MR_TEST_EXPECT_LT_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LT(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, LT, _b)) #define MR_TEST_EXPECT_LT_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LT(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_FLOAT(_a, LT, _b)) #define MR_TEST_EXPECT_LT_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LT(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_DOUBLE(_a, LT, _b)) #define MR_TEST_ASSERT_LT_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LT(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, LT, _b)) #define MR_TEST_ASSERT_LT_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LT(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, LT, _b)) #define MR_TEST_ASSERT_LT_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LT(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, LT, _b)) #define MR_TEST_ASSERT_LT_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LT(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_FLOAT(_a, LT, _b)) #define MR_TEST_ASSERT_LT_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LT(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_DOUBLE(_a, LT, _b)) /* Test expect and assert less than or equal macros definition */ #define MR_TEST_EXPECT_LE_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, LE, _b)) #define MR_TEST_EXPECT_LE_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, LE, _b)) #define MR_TEST_EXPECT_LE_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, LE, _b)) #define MR_TEST_EXPECT_LE_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LE(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_FLOAT(_a, LE, _b)) #define MR_TEST_EXPECT_LE_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_LE(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_DOUBLE(_a, LE, _b)) #define MR_TEST_ASSERT_LE_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, LE, _b)) #define MR_TEST_ASSERT_LE_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, LE, _b)) #define MR_TEST_ASSERT_LE_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, LE, _b)) #define MR_TEST_ASSERT_LE_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LE(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_FLOAT(_a, LE, _b)) #define MR_TEST_ASSERT_LE_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_LE(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_DOUBLE(_a, LE, _b)) /* Test expect and assert greater than macros definition */ #define MR_TEST_EXPECT_GT_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GT(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, GT, _b)) #define MR_TEST_EXPECT_GT_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GT(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, GT, _b)) #define MR_TEST_EXPECT_GT_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GT(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, GT, _b)) #define MR_TEST_EXPECT_GT_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GT(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_FLOAT(_a, GT, _b)) #define MR_TEST_EXPECT_GT_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GT(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_DOUBLE(_a, GT, _b)) #define MR_TEST_ASSERT_GT_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GT(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, GT, _b)) #define MR_TEST_ASSERT_GT_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GT(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, GT, _b)) #define MR_TEST_ASSERT_GT_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GT(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, GT, _b)) #define MR_TEST_ASSERT_GT_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GT(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_FLOAT(_a, GT, _b)) #define MR_TEST_ASSERT_GT_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GT(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_DOUBLE(_a, GT, _b)) /* Test expect and assert greater than or equal macros definition */ #define MR_TEST_EXPECT_GE_INT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_EXPECT(__MR_TEST_COND_INT(_a, GE, _b)) #define MR_TEST_EXPECT_GE_UINT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_EXPECT(__MR_TEST_COND_UINT(_a, GE, _b)) #define MR_TEST_EXPECT_GE_PTR(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_EXPECT(__MR_TEST_COND_PTR(_a, GE, _b)) #define MR_TEST_EXPECT_GE_FLOAT(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GE(_a, _b, float, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_FLOAT(_a, GE, _b)) #define MR_TEST_EXPECT_GE_DOUBLE(_a, _b) \ - __MR_TEST_EXPECT(__MR_TEST_GE(_a, _b, double, "%f")) + __MR_TEST_EXPECT(__MR_TEST_COND_DOUBLE(_a, GE, _b)) #define MR_TEST_ASSERT_GE_INT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GE(_a, _b, mr_int64_t, "%lld")) + __MR_TEST_ASSERT(__MR_TEST_COND_INT(_a, GE, _b)) #define MR_TEST_ASSERT_GE_UINT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GE(_a, _b, mr_uint64_t, "%llu")) + __MR_TEST_ASSERT(__MR_TEST_COND_UINT(_a, GE, _b)) #define MR_TEST_ASSERT_GE_PTR(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GE(_a, _b, mr_ptr_t, "%p")) + __MR_TEST_ASSERT(__MR_TEST_COND_PTR(_a, GE, _b)) #define MR_TEST_ASSERT_GE_FLOAT(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GE(_a, _b, float, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_FLOAT(_a, GE, _b)) #define MR_TEST_ASSERT_GE_DOUBLE(_a, _b) \ - __MR_TEST_ASSERT(__MR_TEST_GE(_a, _b, double, "%f")) + __MR_TEST_ASSERT(__MR_TEST_COND_DOUBLE(_a, GE, _b)) + +/* Test expect and assert near macros definition */ +#define MR_TEST_EXPECT_NEAR(_a, _b, _e) \ + __MR_TEST_EXPECT(__MR_TEST_COND_NEAR(_a, _b, _e)) +#define MR_TEST_ASSERT_NEAR(_a, _b, _e) \ + __MR_TEST_ASSERT(__MR_TEST_COND_NEAR(_a, _b, _e)) /* Testcase entry type */ typedef void(mr_testcase_entry)(void); @@ -183,9 +189,26 @@ typedef struct MR_ALIGN(MR_ALIGN_SIZE) mr_testcase { __MR_TEST_EXPORT(_suite, _name, _entry, "1") /** - * @brief This function runs all testcases. + * @brief This function runs a testcase. + * + * @param suite The testcase suite name. + * @param name The testcase name. */ -int mr_test_all_run(void); +void mr_testcase_run(const char *suite, const char *name); + +/** + * @brief This function runs a testsuite. + * + * @param suite The testsuite name. + */ +void mr_testsuite_run(const char *suite); + +/** + * @brief This function runs all testcases. + * + * @return MR_TRUE on success, MR_FALSE on failure. + */ +mr_bool_t mr_test_run(void); #endif /* defined(MR_USE_TEST) */ /** @} */ diff --git a/libc/malloc.c b/libc/malloc.c index f5da95d..917fea9 100644 --- a/libc/malloc.c +++ b/libc/malloc.c @@ -90,6 +90,11 @@ void *mr_malloc(mr_size_t size) { /* Align the size */ size = HEAP_ALIGN(size, MR_ALIGN_SIZE); + if (size == 0) { + /* Overflow */ + mem = MR_NULL; + goto _exit; + } /* Search for and take blocks that match the criteria */ for (blk = prev->next; blk->size < size; blk = blk->next) { diff --git a/libc/sprintf.c b/libc/sprintf.c index 35a6373..c3a4a4c 100644 --- a/libc/sprintf.c +++ b/libc/sprintf.c @@ -211,7 +211,7 @@ int mr_vsnprintf(char *buf, mr_size_t size, const char *fmt, mr_va_list args) { char eff; /* Check parameter */ - if ((!fmt) || (fmt[0] == '\0')) { + if (!fmt) { return 0; } if (!buf) { @@ -478,7 +478,7 @@ _default: int mr_vsprintf(char *buf, const char *fmt, mr_va_list args) { /* Format string no limit */ - return mr_vsnprintf(buf, (mr_size_t)-1, fmt, args); + return mr_vsnprintf(buf, MR_SIZE_MAX - (mr_size_t)buf, fmt, args); } int mr_snprintf(char *buf, mr_size_t size, const char *fmt, ...) { diff --git a/libc/sscanf.c b/libc/sscanf.c index 9dc71d3..52a0a96 100644 --- a/libc/sscanf.c +++ b/libc/sscanf.c @@ -16,7 +16,7 @@ int mr_sscanf(const char *buf, const char *fmt, ...) { int ret; /* Check parameter */ - if ((!buf) || (!fmt) || (fmt[0] == '\0')) { + if ((!buf) || (!fmt)) { return -1; } diff --git a/libc/string.c b/libc/string.c index d294d92..a391202 100644 --- a/libc/string.c +++ b/libc/string.c @@ -9,6 +9,7 @@ #include #if defined(MR_USE_STRING) #include +#endif /* defined(MR_USE_STRING) */ /* DeBruijn ffs32 */ const int __mr_ffs_debruijn32[32] = { @@ -24,6 +25,7 @@ const int __mr_ffs_debruijn64[64] = { 55, 27, 41, 16, 35, 21, 32, 11, 26, 15, 20, 10, 14, 9, 8, 7, }; +#if defined(MR_USE_STRING) void *mr_memccpy(void *dst, const void *src, int c, mr_size_t size) { const char *s; char *d; diff --git a/test/kernel/list.c b/test/kernel/list.c new file mode 100644 index 0000000..7c8b4cc --- /dev/null +++ b/test/kernel/list.c @@ -0,0 +1,188 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include + +typedef struct test_node { + mr_list_t link; + int id; +} test_node_t; + +/* -------------------------------------------------------------------------- */ +/* Test: static init */ +static void test_list_static_init(void) { + static mr_list_t head = MR_LIST_INIT(&head); + + MR_TEST_ASSERT_EQ_INT(mr_list_is_empty(&head), MR_TRUE); + MR_TEST_ASSERT_EQ_PTR(head.next, &head); + MR_TEST_ASSERT_EQ_PTR(head.prev, &head); +} +MR_TEST_EXPORT(list, static_init, test_list_static_init); + +/* -------------------------------------------------------------------------- */ +/* Test: init/empty */ +static void test_list_init_empty(void) { + mr_list_t head; + + mr_list_init(&head); + MR_TEST_ASSERT_EQ_INT(mr_list_is_empty(&head), MR_TRUE); +} +MR_TEST_EXPORT(list, init_empty, test_list_init_empty); + +/* -------------------------------------------------------------------------- */ +/* Test: add elements */ +static void test_list_add(void) { + test_node_t n1, n2, n3; + int ids[3], idx; + test_node_t *n; + mr_list_t *pos; + mr_list_t head; + + mr_list_init(&head); + n1.id = 1; + n2.id = 2; + n3.id = 3; + mr_list_add(&head, &n1.link); + mr_list_add(&head, &n2.link); + mr_list_add(&head, &n3.link); + + idx = 0; + MR_LIST_FOR_EACH(pos, &head) { + n = MR_LIST_ENTRY(pos, test_node_t, link); + ids[idx++] = n->id; + } + MR_TEST_ASSERT_EQ_INT(ids[0], 1); + MR_TEST_ASSERT_EQ_INT(ids[1], 2); + MR_TEST_ASSERT_EQ_INT(ids[2], 3); +} +MR_TEST_EXPORT(list, add, test_list_add); + +/* -------------------------------------------------------------------------- */ +/* Test: insert to head */ +static void test_list_insert(void) { + test_node_t n1, n2; + test_node_t *n; + mr_list_t head; + mr_list_t *pos; + + mr_list_init(&head); + n1.id = 1; + n2.id = 2; + mr_list_insert(&head, &n1.link); + mr_list_insert(&head, &n2.link); + + pos = head.next; + n = MR_LIST_ENTRY(pos, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(n->id, 2); + pos = pos->next; + n = MR_LIST_ENTRY(pos, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(n->id, 1); +} +MR_TEST_EXPORT(list, insert, test_list_insert); + +/* -------------------------------------------------------------------------- */ +/* Test: remove element */ +static void test_list_del(void) { + test_node_t n1, n2; + test_node_t *n; + mr_list_t head; + + mr_list_init(&head); + n1.id = 1; + n2.id = 2; + mr_list_add(&head, &n1.link); + mr_list_add(&head, &n2.link); + + mr_list_del(&n1.link); + n = MR_LIST_ENTRY(head.next, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(mr_list_is_empty(&head), 0); + MR_TEST_ASSERT_EQ_INT(n->id, 2); +} +MR_TEST_EXPORT(list, del, test_list_del); + +/* -------------------------------------------------------------------------- */ +/* Test: replace element */ +static void test_list_replace(void) { + test_node_t n1, n2, n3; + test_node_t *n; + mr_list_t head; + mr_list_t *pos; + + mr_list_init(&head); + n1.id = 1; + n2.id = 2; + n3.id = 3; + mr_list_add(&head, &n1.link); + mr_list_add(&head, &n2.link); + + mr_list_replace(&n3.link, &n1.link); + + pos = head.next; + n = MR_LIST_ENTRY(pos, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(n->id, 3); + pos = pos->next; + n = MR_LIST_ENTRY(pos, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(n->id, 2); +} +MR_TEST_EXPORT(list, replace, test_list_replace); + +/* -------------------------------------------------------------------------- */ +/* Test: move element */ +static void test_list_move(void) { + mr_list_t head1, head2; + test_node_t n1; + test_node_t *n; + + mr_list_init(&head1); + mr_list_init(&head2); + n1.id = 42; + mr_list_add(&head1, &n1.link); + + mr_list_move(&n1.link, &head2); + n = MR_LIST_ENTRY(head2.next, test_node_t, link); + MR_TEST_ASSERT_EQ_INT(mr_list_is_empty(&head1), 1); + MR_TEST_ASSERT_EQ_INT(mr_list_is_empty(&head2), 0); + MR_TEST_ASSERT_EQ_INT(n->id, 42); +} +MR_TEST_EXPORT(list, move, test_list_move); + +/* -------------------------------------------------------------------------- */ +/* Test: safe traversal and delete */ +static void test_list_safe_foreach_del(void) { + test_node_t nodes[4]; + mr_list_t *pos, *l; + int i, ids[2], idx; + mr_list_t head; + test_node_t *n; + + mr_list_init(&head); + for (i = 0; i < 4; ++i) { + nodes[i].id = i; + mr_list_add(&head, &nodes[i].link); + } + + MR_LIST_FOR_EACH_SAFE(pos, l, &head) { + test_node_t *node = MR_LIST_ENTRY(pos, test_node_t, link); + if ((node->id % 2) == 0) { + mr_list_del(pos); + } + } + + idx = 0; + MR_LIST_FOR_EACH(pos, &head) { + n = MR_LIST_ENTRY(pos, test_node_t, link); + ids[idx++] = n->id; + } + MR_TEST_ASSERT_EQ_INT(idx, 2); + MR_TEST_ASSERT_EQ_INT(ids[0], 1); + MR_TEST_ASSERT_EQ_INT(ids[1], 3); +} +MR_TEST_EXPORT(list, safe_foreach_del, test_list_safe_foreach_del); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/kernel/object.c b/test/kernel/object.c new file mode 100644 index 0000000..c985651 --- /dev/null +++ b/test/kernel/object.c @@ -0,0 +1,95 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include + +typedef struct test_obj { + mr_object_t obj; + int data; +} test_obj_t; + +static void test_obj_release(struct mr_object *obj) { + test_obj_t *t = (test_obj_t *)obj; + t->data = -1; +} + +static const mr_clazz_t test_clazz = MR_CLAZZ_INIT("test", test_obj_release); + +/* -------------------------------------------------------------------------- */ +/* Test: Static initializer */ +static void test_object_static_init(void) { + static test_obj_t o + = {.obj = MR_OBJECT_INIT(&o.o, &test_clazz), .data = 42}; + + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_IS_INITED(&o.obj), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_CLAZZ_IS(&o.obj, &test_clazz), MR_TRUE); +} +MR_TEST_EXPORT(object, static_init, test_object_static_init); + +/* -------------------------------------------------------------------------- */ +/* Test: Dynamic init / del */ +static void test_object_init_del(void) { + test_obj_t o; + + MR_TEST_ASSERT_EQ_INT(mr_object_init(&o.obj, &test_clazz), 0); + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_IS_INITED(&o.obj), MR_TRUE); + + mr_object_del(&o.obj); + MR_TEST_ASSERT_EQ_INT(o.data, -1); +} +MR_TEST_EXPORT(object, init_del, test_object_init_del); + +/* -------------------------------------------------------------------------- */ +/* Test: Get / put reference counting */ +static void test_object_refcount(void) { + mr_object_t *p; + test_obj_t o; + + mr_object_init(&o.obj, &test_clazz); + + p = mr_object_get(&o.obj); + MR_TEST_ASSERT_EQ_PTR(p, &o.obj); + + p = mr_object_get(&o.obj); + MR_TEST_ASSERT_EQ_PTR(p, &o.obj); + MR_TEST_ASSERT_EQ_INT(mr_object_put(&o.obj), MR_FALSE); + MR_TEST_ASSERT_EQ_INT(mr_object_put(&o.obj), MR_FALSE); + MR_TEST_ASSERT_EQ_INT(mr_object_put(&o.obj), MR_TRUE); +} +MR_TEST_EXPORT(object, refcount, test_object_refcount); + +/* -------------------------------------------------------------------------- */ +/* Test: Null and double-init protection */ +static void test_object_invalid(void) { + test_obj_t o; + + MR_TEST_ASSERT_EQ_INT(mr_object_init(MR_NULL, &test_clazz), -MR_EINVAL); + MR_TEST_ASSERT_EQ_INT(mr_object_init(&o.obj, MR_NULL), 0); + + mr_object_init(&o.obj, &test_clazz); + MR_TEST_ASSERT_EQ_INT(mr_object_init(&o.obj, &test_clazz), -MR_EINVAL); +} +MR_TEST_EXPORT(object, invalid, test_object_invalid); + +/* -------------------------------------------------------------------------- */ +/* Test: MR_OBJECT_CLAZZ_IS_OR */ +static void test_object_clazz_or(void) { + test_obj_t o; + + mr_object_init(&o.obj, &test_clazz); + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_CLAZZ_IS_OR(&o.obj, &test_clazz, MR_NULL), + MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_CLAZZ_IS_OR(&o.obj, MR_NULL, &test_clazz), + MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_OBJECT_CLAZZ_IS_OR(&o.obj, MR_NULL, MR_NULL), + MR_FALSE); +} +MR_TEST_EXPORT(object, clazz_or, test_object_clazz_or); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/kernel/ref.c b/test/kernel/ref.c new file mode 100644 index 0000000..53b24e6 --- /dev/null +++ b/test/kernel/ref.c @@ -0,0 +1,74 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include + +static void stub_release(mr_ref_t *ref) { + *ref = 0xdeadbeef; +} + +/* -------------------------------------------------------------------------- */ +/* Test: static init */ +static void test_ref_static_init(void) { + static mr_ref_t r = MR_REF_INIT(); + + MR_TEST_ASSERT_EQ_INT(MR_REF_IS_INITED(r), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_REF_COUNT(r), 1); +} +MR_TEST_EXPORT(ref, static_init, test_ref_static_init); + +/* -------------------------------------------------------------------------- */ +/* Test: init/inited/count */ +static void test_ref_init(void) { + mr_ref_t r; + + mr_ref_init(&r); + MR_TEST_ASSERT_EQ_INT(MR_REF_IS_INITED(r), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_REF_COUNT(r), 1); +} +MR_TEST_EXPORT(ref, init, test_ref_init); + +/* -------------------------------------------------------------------------- */ +/* Test: get increments count */ +static void test_ref_get(void) { + mr_ref_t r; + + mr_ref_init(&r); + MR_TEST_ASSERT_EQ_INT(mr_ref_get(&r), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_REF_COUNT(r), 2); + MR_TEST_ASSERT_EQ_INT(mr_ref_get(&r), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(MR_REF_COUNT(r), 3); +} +MR_TEST_EXPORT(ref, get, test_ref_get); + +/* -------------------------------------------------------------------------- */ +/* Test: put decrements and release */ +static void test_ref_put(void) { + mr_ref_t r; + + mr_ref_init(&r); + MR_TEST_ASSERT_EQ_INT(mr_ref_put(&r, stub_release), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(r, 0xdeadbeef); + MR_TEST_ASSERT_EQ_INT(mr_ref_put(&r, stub_release), MR_FALSE); + MR_TEST_ASSERT_EQ_INT(r, 0xdeadbeef - 1); +} +MR_TEST_EXPORT(ref, put, test_ref_put); + +/* -------------------------------------------------------------------------- */ +/* Test: get after release fails */ +static void test_ref_get_fail(void) { + mr_ref_t r; + + mr_ref_init(&r); + mr_ref_put(&r, stub_release); + MR_TEST_ASSERT_EQ_INT(mr_ref_get(&r), MR_FALSE); +} +MR_TEST_EXPORT(ref, get_fail, test_ref_get_fail); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/kernel/spinlock.c b/test/kernel/spinlock.c new file mode 100644 index 0000000..5a6840b --- /dev/null +++ b/test/kernel/spinlock.c @@ -0,0 +1,52 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include + +/* -------------------------------------------------------------------------- */ +/* Test: static initializer */ +static void test_spinlock_static_init(void) { + static mr_spinlock_t lock = MR_SPINLOCK_INIT(); + + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 0); +} +MR_TEST_EXPORT(spinlock, static_init, test_spinlock_static_init); + +/* -------------------------------------------------------------------------- */ +/* Test: init / lock / unlock */ +static void test_spinlock_lock_unlock(void) { + mr_spinlock_t lock; + + mr_spinlock_init(&lock); + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 0); + + mr_spinlock_lock(&lock); + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 1); + + mr_spinlock_unlock(&lock); + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 0); +} +MR_TEST_EXPORT(spinlock, lock_unlock, test_spinlock_lock_unlock); + +/* -------------------------------------------------------------------------- */ +/* Test: lock_irqsave / unlock_irqrestore */ +static void test_spinlock_irq_save_restore(void) { + mr_spinlock_t lock; + int mask; + + mr_spinlock_init(&lock); + mask = mr_spinlock_lock_irqsave(&lock); + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 1); + + mr_spinlock_unlock_irqrestore(&lock, mask); + MR_TEST_ASSERT_EQ_INT(mr_spinlock_is_locked(&lock), 0); +} +MR_TEST_EXPORT(spinlock, irq_save_restore, test_spinlock_irq_save_restore); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/libc/atomic.c b/test/libc/atomic.c new file mode 100644 index 0000000..08f6121 --- /dev/null +++ b/test/libc/atomic.c @@ -0,0 +1,99 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include + +/* -------------------------------------------------------------------------- */ +/* Test: static init */ +static void test_atomic_static_init(void) { + static mr_atomic_t v = MR_ATOMIC_INIT(12345); + + MR_TEST_ASSERT_EQ_INT(mr_atomic_load(&v), 12345); +} +MR_TEST_EXPORT(atomic, static_init, test_atomic_static_init); + +/* -------------------------------------------------------------------------- */ +/* Test: load/store */ +static void test_atomic_load_store(void) { + mr_atomic_t v; + + mr_atomic_init(&v, 0); + mr_atomic_store(&v, 42); + MR_TEST_ASSERT_EQ_INT(mr_atomic_load(&v), 42); +} +MR_TEST_EXPORT(atomic, load_store, test_atomic_load_store); + +/* -------------------------------------------------------------------------- */ +/* Test: exchange */ +static void test_atomic_exchange(void) { + mr_atomic_t v, old; + + mr_atomic_init(&v, 5); + old = mr_atomic_exchange(&v, 7); + MR_TEST_ASSERT_EQ_INT(old, 5); + MR_TEST_ASSERT_EQ_INT(mr_atomic_load(&v), 7); +} +MR_TEST_EXPORT(atomic, exchange, test_atomic_exchange); + +/* -------------------------------------------------------------------------- */ +/* Test: compare-exchange success/fail */ +static void test_atomic_compare_exchange(void) { + mr_atomic_t v, expect; + + mr_atomic_init(&v, 3); + mr_atomic_store(&expect, 3); + MR_TEST_ASSERT_EQ_INT(mr_atomic_compare_exchange(&v, &expect, 9), MR_TRUE); + MR_TEST_ASSERT_EQ_INT(v, 9); + + expect = 3; + MR_TEST_ASSERT_EQ_INT(mr_atomic_compare_exchange(&v, &expect, 1), MR_FALSE); + MR_TEST_ASSERT_EQ_INT(expect, 9); +} +MR_TEST_EXPORT(atomic, compare_exchange, test_atomic_compare_exchange); + +/* -------------------------------------------------------------------------- */ +/* Test: fetch-add */ +static void test_atomic_fetch_add(void) { + mr_atomic_t v; + + mr_atomic_init(&v, 10); + MR_TEST_ASSERT_EQ_INT(mr_atomic_fetch_add(&v, 5), 10); + MR_TEST_ASSERT_EQ_INT(v, 15); +} +MR_TEST_EXPORT(atomic, fetch_add, test_atomic_fetch_add); + +/* -------------------------------------------------------------------------- */ +/* Test: fetch-sub */ +static void test_atomic_fetch_sub(void) { + mr_atomic_t v; + + mr_atomic_init(&v, 20); + MR_TEST_ASSERT_EQ_INT(mr_atomic_fetch_sub(&v, 8), 20); + MR_TEST_ASSERT_EQ_INT(v, 12); +} +MR_TEST_EXPORT(atomic, fetch_sub, test_atomic_fetch_sub); + +/* -------------------------------------------------------------------------- */ +/* Test: bitwise fetch-and/or/xor */ +static void test_atomic_bitwise(void) { + mr_atomic_t v; + + mr_atomic_init(&v, 0x0F); + MR_TEST_ASSERT_EQ_INT(mr_atomic_fetch_and(&v, 0x07), 0x0F); + MR_TEST_ASSERT_EQ_INT(v, 0x07); + + MR_TEST_ASSERT_EQ_INT(mr_atomic_fetch_or(&v, 0x08), 0x07); + MR_TEST_ASSERT_EQ_INT(v, 0x0F); + + MR_TEST_ASSERT_EQ_INT(mr_atomic_fetch_xor(&v, 0x0F), 0x0F); + MR_TEST_ASSERT_EQ_INT(v, 0x00); +} +MR_TEST_EXPORT(atomic, bitwise, test_atomic_bitwise); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/libc/malloc.c b/test/libc/malloc.c index a6c2e8c..0e0f164 100644 --- a/test/libc/malloc.c +++ b/test/libc/malloc.c @@ -39,12 +39,22 @@ static void test_malloc_free_basic(void) { mr_size_t usable; void *p; + p = mr_malloc(0); + MR_TEST_ASSERT_EQ_PTR(p, MR_NULL); + mr_free(MR_NULL); + p = mr_malloc(128); MR_TEST_ASSERT_NE_PTR(p, MR_NULL); usable = mr_malloc_usable_size(p); MR_TEST_ASSERT_GE_UINT(usable, 128); + usable = mr_malloc_usable_size(MR_NULL); + MR_TEST_ASSERT_EQ_UINT(usable, 0); + + usable = mr_malloc_usable_size(p + 32); + MR_TEST_ASSERT_EQ_UINT(usable, 0); + canary_fill(p, usable); MR_TEST_ASSERT_EQ_INT(canary_verify(p, usable), 1); @@ -56,9 +66,15 @@ MR_TEST_EXPORT(malloc, free_basic, test_malloc_free_basic); /* calloc test: memory must be zero-filled */ static void test_calloc_zero(void) { const mr_size_t n = 16, sz = 32; - mr_size_t usable; + mr_size_t usable, i; void *p; + p = mr_calloc(0, 0); + MR_TEST_ASSERT_EQ_PTR(p, MR_NULL); + + p = mr_calloc(MR_SIZE_MAX / 2, 1); + MR_TEST_ASSERT_EQ_PTR(p, MR_NULL); + p = mr_calloc(n, sz); MR_TEST_ASSERT_NE_PTR(p, MR_NULL); @@ -66,7 +82,7 @@ static void test_calloc_zero(void) { MR_TEST_ASSERT_GE_UINT(usable, n * sz); /* Verify all bytes are zero */ - for (mr_size_t i = 0; i < usable; ++i) { + for (i = 0; i < usable; ++i) { MR_TEST_ASSERT_EQ_INT(((mr_uint8_t *)p)[i], 0); } @@ -83,12 +99,13 @@ static void test_realloc_resize(void) { MR_TEST_ASSERT_NE_PTR(p1, MR_NULL); canary_fill(p1, 64); - /* Expand */ + p2 = mr_realloc(p1, MR_SIZE_MAX); + MR_TEST_ASSERT_EQ_PTR(p2, MR_NULL); + p2 = mr_realloc(p1, 256); MR_TEST_ASSERT_NE_PTR(p2, MR_NULL); MR_TEST_ASSERT_EQ_INT(canary_verify(p2, 64), 1); - /* Shrink */ p3 = mr_realloc(p2, 32); MR_TEST_ASSERT_NE_PTR(p3, MR_NULL); MR_TEST_ASSERT_EQ_INT(canary_verify(p3, 32), 1); @@ -146,7 +163,10 @@ MR_TEST_EXPORT(malloc, frag_merge, test_frag_merge); static void test_malloc_max(void) { const mr_size_t huge = 3 * 1024; mr_size_t usable; - void *p; + void *p, *n; + + p = mr_malloc(MR_SIZE_MAX - 32); + MR_TEST_ASSERT_EQ_PTR(p, MR_NULL); p = mr_malloc(huge); if (p) { diff --git a/test/libc/sprintf.c b/test/libc/sprintf.c new file mode 100644 index 0000000..424e64e --- /dev/null +++ b/test/libc/sprintf.c @@ -0,0 +1,106 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include +#include + +/* -------------------------------------------------------------------------- */ +/* Test: sprintf basic integer formatting */ +static void test_sprintf_basic_int(void) { + char buf[64]; + int ret; + + ret = mr_sprintf(buf, "%d", 123); + MR_TEST_ASSERT_EQ_INT(ret, 3); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "123"), 0); + + ret = mr_sprintf(buf, "%04d", 42); + MR_TEST_ASSERT_EQ_INT(ret, 4); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "0042"), 0); + + ret = mr_sprintf(buf, "%x", 0xAB); + MR_TEST_ASSERT_EQ_INT(ret, 2); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "ab"), 0); +} +MR_TEST_EXPORT(sprintf, basic_int, test_sprintf_basic_int); + +/* -------------------------------------------------------------------------- */ +/* Test: sprintf string formatting */ +static void test_sprintf_string(void) { + char buf[64]; + int ret; + + ret = mr_sprintf(buf, "%s", "hello"); + MR_TEST_ASSERT_EQ_INT(ret, 5); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "hello"), 0); + + ret = mr_sprintf(buf, "%10s", "test"); + MR_TEST_ASSERT_EQ_INT(ret, 10); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, " test"), 0); +} +MR_TEST_EXPORT(sprintf, string, test_sprintf_string); + +/* -------------------------------------------------------------------------- */ +/* Test: sprintf mixed formatting */ +static void test_sprintf_mixed(void) { + char buf[128]; + int ret; + + ret = mr_sprintf(buf, "Val: %d, Str: %s, Hex: %x", 42, "world", 255); + MR_TEST_ASSERT_EQ_INT(ret, 28); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "Val: 42, Str: world, Hex: ff"), 0); +} +MR_TEST_EXPORT(sprintf, mixed, test_sprintf_mixed); + +/* -------------------------------------------------------------------------- */ +/* Test: snprintf with buffer size limit */ +static void test_snprintf_limit(void) { + char buf[10]; + int ret; + + ret = mr_snprintf(buf, sizeof(buf), "%s", "1234567890"); + MR_TEST_ASSERT_EQ_INT(ret, 10); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "123456789"), 0); +} +MR_TEST_EXPORT(sprintf, snprintf_limit, test_snprintf_limit); + +/* -------------------------------------------------------------------------- */ +/* Test: sprintf pointer formatting */ +static void test_sprintf_pointer(void) { + const void *ptr = (void *)0x12345678; + char buf[32]; + int ret; + + ret = mr_sprintf(buf, "%p", ptr); + MR_TEST_ASSERT_GT_INT(ret, 0); + MR_TEST_ASSERT_NE_PTR(mr_memchr(buf, '1', (mr_size_t)ret), MR_NULL); +} +MR_TEST_EXPORT(sprintf, pointer, test_sprintf_pointer); + +/* -------------------------------------------------------------------------- */ +/* Test: sprintf edge cases */ +static void test_sprintf_edge_cases(void) { + char buf[64]; + int ret; + + ret = mr_sprintf(buf, ""); + MR_TEST_ASSERT_EQ_INT(ret, 0); + MR_TEST_ASSERT_EQ_INT(buf[0], '\0'); + + ret = mr_sprintf(buf, "%05d", 7); + MR_TEST_ASSERT_EQ_INT(ret, 5); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "00007"), 0); + + ret = mr_sprintf(buf, "%-5d", 42); + MR_TEST_ASSERT_EQ_INT(ret, 5); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "42 "), 0); +} +MR_TEST_EXPORT(sprintf, edge_cases, test_sprintf_edge_cases); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/libc/sscanf.c b/test/libc/sscanf.c new file mode 100644 index 0000000..f486414 --- /dev/null +++ b/test/libc/sscanf.c @@ -0,0 +1,46 @@ +/** + * @copyright (c) 2024-2025, MacRsh + * + * @license SPDX-License-Identifier: Apache-2.0 + * + * @date 2024-09-06 MacRsh First version + */ + +#include +#if defined(MR_USE_TEST) +#include +#include + +/* -------------------------------------------------------------------------- */ +/* Test: basic integer and string extraction */ +static void test_sscanf_basic(void) { + int val, ret; + char buf[8]; + + ret = mr_sscanf("42 hello", "%d %7s", &val, buf); + MR_TEST_ASSERT_EQ_INT(ret, 2); + MR_TEST_ASSERT_EQ_INT(val, 42); + MR_TEST_ASSERT_EQ_INT(mr_strcmp(buf, "hello"), 0); +} +MR_TEST_EXPORT(sscanf, basic, test_sscanf_basic); + +/* -------------------------------------------------------------------------- */ +/* Test: format mismatch */ +static void test_sscanf_mismatch(void) { + int val, ret; + + ret = mr_sscanf("abc", "%d", &val); + MR_TEST_ASSERT_EQ_INT(ret, 0); +} +MR_TEST_EXPORT(sscanf, mismatch, test_sscanf_mismatch); + +/* -------------------------------------------------------------------------- */ +/* Test: null / empty input */ +static void test_sscanf_null_empty(void) { + int val; + + MR_TEST_ASSERT_EQ_INT(mr_sscanf("", "%d", &val), -1); + MR_TEST_ASSERT_EQ_INT(mr_sscanf("123", "", &val), 0); +} +MR_TEST_EXPORT(sscanf, null_empty, test_sscanf_null_empty); +#endif /* defined(MR_USE_TEST) */ diff --git a/test/libc/string.c b/test/libc/string.c index ef96f9d..40224ed 100644 --- a/test/libc/string.c +++ b/test/libc/string.c @@ -51,6 +51,18 @@ static void test_memcpy_basic(void) { mr_memcpy(dst, src, SZ); MR_TEST_ASSERT_EQ_INT(canary_verify(dst, SZ), 1); + canary_fill(src, SZ / 3); + mr_memcpy(dst, src, SZ / 3); + MR_TEST_ASSERT_EQ_INT(canary_verify(dst, SZ / 3), 1); + + canary_fill(src + 1, SZ - 1); + mr_memcpy(dst + 1, src + 1, SZ - 1); + MR_TEST_ASSERT_EQ_INT(canary_verify(dst + 1, SZ - 1), 1); + + canary_fill(src + 1, SZ - 1); + mr_memcpy(dst, src + 1, SZ - 1); + MR_TEST_ASSERT_EQ_INT(canary_verify(dst, SZ - 1), 1); + mr_free(src); mr_free(dst); } @@ -73,6 +85,11 @@ static void test_memset_basic(void) { MR_TEST_ASSERT_EQ_INT(buf[i], 0x5A); } + mr_memset(buf + 1, 0x5A, SZ - 1); + for (i = 1; i < SZ - 1; ++i) { + MR_TEST_ASSERT_EQ_INT(buf[i], 0x5A); + } + mr_free(buf); } MR_TEST_EXPORT(string, memset_basic, test_memset_basic); @@ -103,9 +120,10 @@ MR_TEST_EXPORT(string, memchr_find, test_memchr_find); static void test_memccpy_stop(void) { const char src[16] = "Hello\0World"; char dst[16]; + void *e; mr_memset(dst, '\0', sizeof(dst)); - void *e = mr_memccpy(dst, src, 'o', sizeof(dst)); + e = mr_memccpy(dst, src, 'o', sizeof(dst)); MR_TEST_ASSERT_NE_PTR(e, MR_NULL); MR_TEST_ASSERT_EQ_INT(dst[4], 'o'); MR_TEST_ASSERT_EQ_INT(dst[5], '\0'); @@ -129,15 +147,38 @@ static void test_strcpy_basic(void) { } MR_TEST_EXPORT(string, strcpy_basic, test_strcpy_basic); +/* -------------------------------------------------------------------------- */ +/* Test: strerror copies C-string including terminator */ +static void test_strerror_basic(void) { + mr_strerror(0); + mr_strerror(MR_EPERM); + mr_strerror(MR_ENOENT); + mr_strerror(MR_EIO); + mr_strerror(MR_EAGAIN); + mr_strerror(MR_ENOMEM); + mr_strerror(MR_EBUSY); + mr_strerror(MR_EEXIST); + mr_strerror(MR_EINVAL); + mr_strerror(MR_ENOSYS); + mr_strerror(MR_ETIMEDOUT); + mr_strerror(-99999); +} +MR_TEST_EXPORT(string, strerror_basic, test_strerror_basic); + /* -------------------------------------------------------------------------- */ /* Test: strncpy copies at most n bytes */ static void test_strncpy_trunc(void) { - const char *src = "long_string"; + const char *long_src = "long_string"; + const char *short_src = "str"; char buf[8]; - mr_strncpy(buf, src, sizeof(buf)); + mr_strncpy(buf, long_src, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; MR_TEST_ASSERT_EQ_INT(mr_memcmp(buf, "long_st", sizeof(buf)), 0); + + mr_strncpy(buf, short_src, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + MR_TEST_ASSERT_EQ_INT(mr_memcmp(buf, "str", 4), 0); } MR_TEST_EXPORT(string, strncpy_trunc, test_strncpy_trunc); @@ -174,4 +215,48 @@ static void test_strchr_find(void) { MR_TEST_ASSERT_EQ_PTR(mr_strchr(s, 'X'), MR_NULL); } MR_TEST_EXPORT(string, strchr_find, test_strchr_find); + +/* -------------------------------------------------------------------------- */ +/* Test: ffs32 basic cases */ +static void test_ffs32_basic(void) { + MR_TEST_ASSERT_EQ_INT(mr_ffs32(0), 0); + MR_TEST_ASSERT_EQ_INT(mr_ffs32(1), 1); + MR_TEST_ASSERT_EQ_INT(mr_ffs32(0x80000000U), 32); + MR_TEST_ASSERT_EQ_INT(mr_ffs32(0x00010000U), 17); + MR_TEST_ASSERT_EQ_INT(mr_ffs32(0x000000FFU), 1); +} +MR_TEST_EXPORT(string, ffs32_basic, test_ffs32_basic); + +/* -------------------------------------------------------------------------- */ +/* Test: ffs64 basic cases */ +static void test_ffs64_basic(void) { + MR_TEST_ASSERT_EQ_INT(mr_ffs64(0), 0); + MR_TEST_ASSERT_EQ_INT(mr_ffs64(1ULL), 1); + MR_TEST_ASSERT_EQ_INT(mr_ffs64(0x8000000000000000ULL), 64); + MR_TEST_ASSERT_EQ_INT(mr_ffs64(0x0000000100000000ULL), 33); + MR_TEST_ASSERT_EQ_INT(mr_ffs64(0x00000000000000FFULL), 1); +} +MR_TEST_EXPORT(string, ffs64_basic, test_ffs64_basic); + +/* -------------------------------------------------------------------------- */ +/* Test: fls32 basic cases */ +static void test_fls32_basic(void) { + MR_TEST_ASSERT_EQ_INT(mr_fls32(0), 0); + MR_TEST_ASSERT_EQ_INT(mr_fls32(1), 1); + MR_TEST_ASSERT_EQ_INT(mr_fls32(0x80000000U), 32); + MR_TEST_ASSERT_EQ_INT(mr_fls32(0x00010000U), 17); + MR_TEST_ASSERT_EQ_INT(mr_fls32(0x000000FFU), 8); +} +MR_TEST_EXPORT(string, fls32_basic, test_fls32_basic); + +/* -------------------------------------------------------------------------- */ +/* Test: fls64 basic cases */ +static void test_fls64_basic(void) { + MR_TEST_ASSERT_EQ_INT(mr_fls64(0), 0); + MR_TEST_ASSERT_EQ_INT(mr_fls64(1ULL), 1); + MR_TEST_ASSERT_EQ_INT(mr_fls64(0x8000000000000000ULL), 64); + MR_TEST_ASSERT_EQ_INT(mr_fls64(0x0000000100000000ULL), 33); + MR_TEST_ASSERT_EQ_INT(mr_fls64(0x00000000000000FFULL), 8); +} +MR_TEST_EXPORT(string, fls64_basic, test_fls64_basic); #endif /* defined(MR_USE_TEST) */ diff --git a/test/test.c b/test/test.c index 943787e..3c2b554 100644 --- a/test/test.c +++ b/test/test.c @@ -10,6 +10,7 @@ #if defined(MR_USE_TEST) #include #include +#include /* Test status */ mr_uint32_t __mr_test_status = __MR_TEST_MAGIC_PASS; @@ -24,45 +25,107 @@ static void end(void) { } __MR_TEST_EXPORT(sys, end, end, "1.end"); -int mr_test_all_run(void) { +static mr_bool_t testcase_run(const mr_testcase_t *cs) { + mr_tick_t tick; + + /* Check parameter */ + MR_ASSERT(cs->entry != MR_NULL); + + /* Test case begin */ + mr_printf("[ RUN ] %s.%s\n", cs->suite, cs->name); + + /* Reset test status */ + __mr_test_status = __MR_TEST_MAGIC_PASS; + + /* Get begin tick */ + tick = mr_clock_tick(); + + /* Call test entry */ + cs->entry(); + + /* Get difference tick */ + tick = mr_clock_tick() - tick; + + /* Test case end */ + if (__mr_test_status == __MR_TEST_MAGIC_PASS) { + mr_printf( + "[ " MR_COLOR(MR_COLOR_GREEN, "OK") " ] %s.%s (%d tick)\n", + cs->suite, cs->name, tick); + return MR_TRUE; + } else { + mr_printf( + "[ " MR_COLOR(MR_COLOR_RED, "FAILED") " ] %s.%s (%d tick)\n", + cs->suite, cs->name, tick); + return MR_FALSE; + } +} + +void mr_testcase_run(const char *suite, const char *name) { + const mr_testcase_t *start = &__mr_testcase_sys_start; + const mr_testcase_t *end = &__mr_testcase_sys_end; + const mr_testcase_t *cs; + + /* Each test cases */ + for (cs = start + 1; cs < end; cs++) { + /* Match testcase */ + if ((!mr_strcmp(cs->suite, suite)) || (!mr_strcmp(cs->name, name))) { + continue; + } + + /* Run testcase */ + testcase_run(cs); + } +} + +void mr_testsuite_run(const char *suite) { const mr_testcase_t *start = &__mr_testcase_sys_start; const mr_testcase_t *end = &__mr_testcase_sys_end; const mr_testcase_t *cs; int pass, fail; - mr_tick_t tick; + mr_bool_t ret; /* Test begin */ - mr_printf("[==========] Running tests\n"); + mr_printf("[==========] Running '%s' suite\n", suite); /* Each test cases */ for (pass = fail = 0, cs = start + 1; cs < end; cs++) { - MR_ASSERT(cs->entry != MR_NULL); + /* Match testcase */ + if (mr_strcmp(cs->suite, suite) != 0) { + continue; + } - /* Test case begin */ - mr_printf("[ RUN ] %s.%s\n", cs->suite, cs->name); - - /* Reset test status */ - __mr_test_status = __MR_TEST_MAGIC_PASS; - - /* Get begin tick */ - tick = mr_clock_tick(); - - /* Call test entry */ - cs->entry(); - - /* Get difference tick */ - tick = mr_clock_tick() - tick; - - /* Test case end */ - if (__mr_test_status == __MR_TEST_MAGIC_PASS) { - mr_printf("[ " MR_COLOR(MR_COLOR_GREEN, - "OK") " ] %s.%s (%d tick)\n", - cs->suite, cs->name, tick); + /* Run testcase */ + ret = testcase_run(cs); + if (ret) { + pass++; + } else { + fail++; + } + } + + /* Test summary */ + mr_printf("[==========]\n"); + mr_printf("[ PASSED ] %d tests\n", pass); + mr_printf("[ FAILED ] %d tests\n", fail); +} + +mr_bool_t mr_test_run(void) { + const mr_testcase_t *start = &__mr_testcase_sys_start; + const mr_testcase_t *end = &__mr_testcase_sys_end; + const mr_testcase_t *cs; + int pass, fail; + mr_bool_t ret; + + /* Test begin */ + mr_printf("[==========] Running 'all' tests\n"); + + /* Each test cases */ + for (pass = fail = 0, cs = start + 1; cs < end; cs++) { + /* Run testcase */ + ret = testcase_run(cs); + if (ret) { pass++; } else { - mr_printf( - "[ " MR_COLOR(MR_COLOR_RED, "FAILED") " ] %s.%s (%d tick)\n", - cs->suite, cs->name, tick); fail++; } } @@ -72,7 +135,7 @@ int mr_test_all_run(void) { mr_printf("[ PASSED ] %d tests\n", pass); mr_printf("[ FAILED ] %d tests\n", fail); - /* Test end */ - return 0; + /* Return result */ + return (fail == 0) ? MR_TRUE : MR_FALSE; } #endif /* defined(MR_USE_TEST) */