diff --git a/src/util/memory.c b/src/util/memory.c index cef2e86..933089e 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -27,6 +27,14 @@ void memoryCopy(void *dest, const void *src, const size_t size) { assertNotNull(src, "Cannot copy from NULL memory."); assertTrue(size > 0, "Cannot copy 0 bytes of memory."); assertTrue(dest != src, "Cannot copy memory to itself."); + + // Check for overlapping regions + assertTrue( + ((uint8_t*)dest + size <= (uint8_t*)src) || + ((uint8_t*)src + size <= (uint8_t*)dest), + "Source and destination memory regions overlap." + ); + memcpy(dest, src, size); } diff --git a/src/util/string.c b/src/util/string.c index 42c0a9c..918962f 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -51,12 +51,6 @@ void stringTrim(char_t *str) { if(start != str) memmove(str, start, end - start + 2); } -char_t * stringToken(char_t *str, const char_t *delim) { - assertNotNull(str, "str must not be NULL"); - assertNotNull(delim, "delim must not be NULL"); - return strtok(str, delim); -} - char_t * stringFindLastChar(const char_t *str, const char_t c) { assertNotNull(str, "str must not be NULL"); char_t *last = NULL; @@ -106,12 +100,25 @@ bool_t stringToI32(const char_t *str, int32_t *out) { assertNotNull(str, "str must not be NULL"); assertNotNull(out, "out must not be NULL"); + // Empty string check + if(str[0] == '\0') return false; + + // Leading or trailing whitespace check + if(stringIsWhitespace(str[0])) return false; + if(stringIsWhitespace(str[strlen(str) - 1])) return false; + char_t *endptr; errno = 0; long int result = strtol(str, &endptr, 10); if(errno != 0 || *endptr != '\0') { return false; } + + // Within INT32 range + if(result < INT32_MIN || result > INT32_MAX) { + return false; + } + *out = (int32_t)result; return true; } @@ -120,6 +127,13 @@ bool_t stringToI64(const char_t *str, int64_t *out) { assertNotNull(str, "str must not be NULL"); assertNotNull(out, "out must not be NULL"); + // Empty string check + if(str[0] == '\0') return false; + + // Leading or trailing whitespace check + if(stringIsWhitespace(str[0])) return false; + if(stringIsWhitespace(str[strlen(str) - 1])) return false; + char_t *endptr; errno = 0; long long int result = strtoll(str, &endptr, 10); @@ -130,10 +144,38 @@ bool_t stringToI64(const char_t *str, int64_t *out) { return true; } +bool_t stringToI16(const char_t *str, int16_t *out) { + assertNotNull(str, "str must not be NULL"); + assertNotNull(out, "out must not be NULL"); + + // Empty string check + if(str[0] == '\0') return false; + + // Leading or trailing whitespace check + if(stringIsWhitespace(str[0])) return false; + if(stringIsWhitespace(str[strlen(str) - 1])) return false; + + char_t *endptr; + errno = 0; + long int result = strtol(str, &endptr, 10); + if(errno != 0 || *endptr != '\0' || result < INT16_MIN || result > INT16_MAX) { + return false; + } + *out = (int16_t)result; + return true; +} + bool_t stringToU16(const char_t *str, uint16_t *out) { assertNotNull(str, "str must not be NULL"); assertNotNull(out, "out must not be NULL"); + // Empty string check + if(str[0] == '\0') return false; + + // Leading or trailing whitespace check + if(stringIsWhitespace(str[0])) return false; + if(stringIsWhitespace(str[strlen(str) - 1])) return false; + char_t *endptr; errno = 0; unsigned long int result = strtoul(str, &endptr, 10); @@ -148,6 +190,13 @@ bool_t stringToF32(const char_t *str, float_t *out) { assertNotNull(str, "str must not be NULL"); assertNotNull(out, "out must not be NULL"); + // Empty string check + if(str[0] == '\0') return false; + + // Leading or trailing whitespace check + if(stringIsWhitespace(str[0])) return false; + if(stringIsWhitespace(str[strlen(str) - 1])) return false; + char_t *endptr; errno = 0; float_t result = strtof(str, &endptr); diff --git a/src/util/string.h b/src/util/string.h index 3c55a0f..c6e2887 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -53,17 +53,6 @@ int stringCompareInsensitive(const char_t *str1, const char_t *str2); */ void stringTrim(char_t *str); -/** - * Gets the next token in a string using a delimiter. - * e.g. input: "Hello, World, Happy Monday!" with stringToken(input, ",") will - * return "Hello" then " World" then " Happy Monday!" on each subsequent call. - * - * @param str The string to split. - * @param delim The delimiter to split by. - * @return A pointer to the next token in the string. - */ -char_t * stringToken(char_t *str, const char_t *delim); - /** * Finds the last occurrence of a character in a string. * @@ -119,6 +108,15 @@ int32_t stringFormatVA( */ bool_t stringToI32(const char_t *str, int32_t *out); +/** + * Converts a string to a signed 64-bit integer. + * + * @param str The string to convert. + * @param out The output signed integer. + * @return TRUE if the conversion was successful, FALSE otherwise. + */ +bool_t stringToI64(const char_t *str, int64_t *out); + /** * Converts a string to a signed 16-bit integer. * diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt index 562d640..c393ae8 100644 --- a/test/util/CMakeLists.txt +++ b/test/util/CMakeLists.txt @@ -7,4 +7,5 @@ include(dusktest) # Tests dusktest(test_math.c) -dusktest(test_memory.c) \ No newline at end of file +dusktest(test_memory.c) +dusktest(test_string.c) \ No newline at end of file diff --git a/test/util/test_math.c b/test/util/test_math.c index fa3d12e..8225a80 100644 --- a/test/util/test_math.c +++ b/test/util/test_math.c @@ -37,6 +37,10 @@ static void test_mathNextPowTwo(void **state) { // Max Value assert_int_equal(mathNextPowTwo(0xFFFFFFFF), 1); + + // Edge: value just below and above a power of two + assert_int_equal(mathNextPowTwo(255), 256); + assert_int_equal(mathNextPowTwo(257), 512); } static void test_mathMax(void **state) { @@ -80,6 +84,13 @@ static void test_mathMax(void **state) { assert_float_equal(mathMax(-1.5f, 0), 0.0f, 0.0001f); assert_float_equal(mathMax(500, -333.5f), 500.0f, 0.0001f); assert_float_equal(mathMax(-333.5f, 500), 500.0f, 0.0001f); + + // Equal values + assert_int_equal(mathMax(5, 5), 5); + assert_float_equal(mathMax(2.5f, 2.5f), 2.5f, 0.0001f); + + // Special float values + assert_float_equal(mathMax(-0.0f, 0.0f), 0.0f, 0.0001f); } static void test_mathMin(void **state) { @@ -124,6 +135,13 @@ static void test_mathMin(void **state) { assert_float_equal(mathMin(-1.5f, 0), -1.5f, 0.0001f); assert_float_equal(mathMin(500, -333.5f), -333.5f, 0.0001f); assert_float_equal(mathMin(-333.5f, 500), -333.5f, 0.0001f); + + // Equal values + assert_int_equal(mathMin(5, 5), 5); + assert_float_equal(mathMin(2.5f, 2.5f), 2.5f, 0.0001f); + + // Special float values + assert_float_equal(mathMin(-0.0f, 0.0f), -0.0f, 0.0001f); } static void test_mathClamp(void **state) { @@ -172,6 +190,16 @@ static void test_mathClamp(void **state) { assert_float_equal(mathClamp(-5.5f, -10, -1), -5.5f, 0.0001f); // Within assert_float_equal(mathClamp(-15.5f, -10, -1), -10.0f, 0.0001f);// Below assert_float_equal(mathClamp(0.0f, -10, -1), -1.0f, 0.0001f); // Above + + // Edge: value equals min or max + assert_int_equal(mathClamp(1, 1, 10), 1); + assert_int_equal(mathClamp(10, 1, 10), 10); + assert_float_equal(mathClamp(1.0f, 1.0f, 10.0f), 1.0f, 0.0001f); + assert_float_equal(mathClamp(10.0f, 1.0f, 10.0f), 10.0f, 0.0001f); + + // Edge: min > max (undefined, but should return min or max) + assert_int_equal(mathClamp(5, 10, 1), 1); // Should clamp to upper + assert_float_equal(mathClamp(5.0f, 10.0f, 1.0f), 1.0f, 0.0001f); } static void test_mathAbs(void **state) { @@ -192,6 +220,12 @@ static void test_mathAbs(void **state) { // Negative floats assert_float_equal(mathAbs(-5.5f), 5.5f, 0.0001f); assert_float_equal(mathAbs(-100.25f), 100.25f, 0.0001f); + + // Edge: -0.0f + assert_float_equal(mathAbs(-0.0f), 0.0f, 0.0001f); + + // INT_MIN edge case is NOT handled due to two's complement overflow + assert_int_equal(mathAbs(INT32_MIN + 1), 2147483647); } int main(int argc, char **argv) { diff --git a/test/util/test_memory.c b/test/util/test_memory.c index b38661a..ab89b0b 100644 --- a/test/util/test_memory.c +++ b/test/util/test_memory.c @@ -65,6 +65,12 @@ static void test_memoryCopy(void **state) { // Cannot copy to itself expect_assert_failure(memoryCopy(src, src, size)); + // Overlapping regions (should assert) + void *overlap = memoryAllocate(size * 2); + expect_assert_failure(memoryCopy((uint8_t*)overlap, (uint8_t*)overlap + 8, size)); + expect_assert_failure(memoryCopy((uint8_t*)overlap + 8, (uint8_t*)overlap, size)); + memoryFree(overlap); + memoryFree(src); memoryFree(dest); } @@ -91,6 +97,12 @@ static void test_memorySet(void **state) { // Canot set 0 bytes expect_assert_failure(memorySet(ptr, 0x00, 0)); + // Set with value outside 0-255 (should be cast to uint8_t) + memorySet(ptr, 0x1FF, size); + for(size_t i = 0; i < size; i++) { + assert_int_equal(((uint8_t*)ptr)[i], 0xFF); + } + memoryFree(ptr); } @@ -139,6 +151,10 @@ static void test_memoryCopyRangeSafe(void **state) { uint8_t expected[10] = {3, 4, 5, 6, 7}; // Expected data in dest assert_memory_equal(dest, expected, 5); + // Copy entire buffer + memoryCopyRangeSafe(dest, src, (uint8_t*)src + size, size); + assert_memory_equal(dest, src, size); + // Cannot copy to NULL destination expect_assert_failure(memoryCopyRangeSafe(NULL, src, (uint8_t*)src + 5, size)); @@ -187,6 +203,15 @@ static void test_memoryMove(void **state) { } assert_memory_equal(ptr, expected, size + 5); + // Overlap backward: move ptr[5..19] to ptr[0..14] + for(size_t i = 0; i < size + 5; i++) { + ((uint8_t*)ptr)[i] = (uint8_t)(i + 1); + } + memoryMove(ptr, (uint8_t*)ptr + 5, size); + for(size_t i = 0; i < size; i++) { + assert_int_equal(((uint8_t*)ptr)[i], (uint8_t)(i + 6)); + } + // Cannot move to NULL expect_assert_failure(memoryMove(NULL, ptr, size)); @@ -243,6 +268,9 @@ static void test_memoryCompare(void **state) { // Cannot compare 0 bytes expect_assert_failure(memoryCompare(a, b, 0)); + // Compare different sizes (should assert, so we only test the API contract) + // Not possible with current API, as size is explicit, but document intent. + memoryFree(a); memoryFree(b); } diff --git a/test/util/test_string.c b/test/util/test_string.c new file mode 100644 index 0000000..ce22ebf --- /dev/null +++ b/test/util/test_string.c @@ -0,0 +1,871 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "dusktest.h" +#include "util/string.h" + +static void test_stringIsWhitespace(void **state) { + (void)state; + assert_true(stringIsWhitespace(' ')); // Space is whitespace + assert_true(stringIsWhitespace('\t')); // Tab is whitespace + assert_true(stringIsWhitespace('\n')); // Newline is whitespace + assert_true(stringIsWhitespace('\r')); // Carriage return is whitespace + assert_true(stringIsWhitespace('\v')); // Vertical tab is whitespace + assert_true(stringIsWhitespace('\f')); // Form feed is whitespace + assert_false(stringIsWhitespace('A')); // 'A' is not whitespace + assert_false(stringIsWhitespace('1')); // '1' is not whitespace + assert_false(stringIsWhitespace('_')); // '_' is not whitespace + assert_false(stringIsWhitespace('-')); // '-' is not whitespace +} + +static void test_stringCopy(void **state) { + (void)state; + char dest[16]; + stringCopy(dest, "hello", sizeof(dest)); + assert_string_equal(dest, "hello"); // Normal copy + + // Empty string + stringCopy(dest, "", sizeof(dest)); + assert_string_equal(dest, ""); // Should copy empty string + + // Euffer just fits + stringCopy(dest, "123456789012345", 16); // 15 chars + null + assert_string_equal(dest, "123456789012345"); // Should fit exactly + + // Cannot copy from NULL source + expect_assert_failure(stringCopy(dest, NULL, sizeof(dest))); + + // Cannot copy to NULL destination + expect_assert_failure(stringCopy(NULL, "hello", sizeof(dest))); + + // Must have destSize > 0 + expect_assert_failure(stringCopy(dest, "hello", 0)); + + // Must have destSize large enough for source + expect_assert_failure(stringCopy(dest, "this string is too long", 10)); +} + +static void test_stringCompare(void **state) { + (void)state; + + // Check for equality + assert_int_equal(stringCompare("abc", "abc"), 0); + assert_int_equal(stringCompare("", ""), 0); + assert_int_equal(stringCompare("a", "a"), 0); + assert_int_equal(stringCompare("longer string", "longer string"), 0); + + // Check for less than cases + assert_true(stringCompare("", "a") < 0); + assert_true(stringCompare("abc", "abd") < 0); + + // Check for greater than cases + assert_true(stringCompare("abd", "abc") > 0); + assert_true(stringCompare("a", "") > 0); + + // Cannot compare NULL strings + expect_assert_failure(stringCompare(NULL, "abc")); + expect_assert_failure(stringCompare("abc", NULL)); +} + +static void test_stringCompareInsensitive(void **state) { + (void)state; + + assert_int_equal(stringCompareInsensitive("abc", "ABC"), 0); + assert_true(stringCompareInsensitive("abc", "abd") < 0); + assert_true(stringCompareInsensitive("abd", "abc") > 0); + + assert_true(stringCompareInsensitive("", "A") < 0); + assert_true(stringCompareInsensitive("A", "") > 0); + + // Cannot compare NULL strings + expect_assert_failure(stringCompareInsensitive(NULL, "abc")); + expect_assert_failure(stringCompareInsensitive("abc", NULL)); +} + +static void test_stringTrim(void **state) { + (void)state; + + char buf[32]; + stringCopy(buf, " hello ", sizeof(buf)); + + stringTrim(buf); + assert_string_equal(buf, "hello"); // Trims spaces + + // Non-space whitespace + stringCopy(buf, "\t\nhello world\r\n", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, "hello world"); + + // Only whitespace + stringCopy(buf, " ", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, ""); + + // No whitespace + stringCopy(buf, "no-trim", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, "no-trim"); + + // Left whitespace only + stringCopy(buf, " left", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, "left"); + + // Right whitespace only + stringCopy(buf, "right ", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, "right"); + + // Cannot trim NULL string + expect_assert_failure(stringTrim(NULL)); + + // Can trim empty string + stringCopy(buf, "", sizeof(buf)); + stringTrim(buf); + assert_string_equal(buf, ""); +} + +static void test_stringFindLastChar(void **state) { + (void)state; + + const char *str = "hello world"; + char *result = stringFindLastChar(str, 'o'); + assert_non_null(result); + assert_int_equal(*result, 'o'); + assert_int_equal(result - str, 7); // Position of last 'o' + + result = stringFindLastChar(str, 'h'); + assert_non_null(result); + assert_int_equal(*result, 'h'); + assert_int_equal(result - str, 0); // Position of 'h' + + result = stringFindLastChar(str, 'z'); + assert_null(result); // 'z' not found + + // Cannot search NULL string + expect_assert_failure(stringFindLastChar(NULL, 'a')); +} + +static void test_stringFormat(void **state) { + (void)state; + + // Test a string format + char buffer[32]; + int32_t len = stringFormat(buffer, sizeof(buffer), "Hello %s!", "World"); + assert_int_equal(len, 12); + assert_string_equal(buffer, "Hello World!"); + + // Test a number format + len = stringFormat(buffer, sizeof(buffer), "Number: %d", 42); + assert_int_equal(len, 10); + assert_string_equal(buffer, "Number: 42"); + + // Test floats with different precision + len = stringFormat(buffer, sizeof(buffer), "Float: %.2f", 3.14159); + assert_int_equal(len, 11); + assert_string_equal(buffer, "Float: 3.14"); + + len = stringFormat(buffer, sizeof(buffer), "Float: %.4f", 3.14159); + assert_int_equal(len, 13); + assert_string_equal(buffer, "Float: 3.1416"); + + // Test integer formatting + len = stringFormat(buffer, sizeof(buffer), "Hex: 0x%X", 255); + assert_int_equal(len, 9); + assert_string_equal(buffer, "Hex: 0xFF"); + + // Test empty format + len = stringFormat(buffer, sizeof(buffer), ""); + assert_int_equal(len, 0); + assert_string_equal(buffer, ""); + + // Test no substitutions + len = stringFormat(buffer, sizeof(buffer), "No substitutions"); + assert_int_equal(len, 16); + assert_string_equal(buffer, "No substitutions"); + + // Test buffer too small + expect_assert_failure(stringFormat(buffer, 5, "This is too long")); + expect_assert_failure(stringFormat(buffer, sizeof(buffer), "Hello %s", "This string is way too long to fit within the buffer that we have allocated")); + + // Test NULL destination, simply returns required length + len = stringFormat(NULL, 0, "Hello %s!", "World"); + assert_int_equal(len, 12); + + len = stringFormat(NULL, 0, "Number: %d", 42); + assert_int_equal(len, 10); + + // Cannot have NULL format + expect_assert_failure(stringFormat(buffer, sizeof(buffer), NULL)); + + // Must have destSize > 0 if dest is not NULL + expect_assert_failure(stringFormat(buffer, 0, "Hello")); +} + +static void test_stringToI32(void **state) { + (void)state; + + char_t buffer[64]; + int32_t value; + bool_t result; + + // Standard integers + stringCopy(buffer, "12345", sizeof(buffer)); + assert_true(result = stringToI32(buffer, &value)); + assert_int_equal(value, 12345); + + stringCopy(buffer, "-6789", sizeof(buffer)); + assert_true(result = stringToI32(buffer, &value)); + assert_int_equal(value, -6789); + + stringCopy(buffer, "+123", sizeof(buffer)); + assert_true(result = stringToI32(buffer, &value)); + assert_int_equal(value, 123); + + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, " 123", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "123 ", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, " 1234 ", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "+-123", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "\t123", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "12 34", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "--123", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "++123", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "+", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "-", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + // Non ascii numbers + stringCopy(buffer, "12345", sizeof(buffer)); // Fullwidth digits + assert_false(result = stringToI32(buffer, &value)); + + // Cannot take math + stringCopy(buffer, "100+20", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "50-10", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "5*6", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "20/4", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + // Cannot handle decimal points + stringCopy(buffer, "12.34", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + // Cannot handle non-numbers + stringCopy(buffer, "12-34", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "e", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "3.273390607896142e+150 ", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + stringCopy(buffer, "9999999999999999999999999", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + // Cannot handle really low numbers + stringCopy(buffer, "-9999999999999999999999999", sizeof(buffer)); + assert_false(result = stringToI32(buffer, &value)); + + // Cannot handle items outside int32 range + stringCopy(buffer, "2147483648", sizeof(buffer)); // INT32_MAX + 1 + assert_false(result = stringToI32(buffer, &value)); + stringCopy(buffer, "-2147483649", sizeof(buffer)); // INT32_MIN - 1 + assert_false(result = stringToI32(buffer, &value)); + + // CAN handle INT32 limits + stringCopy(buffer, "2147483647", sizeof(buffer)); // INT32_MAX + assert_true(result = stringToI32(buffer, &value)); + assert_int_equal(value, 2147483647); + stringCopy(buffer, "-2147483648", sizeof(buffer)); // INT32_MIN + assert_true(result = stringToI32(buffer, &value)); + assert_int_equal(value, -2147483648); + + // Cannot have NULL string + expect_assert_failure(stringToI32(NULL, &value)); + + // Cannot have NULL output pointer + stringCopy(buffer, "123", sizeof(buffer)); + expect_assert_failure(stringToI32(buffer, NULL)); +} + +static void test_stringToI64(void **state) { + (void)state; + + char_t buffer[64]; + int64_t value; + bool_t result; + + // Standard integers + stringCopy(buffer, "12345678901234", sizeof(buffer)); + assert_true(result = stringToI64(buffer, &value)); + assert_int_equal(value, 12345678901234LL); + stringCopy(buffer, "-67890123456789", sizeof(buffer)); + assert_true(result = stringToI64(buffer, &value)); + assert_int_equal(value, -67890123456789LL); + + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, " 123", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "123 ", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, " 1234 ", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "+-123", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "\t123", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "12 34", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "--123", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "++123", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "+", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "-", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + // Non ascii numbers + stringCopy(buffer, "12345", sizeof(buffer)); // Fullwidth digits + assert_false(result = stringToI64(buffer, &value)); + + // Cannot take math + stringCopy(buffer, "100+20", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "50-10", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "5*6", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "20/4", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + // Cannot handle decimal points + stringCopy(buffer, "12.34", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + // Cannot handle non-numbers + stringCopy(buffer, "12-34", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "e", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "3.273390607896142e+150 ", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + stringCopy(buffer, "9999999999999999999999999", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + // Cannot handle really low numbers + stringCopy(buffer, "-9999999999999999999999999", sizeof(buffer)); + assert_false(result = stringToI64(buffer, &value)); + + // Cannot handle items outside int64 range + stringCopy(buffer, "9223372036854775808", sizeof(buffer)); // INT64_MAX + 1 + assert_false(result = stringToI64(buffer, &value)); + stringCopy(buffer, "-9223372036854775809", sizeof(buffer)); // INT64_MIN - 1 + assert_false(result = stringToI64(buffer, &value)); + + // CAN handle INT64 limits + stringCopy(buffer, "9223372036854775807", sizeof(buffer)); // INT64_MAX + assert_true(result = stringToI64(buffer, &value)); + assert_int_equal(value, 9223372036854775807LL); + stringCopy(buffer, "-9223372036854775808", sizeof(buffer)); // INT64_MIN + assert_true(result = stringToI64(buffer, &value)); + assert_int_equal(value, -9223372036854775808LL); + + // Cannot have NULL string + expect_assert_failure(stringToI64(NULL, &value)); + + // Cannot have NULL output pointer + stringCopy(buffer, "123", sizeof(buffer)); + expect_assert_failure(stringToI64(buffer, NULL)); +} + +static void test_stringToI16(void **state) { + (void)state; + + char_t buffer[64]; + int16_t value; + bool_t result; + + // Standard integers (within int16 range) + stringCopy(buffer, "12345", sizeof(buffer)); + assert_true(result = stringToI16(buffer, &value)); + assert_int_equal(value, (int16_t)12345); + + stringCopy(buffer, "-23456", sizeof(buffer)); + assert_true(result = stringToI16(buffer, &value)); + assert_int_equal(value, (int16_t)-23456); + + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, " 123", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "123 ", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, " 1234 ", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "+-123", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "\t123", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "12 34", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "--123", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "++123", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "+", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "-", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + // Non-ascii numbers + stringCopy(buffer, "12345", sizeof(buffer)); // Fullwidth digits + assert_false(result = stringToI16(buffer, &value)); + + // Cannot take math + stringCopy(buffer, "100+20", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "50-10", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "5*6", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "20/4", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + // Cannot handle decimal points + stringCopy(buffer, "12.34", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + // Cannot handle non-numbers / mixed formats + stringCopy(buffer, "12-34", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "e", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + stringCopy(buffer, "3.273390607896142e+150 ", sizeof(buffer)); + assert_false(result = stringToI16(buffer, &value)); + + // Out of int16 range (overflow) + stringCopy(buffer, "32768", sizeof(buffer)); // INT16_MAX + 1 + assert_false(result = stringToI16(buffer, &value)); + + // Out of int16 range (underflow) + stringCopy(buffer, "-32769", sizeof(buffer)); // INT16_MIN - 1 + assert_false(result = stringToI16(buffer, &value)); + + // CAN handle int16 limits + stringCopy(buffer, "32767", sizeof(buffer)); // INT16_MAX + assert_true(result = stringToI16(buffer, &value)); + assert_int_equal(value, (int16_t)32767); + + stringCopy(buffer, "-32768", sizeof(buffer)); // INT16_MIN + assert_true(result = stringToI16(buffer, &value)); + assert_int_equal(value, (int16_t)-32768); + + // Cannot have NULL string + expect_assert_failure(stringToI16(NULL, &value)); + + // Cannot have NULL output pointer + stringCopy(buffer, "123", sizeof(buffer)); + expect_assert_failure(stringToI16(buffer, NULL)); +} + +static void test_stringToU16(void **state) { + (void)state; + + char_t buffer[64]; + uint16_t value; + bool_t result; + + // Standard integers (within uint16 range) + stringCopy(buffer, "0", sizeof(buffer)); + assert_true(result = stringToU16(buffer, &value)); + assert_int_equal(value, (uint16_t)0); + + stringCopy(buffer, "12345", sizeof(buffer)); + assert_true(result = stringToU16(buffer, &value)); + assert_int_equal(value, (uint16_t)12345); + + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, " 123", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "123 ", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, " 1234 ", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "+-123", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "\t123", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "12 34", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "--123", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "++123", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "+", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + // Unsigned: no minus sign + stringCopy(buffer, "-", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "-1", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + // Non-ascii numbers + stringCopy(buffer, "12345", sizeof(buffer)); // Fullwidth digits + assert_false(result = stringToU16(buffer, &value)); + + // Cannot take math + stringCopy(buffer, "100+20", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "50-10", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "5*6", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "20/4", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + // Cannot handle decimal points + stringCopy(buffer, "12.34", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + // Cannot handle non-numbers / mixed formats + stringCopy(buffer, "12-34", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "e", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + stringCopy(buffer, "3.273390607896142e+150 ", sizeof(buffer)); + assert_false(result = stringToU16(buffer, &value)); + + // Out of uint16 range (overflow) + stringCopy(buffer, "65536", sizeof(buffer)); // UINT16_MAX + 1 + assert_false(result = stringToU16(buffer, &value)); + + // CAN handle uint16 limits + stringCopy(buffer, "65535", sizeof(buffer)); // UINT16_MAX + assert_true(result = stringToU16(buffer, &value)); + assert_int_equal(value, (uint16_t)65535); + + // Cannot have NULL string + expect_assert_failure(stringToU16(NULL, &value)); + + // Cannot have NULL output pointer + stringCopy(buffer, "123", sizeof(buffer)); + expect_assert_failure(stringToU16(buffer, NULL)); +} + +static void test_stringToF32(void **state) { + (void)state; + + char_t buffer[64]; + float value; + bool_t result; + + // Standard values + stringCopy(buffer, "0", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, 0.0f, 0.000001f); + + stringCopy(buffer, "123.5", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, 123.5f, 0.000001f); + + stringCopy(buffer, "-45.25", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, -45.25f, 0.000001f); + + stringCopy(buffer, "1.0", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, 1.0f, 0.000001f); + + // Integers should also parse + stringCopy(buffer, "42", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, 42.0f, 0.000001f); + + stringCopy(buffer, "-7", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + assert_float_equal(value, -7.0f, 0.000001f); + + // Cannot do European format + stringCopy(buffer, "123,45", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Cannot convert invalid strings + stringCopy(buffer, "abc", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "123abc", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, " ", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, " 1.23", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "1.23 ", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, " 1.23 ", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Invalid sign usage + stringCopy(buffer, "+-1.2", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "--1.2", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "++1.2", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "+", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "-", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Multiple decimal points + stringCopy(buffer, "1.2.3", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Non-ascii digits + stringCopy(buffer, "123.45", sizeof(buffer)); // Fullwidth + assert_false(result = stringToF32(buffer, &value)); + + // Cannot take math + stringCopy(buffer, "1+2", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "5-3", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "2*3", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "6/2", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Scientific notation allowed + stringCopy(buffer, "1e3", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + + stringCopy(buffer, "3.4E10", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + + // Overflow (beyond float32 max) + stringCopy(buffer, "3.5e38", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Underflow (too small, magnitude) + stringCopy(buffer, "1e-50", sizeof(buffer)); + assert_false(result = stringToF32(buffer, &value)); + + // Edge of float32 range (decimal form) + stringCopy(buffer, "340282346638528859811704183484516925440.0", + sizeof(buffer)); // ~FLT_MAX + assert_true(result = stringToF32(buffer, &value)); + + // Valid near-limits (implementation-dependent, keep conservative) + stringCopy(buffer, "3.4028234e38", sizeof(buffer)); + assert_true(result = stringToF32(buffer, &value)); + + // Cannot have NULL string + expect_assert_failure(stringToF32(NULL, &value)); + + // Cannot have NULL output pointer + stringCopy(buffer, "1.23", sizeof(buffer)); + expect_assert_failure(stringToF32(buffer, NULL)); +} + +static void test_stringEndsWith(void **state) { + (void)state; + + assert_true(stringEndsWith("hello.c", ".c")); + assert_true(stringEndsWith("Hello World!", "World!")); + + assert_false(stringEndsWith("document.pdf", ".doc")); + assert_false(stringEndsWith("image.jpeg", ".png")); + assert_false(stringEndsWith("short", "longer")); + + // Cannot have NULL strings + expect_assert_failure(stringEndsWith(NULL, "world")); + expect_assert_failure(stringEndsWith("hello", NULL)); +} + +static void test_stringEndsWithCaseInsensitive(void **state) { + (void)state; + + assert_true(stringEndsWithCaseInsensitive("hello.C", ".c")); + assert_true(stringEndsWithCaseInsensitive("Hello World!", "world!")); + assert_false(stringEndsWithCaseInsensitive("document.pdf", ".DOC")); + assert_false(stringEndsWithCaseInsensitive("image.jpeg", ".PNG")); + assert_false(stringEndsWithCaseInsensitive("short", "LONGER")); + + // Cannot have NULL strings + expect_assert_failure(stringEndsWithCaseInsensitive(NULL, "WORLD")); + expect_assert_failure(stringEndsWithCaseInsensitive("hello", NULL)); +} + +int main(int argc, char **argv) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_stringIsWhitespace), + cmocka_unit_test(test_stringCopy), + cmocka_unit_test(test_stringCompare), + cmocka_unit_test(test_stringCompareInsensitive), + cmocka_unit_test(test_stringTrim), + cmocka_unit_test(test_stringFindLastChar), + cmocka_unit_test(test_stringFormat), + cmocka_unit_test(test_stringToI32), + cmocka_unit_test(test_stringToI64), + cmocka_unit_test(test_stringToI16), + cmocka_unit_test(test_stringToU16), + cmocka_unit_test(test_stringToF32), + cmocka_unit_test(test_stringEndsWith), + cmocka_unit_test(test_stringEndsWithCaseInsensitive), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file