diff --git a/CoDeLib/RaiiString/src/RaiiString.c b/CoDeLib/RaiiString/src/RaiiString.c index f5a6492c..479aee3a 100644 --- a/CoDeLib/RaiiString/src/RaiiString.c +++ b/CoDeLib/RaiiString/src/RaiiString.c @@ -1,21 +1,45 @@ #include #include #include +#include -RaiiString RaiiStringCreate(size_t length) { - RaiiString newRaiistring = {NULL, length}; - newRaiistring.pString = (char *)calloc(length, sizeof(char)); +RaiiString RaiiStringCreate(size_t lengthWithTermination) { + RaiiString newRaiistring = {NULL, 0}; + + if (lengthWithTermination == 0 || + lengthWithTermination >= MAX_CSTRING_INCLUDING_TERMINATION_LENGTH) { + return newRaiistring; + } + + newRaiistring.lengthWithTermination = lengthWithTermination; + newRaiistring.pString = (char *)calloc(lengthWithTermination, sizeof(char)); if (newRaiistring.pString == NULL) { - newRaiistring.length = 0; + newRaiistring.lengthWithTermination = 0; printf("Failed to allocate memory for RaiiString\n"); exit(1); } return newRaiistring; } +RaiiString RaiiStringCreateFromCString(const char *pCString) { + const size_t length = + strnlen(pCString, MAX_CSTRING_INCLUDING_TERMINATION_LENGTH); + RaiiString newRaiistring = RaiiStringCreate(length + 1); + + if (newRaiistring.pString == NULL) { + return newRaiistring; + } + + strncpy(newRaiistring.pString, pCString, + newRaiistring.lengthWithTermination - 1); + newRaiistring.pString[newRaiistring.lengthWithTermination - 1] = '\0'; + return newRaiistring; +} + void RaiiStringClean(RaiiString *pThis) { if (pThis->pString != NULL) { free(pThis->pString); pThis->pString = NULL; } -} + pThis->lengthWithTermination = 0; +} \ No newline at end of file diff --git a/CoDeLib/Test/CMakeLists.txt b/CoDeLib/Test/CMakeLists.txt index 381cf123..57e52e61 100644 --- a/CoDeLib/Test/CMakeLists.txt +++ b/CoDeLib/Test/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(CoDeLib_Test src/main.c + src/TestRaiiString.c src/TestDeflateInflateZlib.c) target_include_directories(CoDeLib_Test PUBLIC diff --git a/CoDeLib/Test/Utility/FileUtils.c b/CoDeLib/Test/Utility/FileUtils.c index cac85768..092f33b3 100644 --- a/CoDeLib/Test/Utility/FileUtils.c +++ b/CoDeLib/Test/Utility/FileUtils.c @@ -10,7 +10,7 @@ void CreateFullPathToFile(const RaiiString *pFullPath, assert(pBasePath != NULL); assert(pFileName != NULL); assert(pFullPath->pString != NULL); - assert(pFullPath->length >= maxFullPathStringSize); + assert(pFullPath->lengthWithTermination >= maxFullPathStringSize); const int charsWritten = snprintf(pFullPath->pString, maxFullPathStringSize, "%s%s", pBasePath, pFileName); diff --git a/CoDeLib/Test/src/TestRaiiString.c b/CoDeLib/Test/src/TestRaiiString.c new file mode 100644 index 00000000..5bb464ee --- /dev/null +++ b/CoDeLib/Test/src/TestRaiiString.c @@ -0,0 +1,175 @@ +#include "unity_fixture.h" +#include +#include +#include + +TEST_GROUP(TestRaiiString); + +static RaiiString raiiString; + +TEST_SETUP(TestRaiiString) {} + +TEST_TEAR_DOWN(TestRaiiString) { RaiiStringClean(&raiiString); } + +//============================== +// RaiiStringCreate() +//============================== + +TEST(TestRaiiString, test_RaiiStringCreate_SetsCorrectsLengthInReturnedObject) { + const size_t length = 10; + raiiString = RaiiStringCreate(length); + TEST_ASSERT_EQUAL(length, raiiString.lengthWithTermination); +} + +TEST(TestRaiiString, test_RaiiStringCreate_SetsNonNullPointerInReturnedObject) { + const size_t length = 10; + raiiString = RaiiStringCreate(length); + TEST_ASSERT_NOT_NULL(raiiString.pString); +} + +TEST(TestRaiiString, + test_RaiiStringCreate_SetsNullptrAndLengthZeroIfGivenLengthIsZero) { + const size_t length = 0; + raiiString = RaiiStringCreate(length); + TEST_ASSERT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +TEST(TestRaiiString, + test_RaiiStringCreate_SetsZeroLengthAndNullptrIfLengthIsMaxLength) { + const size_t length = MAX_CSTRING_INCLUDING_TERMINATION_LENGTH; + raiiString = RaiiStringCreate(length); + TEST_ASSERT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +TEST( + TestRaiiString, + test_RaiiStringCreate_SetsZeroLengthAndNullptrIfLengthIsGreaterThanMaxLength) { + const size_t length = MAX_CSTRING_INCLUDING_TERMINATION_LENGTH + 1; + raiiString = RaiiStringCreate(length); + TEST_ASSERT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +//============================== +// RaiiStringCreateFromCString() +//============================== + +TEST( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsLengthOfOneAndNonNullptrIfProvidedEmptyString) { + const char *pCString = ""; + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_NOT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(1, raiiString.lengthWithTermination); +} + +TEST( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsExecptedLengthAndNonNullptrIfProvidedNormalString) { + const char *pCString = "Hello, world!"; + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_NOT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(14, raiiString.lengthWithTermination); +} + +TEST(TestRaiiString, test_RaiiStringCreateFromCString_SetsExecptedString) { + const char *pCString = "Hello, world!"; + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_EQUAL_STRING(pCString, raiiString.pString); +} + +TEST( + TestRaiiString, + test_RaiiStringCreateFromCString_DoesNotUseProvidedPointerInReturnedObject) { + const char *pCString = ""; + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_NOT_EQUAL(pCString, raiiString.pString); +} + +TEST( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsZeroLengthAndNullptrIfLengthIsMaxLength) { + const size_t lengthWithNullTermination = + MAX_CSTRING_INCLUDING_TERMINATION_LENGTH + 1; + char *pCString = (char *)malloc(lengthWithNullTermination * sizeof(char)); + assert(pCString != NULL && "Failed to allocate memory for pCString"); + memset(pCString, 'a', lengthWithNullTermination - 1); + pCString[MAX_CSTRING_INCLUDING_TERMINATION_LENGTH] = '\0'; + + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +TEST( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsZeroLengthAndNullptrIfLengthIsGreaterThanMaxLength) { + const size_t lengthWithNullTermination = + MAX_CSTRING_INCLUDING_TERMINATION_LENGTH + 2; + char *pCString = (char *)malloc(lengthWithNullTermination * sizeof(char)); + assert(pCString != NULL && "Failed to allocate memory for pCString"); + memset(pCString, 'a', lengthWithNullTermination - 1); + pCString[lengthWithNullTermination - 1] = '\0'; + + raiiString = RaiiStringCreateFromCString(pCString); + TEST_ASSERT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +//============================== +// RaiiStringClean() +//============================== + +TEST(TestRaiiString, test_RaiiStringClean_SetsNullptrInObject) { + raiiString = RaiiStringCreate(10); + RaiiStringClean(&raiiString); + TEST_ASSERT_NULL(raiiString.pString); +} + +TEST(TestRaiiString, test_RaiiStringClean_SetsLengthZeroInObject) { + raiiString = RaiiStringCreate(10); + RaiiStringClean(&raiiString); + TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); +} + +TEST_GROUP_RUNNER(TestRaiiString) { + // RaiiStringCreate() + RUN_TEST_CASE(TestRaiiString, + test_RaiiStringCreate_SetsCorrectsLengthInReturnedObject); + RUN_TEST_CASE(TestRaiiString, + test_RaiiStringCreate_SetsNonNullPointerInReturnedObject); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreate_SetsNullptrAndLengthZeroIfGivenLengthIsZero); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreate_SetsZeroLengthAndNullptrIfLengthIsMaxLength); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreate_SetsZeroLengthAndNullptrIfLengthIsGreaterThanMaxLength); + + // RaiiStringCreateFromCString() + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsLengthOfOneAndNonNullptrIfProvidedEmptyString); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsExecptedLengthAndNonNullptrIfProvidedNormalString); + RUN_TEST_CASE(TestRaiiString, + test_RaiiStringCreateFromCString_SetsExecptedString); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreateFromCString_DoesNotUseProvidedPointerInReturnedObject); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsZeroLengthAndNullptrIfLengthIsMaxLength); + RUN_TEST_CASE( + TestRaiiString, + test_RaiiStringCreateFromCString_SetsZeroLengthAndNullptrIfLengthIsGreaterThanMaxLength); + + // RaiiStringClean() + RUN_TEST_CASE(TestRaiiString, test_RaiiStringClean_SetsNullptrInObject); + RUN_TEST_CASE(TestRaiiString, test_RaiiStringClean_SetsLengthZeroInObject); +} \ No newline at end of file diff --git a/CoDeLib/Test/src/main.c b/CoDeLib/Test/src/main.c index de9603cc..9e28b017 100644 --- a/CoDeLib/Test/src/main.c +++ b/CoDeLib/Test/src/main.c @@ -3,7 +3,10 @@ #include "TestDeflateInflateZlib.h" #include -static void RunAllTests(void) { RUN_TEST_GROUP(TestDeflateInflateZlib); } +static void RunAllTests(void) { + RUN_TEST_GROUP(TestRaiiString); + RUN_TEST_GROUP(TestDeflateInflateZlib); +} int main(int argc, const char **argv) { char *pFullPathToBenchmarkTestFiles; diff --git a/CoDeLib/include/CoDeLib/RaiiString/RaiiString.h b/CoDeLib/include/CoDeLib/RaiiString/RaiiString.h index ec348338..d738362d 100644 --- a/CoDeLib/include/CoDeLib/RaiiString/RaiiString.h +++ b/CoDeLib/include/CoDeLib/RaiiString/RaiiString.h @@ -11,8 +11,14 @@ typedef struct { char *pString; - size_t length; + + // Length of the string (including the null terminator) + size_t lengthWithTermination; } RaiiString; +// Maximum length of a C string (including the null terminator) +static const size_t MAX_CSTRING_INCLUDING_TERMINATION_LENGTH = 1024; + RaiiString RaiiStringCreate(size_t length); +RaiiString RaiiStringCreateFromCString(const char *pCString); void RaiiStringClean(RaiiString *pThis); \ No newline at end of file