diff --git a/.github/workflows/build-test-run-reusable-workflow.yaml b/.github/workflows/build-test-run-reusable-workflow.yaml index fc3b7eaf..7e2cdac5 100644 --- a/.github/workflows/build-test-run-reusable-workflow.yaml +++ b/.github/workflows/build-test-run-reusable-workflow.yaml @@ -51,7 +51,7 @@ jobs: runs-on: ${{ inputs.runner }} name : Test (Target ${{ inputs.target-platform }} - Host ${{ inputs.host-platform }}) needs: build - timeout-minutes: 15 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -62,7 +62,7 @@ jobs: name: CoDeLibBuildArtifacts-target-${{ inputs.target-platform }}-host-${{ inputs.host-platform }} path: CoDeLib/Build - run: | - python -u Scripts/RunTest.py + python -u Scripts/RunTest.py --RunLongTests - name: 'Upload test results' uses: actions/upload-artifact@v4 with: diff --git a/.gitmodules b/.gitmodules index 92c1e067..4ae0ef48 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/madler/zlib.git [submodule "External/minizip-ng"] path = External/minizip-ng - url = https://github.com/zlib-ng/minizip-ng + url = https://github.com/enzoevers/minizip-ng diff --git a/CoDeLib/CMakeLists.txt b/CoDeLib/CMakeLists.txt index 762b20b0..aa5607e3 100644 --- a/CoDeLib/CMakeLists.txt +++ b/CoDeLib/CMakeLists.txt @@ -1,6 +1,15 @@ cmake_minimum_required(VERSION 3.28) project(COMPRESSION_DECOMPRESSION_LIB VERSION 0.0.1 LANGUAGES C) +add_compile_definitions("$<$>:NDEBUG>") + +# Required for FileUtils.c (and its tests) to use the correct versions of ftello and similar functions +add_compile_definitions(_FILE_OFFSET_BITS=64) +# The follwing may be interesting in the future +# add_compile_definitions(__USE_LARGEFILE64) +# add_compile_definitions(_LARGEFILE_SOURCE) +# add_compile_definitions(_LARGEFILE64_SOURCE) + include(FetchContent) include(GNUInstallDirs) @@ -20,6 +29,7 @@ set(COMMON_HEADERS ${CoDeLib_PUBLIC_INCLUDE_PATH}/IDeflate.h ${CoDeLib_PUBLIC_INCLUDE_PATH}/IInflate.h ${CoDeLib_PUBLIC_INCLUDE_PATH}/IUnZip.h + ${CoDeLib_PUBLIC_INCLUDE_PATH}/IZip.h ) install(FILES ${COMMON_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CoDeLib) @@ -27,12 +37,13 @@ add_subdirectory(Deflate_zlib) add_subdirectory(Inflate_zlib) add_subdirectory(FileUtils) add_subdirectory(UnZip_minizip) +add_subdirectory(Zip_minizip) add_subdirectory(RaiiString) add_subdirectory(ZipContentInfo) add_subdirectory(Test) install( - TARGETS CoDeLib Deflate_zlib Inflate_zlib UnZip_minizip RaiiString ZipContentInfo FileUtils + TARGETS CoDeLib Deflate_zlib Inflate_zlib UnZip_minizip Zip_minizip RaiiString ZipContentInfo FileUtils EXPORT CoDeLibTargets INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/CoDeLib/CustomDeflate.md b/CoDeLib/CustomDeflate.md index ab327d3b..1a8b5534 100644 --- a/CoDeLib/CustomDeflate.md +++ b/CoDeLib/CustomDeflate.md @@ -5,4 +5,8 @@ # refs - https://pnrsolution.org/Datacenter/Vol4/Issue1/58.pdf -- https://nachtimwald.com/2019/09/08/making-minizip-easier-to-use/ \ No newline at end of file +- https://nachtimwald.com/2019/09/08/making-minizip-easier-to-use/ +- https://pkwaredownloads.blob.core.windows.net/pkware-general/Documentation/APPNOTE-6.3.9.TXT +- https://github.com/zlib-ng/minizip-ng/blob/cf5404bb714ee11ca2fdc3168d9770a11ec8b576/test/fuzz/zip_fuzzer.c#L100 +- https://stackoverflow.com/questions/12609747/traversing-a-filesystem-with-fts3 +- https://man7.org/linux/man-pages/man3/alloca.3.html \ No newline at end of file diff --git a/CoDeLib/FileUtils/src/FileUtils.c b/CoDeLib/FileUtils/src/FileUtils.c index ba12ac81..2d0b4b23 100644 --- a/CoDeLib/FileUtils/src/FileUtils.c +++ b/CoDeLib/FileUtils/src/FileUtils.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -278,7 +277,7 @@ size_t ExtractLastPartOfPath(const char *const pPath, char *const pDestBuffer, return SIZE_MAX; } - const size_t pathLenght = strnlen(pPath, MAX_PATH_LENGTH_WTH_TERMINATOR); + size_t pathLenght = strnlen(pPath, MAX_PATH_LENGTH_WTH_TERMINATOR); if (pathLenght == 0 || pathLenght == MAX_PATH_LENGTH_WTH_TERMINATOR) { return SIZE_MAX; } @@ -287,11 +286,25 @@ size_t ExtractLastPartOfPath(const char *const pPath, char *const pDestBuffer, return SIZE_MAX; } + char lastChar = pPath[pathLenght - 1]; + bool isDir = lastChar == '/' || lastChar == '\\'; + + if (isDir) { + // Ignore empty sections of the path + for (size_t i = pathLenght - 2; i > 0; --i) { + if (pPath[i] == '/' || pPath[i] == '\\') { + // Assigning again because this may be different from the last + lastChar = pPath[i]; + pathLenght--; + } else { + break; + } + } + } + char *pLocalPath = (char *)calloc(pathLenght + 1, sizeof(char)); memcpy(pLocalPath, pPath, pathLenght + 1); - - const char lastChar = pLocalPath[pathLenght - 1]; - bool isDir = lastChar == '/' || lastChar == '\\'; + pLocalPath[pathLenght] = '\0'; if (isDir) { // Remove the last separator to make getting the file or directory name @@ -341,7 +354,8 @@ size_t ExtractLastPartOfPath(const char *const pPath, char *const pDestBuffer, return startIndexOfNameInPath; } -void OpenFileWithMode(FILE **pInFile, RaiiString *pFullPath, char *pOpenMode) { +void OpenFileWithMode(FILE **pInFile, const RaiiString *const pFullPath, + const char *const pOpenMode) { *pInFile = fopen(pFullPath->pString, pOpenMode); if (*pInFile == NULL) { printf("Failed to open file: %s\n", pFullPath->pString); @@ -349,21 +363,31 @@ void OpenFileWithMode(FILE **pInFile, RaiiString *pFullPath, char *pOpenMode) { } } -size_t GetFileSizeInBytes(FILE *pFile) { - fseek(pFile, 0, SEEK_END); - const size_t fileSize = ftell(pFile); +uint64_t GetFileSizeInBytes(FILE *pFile) { + if (pFile == NULL) { + return 0; + } + + fseeko(pFile, 0, SEEK_END); + const off_t fileSize = ftello(pFile); rewind(pFile); + + if (fileSize == -1) { + printf("Failed to get file size: %s\n", strerror(errno)); + return 0; + } + return fileSize; } bool FilesAreEqual(FILE *pFile1, FILE *pFile2) { - const size_t fileSize1 = GetFileSizeInBytes(pFile1); - const size_t fileSize2 = GetFileSizeInBytes(pFile2); + const uint64_t fileSize1 = GetFileSizeInBytes(pFile1); + const uint64_t fileSize2 = GetFileSizeInBytes(pFile2); if (fileSize1 != fileSize2) { printf("File sizes not equal:\n"); - printf(" fileSize1: %lu\n", (unsigned long)fileSize1); - printf(" fileSize2: %lu\n", (unsigned long)fileSize2); + printf(" fileSize1: %llu\n", (unsigned long long)fileSize1); + printf(" fileSize2: %llu\n", (unsigned long long)fileSize2); return false; } diff --git a/CoDeLib/RaiiString/src/RaiiString.c b/CoDeLib/RaiiString/src/RaiiString.c index 6a412de3..8b214d73 100644 --- a/CoDeLib/RaiiString/src/RaiiString.c +++ b/CoDeLib/RaiiString/src/RaiiString.c @@ -9,8 +9,7 @@ RaiiString RaiiStringCreateFromCString(const char *pCString) { RaiiString newRaiistring = {NULL, 0}; - if (lengthWithoutTermination == 0 || - lengthWithoutTermination == MAX_CSTRING_INCLUDING_TERMINATION_LENGTH) { + if (lengthWithoutTermination == MAX_CSTRING_INCLUDING_TERMINATION_LENGTH) { return newRaiistring; } diff --git a/CoDeLib/Test/CMakeLists.txt b/CoDeLib/Test/CMakeLists.txt index 8b78df59..543a3a24 100644 --- a/CoDeLib/Test/CMakeLists.txt +++ b/CoDeLib/Test/CMakeLists.txt @@ -4,7 +4,9 @@ add_executable(CoDeLib_Test src/TestDeflateInflateZlib.c src/TestFileUtils.c src/TestUnZipMinizip.c + src/TestZipMinizip.c src/TestUnZipMinizipInflateZlib.c + src/TestZipMinizipUnZipMinizip.c src/TestZipContentInfo.c ) @@ -27,6 +29,7 @@ target_link_libraries(CoDeLib_Test PRIVATE Deflate_zlib) target_link_libraries(CoDeLib_Test PRIVATE Inflate_zlib) target_link_libraries(CoDeLib_Test PRIVATE FileUtils) target_link_libraries(CoDeLib_Test PRIVATE UnZip_minizip) +target_link_libraries(CoDeLib_Test PRIVATE Zip_minizip) target_link_libraries(CoDeLib_Test PRIVATE RaiiString) target_link_libraries(CoDeLib_Test PRIVATE ZipContentInfo) diff --git a/CoDeLib/Test/src/TestFileUtils.c b/CoDeLib/Test/src/TestFileUtils.c index 2d9a0ff1..11ce3ba7 100644 --- a/CoDeLib/Test/src/TestFileUtils.c +++ b/CoDeLib/Test/src/TestFileUtils.c @@ -13,19 +13,27 @@ static char *g_pFullPathToBenchmarkTestFiles = NULL; static char *g_pCurrentWorkingDirectory = NULL; +static bool g_runLongTests = false; void SetupTestFileUtils(char *pFullPathToBenchmarkTestFiles, - char *pCurrentWorkingDirectory) { + char *pCurrentWorkingDirectory, bool runLongTests) { g_pFullPathToBenchmarkTestFiles = pFullPathToBenchmarkTestFiles; g_pCurrentWorkingDirectory = pCurrentWorkingDirectory; + g_runLongTests = runLongTests; } TEST_GROUP(TestFileUtils); -TEST_SETUP(TestFileUtils) {} +TEST_SETUP(TestFileUtils) { + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } +} TEST_TEAR_DOWN(TestFileUtils) { - // TODO: Remove all created directories during tests + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } } //============================== @@ -918,6 +926,279 @@ TEST( TEST_ASSERT_EQUAL_STRING("someDir/", lastPartBuffer); } +TEST(TestFileUtils, + test_ExtractLastPartOfPath_IgnoresEmptyEntriesInPath_ForwardSlashes) { + const char *pPath = "./tmp/someDir///"; + char lastPartBuffer[MAX_PATH_LENGTH_WTH_TERMINATOR]; + size_t indexInPath = ExtractLastPartOfPath(pPath, &lastPartBuffer[0], + MAX_PATH_LENGTH_WTH_TERMINATOR); + + TEST_ASSERT_EQUAL_size_t(6, indexInPath); + TEST_ASSERT_EQUAL_STRING("someDir/", lastPartBuffer); +} + +TEST(TestFileUtils, + test_ExtractLastPartOfPath_IgnoresEmptyEntriesInPath_BackwardSlashes) { + const char *pPath = ".\\tmp\\someDir\\\\\\"; + char lastPartBuffer[MAX_PATH_LENGTH_WTH_TERMINATOR]; + size_t indexInPath = ExtractLastPartOfPath(pPath, &lastPartBuffer[0], + MAX_PATH_LENGTH_WTH_TERMINATOR); + + TEST_ASSERT_EQUAL_size_t(6, indexInPath); + TEST_ASSERT_EQUAL_STRING("someDir\\", lastPartBuffer); +} + +//============================== +// GetFileSizeInBytes(...) +//============================== + +TEST(TestFileUtils, test_GetFileSizeInBytes_ReturnsZeroIfPathIsNull) { + const uint64_t fileSize = GetFileSizeInBytes(NULL); + + // For some reason, when building for Zynq, there is a warning that fileSize + // is not used when using `TEST_ASSERT_EQUAL_UINT64` + if (fileSize != 0) { + printf("Expected (sizeToCreate): %llu\n", (unsigned long long)0); + printf("Actual (fileSize): %llu\n", (unsigned long long)fileSize); + TEST_FAIL_MESSAGE("File size does not match expected size."); + } +} + +TEST(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_SmallFile) { + + //----- Genereate 100KB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "dummy.txt"; + RAII_STRING generatedFilePath = + RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&generatedFilePath, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFileGenerate = NULL; + OpenFileWithMode(&pFileGenerate, &generatedFilePath, "wb"); + + const uint64_t sizeToCreate = 100000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < sizeToCreate; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFileGenerate); + } + if (bufSizeWritten != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFileGenerate); + + //----- Test ----- + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &generatedFilePath, "rb"); + + const uint64_t fileSize = GetFileSizeInBytes(pFile); + fclose(pFile); + + if (fileSize != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (fileSize): %llu\n", (unsigned long long)fileSize); + TEST_FAIL_MESSAGE("File size does not match expected size."); + } +} + +TEST(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_MediumFile) { + + if (!g_runLongTests) { + TEST_IGNORE_MESSAGE("Not running because g_runLongTests is false"); + } + + //----- Genereate 1GB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "dummy.txt"; + RAII_STRING generatedFilePath = + RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&generatedFilePath, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFileGenerate = NULL; + OpenFileWithMode(&pFileGenerate, &generatedFilePath, "wb"); + + const uint64_t sizeToCreate = 1000000000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < sizeToCreate; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFileGenerate); + } + if (bufSizeWritten != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFileGenerate); + + //----- Test ----- + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &generatedFilePath, "rb"); + + const uint64_t fileSize = GetFileSizeInBytes(pFile); + fclose(pFile); + + if (fileSize != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (fileSize): %llu\n", (unsigned long long)fileSize); + TEST_FAIL_MESSAGE("File size does not match expected size."); + } +} + +TEST(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_LargeFile) { + + if (!g_runLongTests) { + TEST_IGNORE_MESSAGE("Not running because g_runLongTests is false"); + } + + //----- Genereate 6GB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "dummy.txt"; + RAII_STRING generatedFilePath = + RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&generatedFilePath, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFileGenerate = NULL; + OpenFileWithMode(&pFileGenerate, &generatedFilePath, "wb"); + + const uint64_t sizeToCreate = 6000000000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < sizeToCreate; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFileGenerate); + } + if (bufSizeWritten != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFileGenerate); + + //----- Test ----- + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &generatedFilePath, "rb"); + + const uint64_t fileSize = GetFileSizeInBytes(pFile); + fclose(pFile); + + if (fileSize != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (fileSize): %llu\n", (unsigned long long)fileSize); + TEST_FAIL_MESSAGE("File size does not match expected size."); + } +} + +TEST(TestFileUtils, + test_GetFileSizeInBytes_DoesNotChangeCurrentPositionInFile) { + + //----- Genereate 100KB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "dummy.txt"; + RAII_STRING generatedFilePath = + RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&generatedFilePath, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFileGenerate = NULL; + OpenFileWithMode(&pFileGenerate, &generatedFilePath, "wb"); + + const uint64_t sizeToCreate = 100000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < sizeToCreate; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFileGenerate); + } + if (bufSizeWritten != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFileGenerate); + + //----- Test ----- + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &generatedFilePath, "rb"); + + const uint64_t offset = 10; + TEST_ASSERT_EQUAL(0, fseeko(pFile, offset, SEEK_SET)); + const off_t currentPosBefore = ftello(pFile); + if (currentPosBefore == -1) { + fclose(pFile); + TEST_FAIL_MESSAGE("Failed to get current position in file."); + } + TEST_ASSERT_EQUAL_UINT64(offset, currentPosBefore); + + const uint64_t fileSize = GetFileSizeInBytes(pFile); + const off_t currentPos = ftello(pFile); + if (currentPos == -1) { + fclose(pFile); + TEST_FAIL_MESSAGE("Failed to get current position in file."); + } + fclose(pFile); + + if (fileSize != sizeToCreate) { + printf("Expected (sizeToCreate): %llu\n", + (unsigned long long)sizeToCreate); + printf("Actual (fileSize): %llu\n", (unsigned long long)fileSize); + TEST_FAIL_MESSAGE("File size does not match expected size."); + } + TEST_ASSERT_EQUAL_UINT64(offset, currentPos); +} + //============================== // TEST_GROUP_RUNNER //============================== @@ -1128,4 +1409,20 @@ TEST_GROUP_RUNNER(TestFileUtils) { RUN_TEST_CASE( TestFileUtils, test_ExtractLastPartOfPath_ReturnsCorrectStringAndIndexWhenNoBasePath_Directory); + RUN_TEST_CASE( + TestFileUtils, + test_ExtractLastPartOfPath_IgnoresEmptyEntriesInPath_ForwardSlashes); + RUN_TEST_CASE( + TestFileUtils, + test_ExtractLastPartOfPath_IgnoresEmptyEntriesInPath_BackwardSlashes); + + // GetFileSizeInBytes(...) + RUN_TEST_CASE(TestFileUtils, + test_GetFileSizeInBytes_ReturnsZeroIfPathIsNull); + RUN_TEST_CASE(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_SmallFile); + RUN_TEST_CASE(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_MediumFile); + RUN_TEST_CASE(TestFileUtils, + test_GetFileSizeInBytes_ReturnsCorrectSizeOfFile_LargeFile); } diff --git a/CoDeLib/Test/src/TestFileUtils.h b/CoDeLib/Test/src/TestFileUtils.h index c179ad84..2f923855 100644 --- a/CoDeLib/Test/src/TestFileUtils.h +++ b/CoDeLib/Test/src/TestFileUtils.h @@ -1,4 +1,6 @@ #pragma once +#include + void SetupTestFileUtils(char *pFullPathToBenchmarkTestFiles, - char *pCurrentWorkingDirectory); + char *pCurrentWorkingDirectory, bool runLongTests); diff --git a/CoDeLib/Test/src/TestRaiiString.c b/CoDeLib/Test/src/TestRaiiString.c index 6f97fb47..96ae7551 100644 --- a/CoDeLib/Test/src/TestRaiiString.c +++ b/CoDeLib/Test/src/TestRaiiString.c @@ -93,11 +93,11 @@ TEST( TEST( TestRaiiString, - test_RaiiStringCreateFromCString_SetsLengthOfZeroAndNullptrIfProvidedEmptyString) { + test_RaiiStringCreateFromCString_SetsExpectedLengthAndPtrIfProvidedEmptyString) { const char *pCString = ""; raiiString = RaiiStringCreateFromCString(pCString); - TEST_ASSERT_NULL(raiiString.pString); - TEST_ASSERT_EQUAL(0, raiiString.lengthWithTermination); + TEST_ASSERT_NOT_NULL(raiiString.pString); + TEST_ASSERT_EQUAL(1, raiiString.lengthWithTermination); } //============================== @@ -297,7 +297,7 @@ TEST_GROUP_RUNNER(TestRaiiString) { test_RaiiStringCreateFromCString_SetsZeroLengthAndNullptrIfStringLengthIsGreaterThanMaxTotalLength); RUN_TEST_CASE( TestRaiiString, - test_RaiiStringCreateFromCString_SetsLengthOfZeroAndNullptrIfProvidedEmptyString); + test_RaiiStringCreateFromCString_SetsExpectedLengthAndPtrIfProvidedEmptyString); // RaiiStringClean() RUN_TEST_CASE(TestRaiiString, diff --git a/CoDeLib/Test/src/TestUnZipMinizip.c b/CoDeLib/Test/src/TestUnZipMinizip.c index 2e034631..e43860df 100644 --- a/CoDeLib/Test/src/TestUnZipMinizip.c +++ b/CoDeLib/Test/src/TestUnZipMinizip.c @@ -36,6 +36,10 @@ static RaiiString g_pathToMultiTextFileAndSubDirZipStore; static RaiiString g_pathToMultiTextFileAndSubDirZipSource; TEST_SETUP(TestUnZipMinizip) { + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } + g_someUnZippedDirPath = RaiiStringCreateFromCString("SomePath/someUnZippedDirPath.zip"); g_someZipPath = RaiiStringCreateFromCString("SomePath/someZipPath.zip"); diff --git a/CoDeLib/Test/src/TestUnZipMinizipInflateZlib.c b/CoDeLib/Test/src/TestUnZipMinizipInflateZlib.c index 3d71af4b..276e3178 100644 --- a/CoDeLib/Test/src/TestUnZipMinizipInflateZlib.c +++ b/CoDeLib/Test/src/TestUnZipMinizipInflateZlib.c @@ -27,6 +27,10 @@ static RaiiString g_pathToMultiTextFileAndSubDirZipDeflate; static RaiiString g_pathToMultiTextFileAndSubDirZipSource; TEST_SETUP(TestUnZipMinizipInflateZlib) { + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } + g_someUnZippedDirPath = RaiiStringCreateFromCString("SomePath/someUnZippedDirPath.zip"); g_someZipPath = RaiiStringCreateFromCString("SomePath/someZipPath.zip"); diff --git a/CoDeLib/Test/src/TestZipMinizip.c b/CoDeLib/Test/src/TestZipMinizip.c new file mode 100644 index 00000000..e185f0f1 --- /dev/null +++ b/CoDeLib/Test/src/TestZipMinizip.c @@ -0,0 +1,180 @@ +#include "TestZipMinizip.h" +#include "unity_fixture.h" +#include +#include +#include +#include +#include +#include +#include + +static char *g_pFullPathToBenchmarkTestFiles; + +void SetupTestZipMinizip(char *pFullPathToBenchmarkTestFiles) { + g_pFullPathToBenchmarkTestFiles = pFullPathToBenchmarkTestFiles; +} + +TEST_GROUP(TestZipMinizip); + +static RaiiString g_pathToSmallBasicTextFileZipDeflate; +static RaiiString g_pathToSmallBasicTextFileZipSource; +static RaiiString g_pathToMultiTextFileZipSource; +static RaiiString g_pathToMultiTextFileAndSubDirZipSource; + +TEST_SETUP(TestZipMinizip) { + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } + + g_pathToSmallBasicTextFileZipDeflate = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToSmallBasicTextFileZipDeflate, + "/SmallBasicTextFileZip/" + "SmallBasicTextFile_deflate.zip"); + + g_pathToSmallBasicTextFileZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToSmallBasicTextFileZipSource, + "/SmallBasicTextFileZip/"); + + g_pathToMultiTextFileZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToMultiTextFileZipSource, + "/MultiTextFileZip/"); + + g_pathToMultiTextFileAndSubDirZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToMultiTextFileAndSubDirZipSource, + "/MultiTextFileAndSubDirZip/"); +} + +TEST_TEAR_DOWN(TestZipMinizip) { + RaiiStringClean(&g_pathToSmallBasicTextFileZipDeflate); + RaiiStringClean(&g_pathToSmallBasicTextFileZipSource); + RaiiStringClean(&g_pathToMultiTextFileZipSource); + RaiiStringClean(&g_pathToMultiTextFileAndSubDirZipSource); + + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } +} + +//============================== +// Zip() +//============================== + +TEST(TestZipMinizip, test_Zip_ReturnsErrorIfOutputZipPathIsNullptr) { + const char *pInputPathArray[] = { + "123", + "asdf", + }; + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(NULL, &pInputPathArray[0], 2); + TEST_ASSERT_EQUAL(ZIP_ERROR, statusZip); +} + +TEST(TestZipMinizip, test_Zip_ReturnsErrorIfInputPathArrayIsNullptr) { + const char dummyString[] = "123"; + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(&dummyString[0], NULL, 2); + TEST_ASSERT_EQUAL(ZIP_ERROR, statusZip); +} + +TEST(TestZipMinizip, test_Zip_ReturnsErrorIfProvidedSizeIsZero) { + const char dummyString[] = "123"; + const char *pInputPathArray[] = { + "123", + "asdf", + }; + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(&dummyString[0], &pInputPathArray[0], 0); + TEST_ASSERT_EQUAL(ZIP_ERROR, statusZip); +} + +TEST(TestZipMinizip, test_Zip_ReturnsErrorIfZipFileAlreadyExists) { + RAII_STRING dummyZipPath = RaiiStringCreateFromCString("./tmp/"); + RecursiveMkdir(dummyZipPath.pString); + + RaiiStringAppend_cString(&dummyZipPath, "SomeZip.zip"); + FILE *pDummyFile = fopen(dummyZipPath.pString, "w"); + TEST_ASSERT_NOT_NULL(pDummyFile); + fclose(pDummyFile); + + const char *pInputPathArray[] = { + "123", + }; + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(dummyZipPath.pString, &pInputPathArray[0], 1); + TEST_ASSERT_EQUAL(ZIP_ERROR, statusZip); +} + +TEST(TestZipMinizip, + test_Zip_CreatesZipFileAfterZippingInCorrectPathProvidingRelativePath) { + RAII_STRING inputTextFilePath = RaiiStringCreateFromCString( + g_pathToSmallBasicTextFileZipSource.pString); + RaiiStringAppend_cString(&inputTextFilePath, "SmallBasicTextFile.txt"); + + const char *pInputPathArray[] = { + inputTextFilePath.pString, + }; + + RAII_STRING outputZipPath = + RaiiStringCreateFromCString("./tmp/SmallBasicTextFile.zip"); + + TEST_ASSERT_FALSE(PathExists(outputZipPath.pString)); + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(outputZipPath.pString, &pInputPathArray[0], 1); + TEST_ASSERT_EQUAL(ZIP_SUCCESS, statusZip); + + TEST_ASSERT_TRUE(PathExists(outputZipPath.pString)); +} + +TEST(TestZipMinizip, + test_Zip_CreatesZipFileAfterZippingInCorrectPathProvidingAbsolutePath) { + RAII_STRING inputTextFilePath = RaiiStringCreateFromCString( + g_pathToSmallBasicTextFileZipSource.pString); + RaiiStringAppend_cString(&inputTextFilePath, "SmallBasicTextFile.txt"); + + const char *pInputPathArray[] = { + inputTextFilePath.pString, + }; + + char cwdBuffer[MAX_PATH_LENGTH_WTH_TERMINATOR]; + GetCurrentWorkingDirectory(&cwdBuffer[0], MAX_PATH_LENGTH_WTH_TERMINATOR); + + RAII_STRING outputZipPath = RaiiStringCreateFromCString(&cwdBuffer[0]); + RaiiStringAppend_cString(&outputZipPath, "tmp/SmallBasicTextFile.zip"); + + TEST_ASSERT_FALSE(PathExists(outputZipPath.pString)); + + const ZIP_RETURN_CODES statusZip = + zip_minizip.Zip(outputZipPath.pString, &pInputPathArray[0], 1); + TEST_ASSERT_EQUAL(ZIP_SUCCESS, statusZip); + + TEST_ASSERT_TRUE(PathExists(outputZipPath.pString)); +} + +//============================== +// TEST_GROUP_RUNNER +//============================== + +TEST_GROUP_RUNNER(TestZipMinizip) { + // Zip() + RUN_TEST_CASE(TestZipMinizip, + test_Zip_ReturnsErrorIfOutputZipPathIsNullptr); + RUN_TEST_CASE(TestZipMinizip, + test_Zip_ReturnsErrorIfInputPathArrayIsNullptr); + RUN_TEST_CASE(TestZipMinizip, test_Zip_ReturnsErrorIfProvidedSizeIsZero); + RUN_TEST_CASE(TestZipMinizip, test_Zip_ReturnsErrorIfZipFileAlreadyExists); + RUN_TEST_CASE( + TestZipMinizip, + test_Zip_CreatesZipFileAfterZippingInCorrectPathProvidingRelativePath); + RUN_TEST_CASE( + TestZipMinizip, + test_Zip_CreatesZipFileAfterZippingInCorrectPathProvidingAbsolutePath); +} diff --git a/CoDeLib/Test/src/TestZipMinizip.h b/CoDeLib/Test/src/TestZipMinizip.h new file mode 100644 index 00000000..30abb2fb --- /dev/null +++ b/CoDeLib/Test/src/TestZipMinizip.h @@ -0,0 +1,3 @@ +#pragma once + +void SetupTestZipMinizip(char *pFullPathToBenchmarkTestFiles); diff --git a/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.c b/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.c new file mode 100644 index 00000000..616b26f3 --- /dev/null +++ b/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.c @@ -0,0 +1,815 @@ +#include "TestZipMinizipUnZipMinizip.h" +#include "unity_fixture.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static char *g_pFullPathToBenchmarkTestFiles; +static bool g_runLongTests = false; + +void SetupTestZipMinizipUnZipMinizip(char *pFullPathToBenchmarkTestFiles, + bool runLongTests) { + g_pFullPathToBenchmarkTestFiles = pFullPathToBenchmarkTestFiles; + g_runLongTests = runLongTests; +} + +TEST_GROUP(TestZipMinizipUnZipMinizip); + +static RaiiString g_pathToSmallBasicTextFileZipSource; +static RaiiString g_pathToMultiTextFileZipSource; +static RaiiString g_pathToMultiTextFileAndSubDirZipSource; + +TEST_SETUP(TestZipMinizipUnZipMinizip) { + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } + + g_pathToSmallBasicTextFileZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToSmallBasicTextFileZipSource, + "/SmallBasicTextFileZip/"); + + g_pathToMultiTextFileZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToMultiTextFileZipSource, + "/MultiTextFileZip/"); + + g_pathToMultiTextFileAndSubDirZipSource = + RaiiStringCreateFromCString(g_pFullPathToBenchmarkTestFiles); + RaiiStringAppend_cString(&g_pathToMultiTextFileAndSubDirZipSource, + "/MultiTextFileAndSubDirZip/"); +} + +TEST_TEAR_DOWN(TestZipMinizipUnZipMinizip) { + RaiiStringClean(&g_pathToSmallBasicTextFileZipSource); + RaiiStringClean(&g_pathToMultiTextFileZipSource); + RaiiStringClean(&g_pathToMultiTextFileAndSubDirZipSource); + + if (PathExists("./tmp/")) { + TEST_ASSERT_TRUE(RecursiveRmdir("./tmp/")); + } +} + +void TestZipMinizipUnZipMinizip_Store( + const RaiiString *const inputPathArray, const size_t inputPathArraySize, + const RaiiString *const sourceFilePathArrayToCompare, + const size_t sourceFilePathArrayToCompareSize, + const RaiiString *const unZippedFilePathArrayToCompare, + const size_t unZippedFilePathArrayToCompareSize, + const RaiiString *const pZippedResultPath, + const RaiiString *const pUnZippedResultPath) { + + TEST_ASSERT_EQUAL(sourceFilePathArrayToCompareSize, + unZippedFilePathArrayToCompareSize); + //---------- + // Generic setup + //---------- + + const char *charInputPathArray[inputPathArraySize]; + for (size_t i = 0; i < inputPathArraySize; ++i) { + charInputPathArray[i] = inputPathArray[i].pString; + } + + //---------- + // Zipping + //---------- + + printf("Zipping\n"); + const ZIP_RETURN_CODES statusZip = zip_minizip.Zip( + pZippedResultPath->pString, &charInputPathArray[0], inputPathArraySize); + + TEST_ASSERT_EQUAL(ZIP_SUCCESS, statusZip); + + //---------- + // Un-zipping + //---------- + + printf("Unzipping\n"); + RAII_ZIPCONTENTINFO zipInfo = ZipContentInfoCreate(pZippedResultPath); + + const UNZIP_RETURN_CODES statusUnZip = + unzip_minizip.UnZip(&zipInfo, pUnZippedResultPath); + + TEST_ASSERT_EQUAL(UNZIP_SUCCESS, statusUnZip); + + //---------- + // Check if files are identical + //---------- + + printf("Comparing files\n"); + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + FILE *pFileSource = NULL; + FILE *pFileUnzipped = NULL; + + OpenFileWithMode(&pFileSource, &sourceFilePathArrayToCompare[i], "rb"); + OpenFileWithMode(&pFileUnzipped, &unZippedFilePathArrayToCompare[i], + "rb"); + + TEST_ASSERT_TRUE(FilesAreEqual(pFileSource, pFileUnzipped)); + + fclose(pFileSource); + fclose(pFileUnzipped); + } + + //---------- + // Clean up + //---------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + charInputPathArray[i] = NULL; + } +} + +//============================== +// Zip() + UnZip() +//============================== + +TEST(TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_SingleFile) { + + //-------------------- + // Setup + //-------------------- + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 1; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(g_pathToSmallBasicTextFileZipSource.pString); + } + + RaiiStringAppend_cString(&inputPathArray[0], "SmallBasicTextFile.txt"); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 1; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(g_pathToSmallBasicTextFileZipSource.pString); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], "SmallBasicTextFile.txt"); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], "SmallBasicTextFile.txt"); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST(TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFile) { + + //-------------------- + // Setup + //-------------------- + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 3; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(g_pathToMultiTextFileZipSource.pString); + } + + RaiiStringAppend_cString(&inputPathArray[0], "TextFileOne.txt"); + RaiiStringAppend_cString(&inputPathArray[1], "TextFileTwo.txt"); + RaiiStringAppend_cString(&inputPathArray[2], "ThirdTextFile.txt"); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 3; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(g_pathToMultiTextFileZipSource.pString); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], "TextFileOne.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[1], "TextFileTwo.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[2], "ThirdTextFile.txt"); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], "TextFileOne.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[1], "TextFileTwo.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[2], "ThirdTextFile.txt"); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST(TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFileAndSubDir) { + + //-------------------- + // Setup + //-------------------- + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 3; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(g_pathToMultiTextFileAndSubDirZipSource.pString); + } + + RaiiStringAppend_cString(&inputPathArray[0], "DirFileOne/"); + RaiiStringAppend_cString(&inputPathArray[1], "DirOfDirs/"); + RaiiStringAppend_cString(&inputPathArray[2], "TextFile.txt"); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 5; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(g_pathToMultiTextFileAndSubDirZipSource.pString); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], "DirFileOne/TextFileOne.txt"), + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[1], "DirOfDirs/OtherDir/DummyFile.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[2], "DirOfDirs/OtherDir/FilleInDirectory.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[3], "DirOfDirs/SubDirWithCopyOfTopLevelFile/TextFile.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[4], "TextFile.txt"); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], "DirFileOne/TextFileOne.txt"), + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[1], "DirOfDirs/OtherDir/DummyFile.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[2], "DirOfDirs/OtherDir/FilleInDirectory.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[3], "DirOfDirs/SubDirWithCopyOfTopLevelFile/TextFile.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[4], "TextFile.txt"); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFileAndSubDirDifferentBaseDir) { + + //-------------------- + // Setup + //-------------------- + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 2; + RaiiString inputPathArray[inputPathArraySize]; + + inputPathArray[0] = RaiiStringCreateFromCString(g_pathToMultiTextFileAndSubDirZipSource.pString); + inputPathArray[1] = RaiiStringCreateFromCString(g_pathToSmallBasicTextFileZipSource.pString); + + RaiiStringAppend_cString(&inputPathArray[0], "DirOfDirs/"); + RaiiStringAppend_cString(&inputPathArray[1], "SmallBasicTextFile.txt"); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 4; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < 3; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(g_pathToMultiTextFileAndSubDirZipSource.pString); + } + sourceFilePathArrayToCompare[3] = RaiiStringCreateFromCString(g_pathToSmallBasicTextFileZipSource.pString); + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], "DirOfDirs/OtherDir/DummyFile.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[1], "DirOfDirs/OtherDir/FilleInDirectory.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[2], "DirOfDirs/SubDirWithCopyOfTopLevelFile/TextFile.txt"); + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[3], "SmallBasicTextFile.txt"); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], "DirOfDirs/OtherDir/DummyFile.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[1], "DirOfDirs/OtherDir/FilleInDirectory.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[2], "DirOfDirs/SubDirWithCopyOfTopLevelFile/TextFile.txt"); + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[3], "SmallBasicTextFile.txt"); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf1GB) { + + if (!g_runLongTests) { + TEST_IGNORE_MESSAGE("Not running because g_runLongTests is false"); + } + + //-------------------- + // Setup + //-------------------- + + //----- Genereate 1GB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "1GB.txt"; + RAII_STRING fileWith1GB = RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&fileWith1GB, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &fileWith1GB, "wb"); + + const uint64_t oneGB = 1000000000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + printf("Writing 1GB file\n"); + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < oneGB; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFile); + } + if (bufSizeWritten != oneGB) { + printf("Expected (oneGB): %llu\n", (unsigned long long)oneGB); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFile); + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 1; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&inputPathArray[0], pFileName); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 1; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], pFileName); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], pFileName); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf3GB) { + + if (!g_runLongTests) { + TEST_IGNORE_MESSAGE("Not running because g_runLongTests is false"); + } + + //-------------------- + // Setup + //-------------------- + + //----- Genereate 3GB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "3GB.txt"; + RAII_STRING fileWith3GB = RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&fileWith3GB, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &fileWith3GB, "wb"); + + const uint64_t threeGB = 3000000000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + printf("Writing 3GB file\n"); + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < threeGB; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFile); + } + if (bufSizeWritten != threeGB) { + printf("Expected (threeGB): %llu\n", (unsigned long long)threeGB); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFile); + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 1; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&inputPathArray[0], pFileName); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 1; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], pFileName); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], pFileName); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +TEST( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf6GB) { + + if (!g_runLongTests) { + TEST_IGNORE_MESSAGE("Not running because g_runLongTests is false"); + } + + //-------------------- + // Setup + //-------------------- + + //----- Genereate 6GB file ----- + + const char *const pRelativePathToTmp = "./tmp/"; + const char *const pFileName = "6GB.txt"; + RAII_STRING fileWith6GB = RaiiStringCreateFromCString(pRelativePathToTmp); + RaiiStringAppend_cString(&fileWith6GB, pFileName); + + RecursiveMkdir(pRelativePathToTmp); + + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &fileWith6GB, "wb"); + + const uint64_t sixGB = 6000000000; + const uint64_t bufSize = 5000; + + char buf[bufSize]; + memset(buf, 'a', bufSize - 1); + buf[bufSize - 1] = '\0'; + + printf("Writing 6GB file\n"); + uint64_t bufSizeWritten = 0; + for (uint64_t i = 0; i < sixGB; i += bufSize) { + bufSizeWritten += fwrite(buf, 1, bufSize, pFile); + } + if (bufSizeWritten != sixGB) { + printf("Expected (sixGB): %llu\n", (unsigned long long)sixGB); + printf("Actual (bufSizeWritten): %llu\n", + (unsigned long long)bufSizeWritten); + TEST_FAIL_MESSAGE("Bytes written does not match expected size."); + } + + fclose(pFile); + + // clang-format off + + //----- Input path to zip ----- + + const size_t inputPathArraySize = 1; + RaiiString inputPathArray[inputPathArraySize]; + + for (size_t i = 0; i < inputPathArraySize; ++i) { + inputPathArray[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&inputPathArray[0], pFileName); + + //----- Source files to compare with ----- + + const size_t sourceFilePathArrayToCompareSize = 1; + RaiiString sourceFilePathArrayToCompare[sourceFilePathArrayToCompareSize]; + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + sourceFilePathArrayToCompare[i] = RaiiStringCreateFromCString(pRelativePathToTmp); + } + + RaiiStringAppend_cString(&sourceFilePathArrayToCompare[0], pFileName); + + //----- Unzipped files to compare with ----- + + RAII_STRING zippedResultPath = RaiiStringCreateFromCString("./tmp/out.zip"); + RAII_STRING unZippedResultPath = RaiiStringCreateFromCString("./tmp/out_unzipped/"); + + const size_t unZippedFilePathArrayToCompareSize = sourceFilePathArrayToCompareSize; + RaiiString unZippedFilePathArrayToCompare[unZippedFilePathArrayToCompareSize]; + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + unZippedFilePathArrayToCompare[i] = RaiiStringCreateFromCString(unZippedResultPath.pString); + } + + RaiiStringAppend_cString(&unZippedFilePathArrayToCompare[0], pFileName); + + // clang-format on + + //-------------------- + // Test + //-------------------- + + TestZipMinizipUnZipMinizip_Store( + &inputPathArray[0], inputPathArraySize, + &sourceFilePathArrayToCompare[0], sourceFilePathArrayToCompareSize, + &unZippedFilePathArrayToCompare[0], unZippedFilePathArrayToCompareSize, + &zippedResultPath, &unZippedResultPath); + + //-------------------- + // Cleanup + //-------------------- + + for (size_t i = 0; i < inputPathArraySize; ++i) { + RaiiStringClean(&inputPathArray[i]); + } + + for (size_t i = 0; i < sourceFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&sourceFilePathArrayToCompare[i]); + } + + for (size_t i = 0; i < unZippedFilePathArrayToCompareSize; ++i) { + RaiiStringClean(&unZippedFilePathArrayToCompare[i]); + } +} + +//============================== +// TEST_GROUP_RUNNER +//============================== + +TEST_GROUP_RUNNER(TestZipMinizipUnZipMinizip) { + // Zip() + UnZip() + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_SingleFile); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFile); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFileAndSubDir); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_MultiFileAndSubDirDifferentBaseDir); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf1GB); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf3GB); + RUN_TEST_CASE( + TestZipMinizipUnZipMinizip, + test_ZippingAndUnzippingRawTextGivesIdenticalOutput_CanZipAndUnZipFileOf6GB); +} diff --git a/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.h b/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.h new file mode 100644 index 00000000..8ff58133 --- /dev/null +++ b/CoDeLib/Test/src/TestZipMinizipUnZipMinizip.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void SetupTestZipMinizipUnZipMinizip(char *pFullPathToBenchmarkTestFiles, + bool runLongTests); diff --git a/CoDeLib/Test/src/main.c b/CoDeLib/Test/src/main.c index cfe9cf7d..f6efc7b5 100644 --- a/CoDeLib/Test/src/main.c +++ b/CoDeLib/Test/src/main.c @@ -4,15 +4,20 @@ #include "TestFileUtils.h" #include "TestUnZipMinizip.h" #include "TestUnZipMinizipInflateZlib.h" +#include "TestZipMinizip.h" +#include "TestZipMinizipUnZipMinizip.h" #include #include +#include static void RunAllTests(void) { RUN_TEST_GROUP(TestRaiiString); RUN_TEST_GROUP(TestDeflateInflateZlib); RUN_TEST_GROUP(TestFileUtils); RUN_TEST_GROUP(TestUnZipMinizip); + RUN_TEST_GROUP(TestZipMinizip); RUN_TEST_GROUP(TestUnZipMinizipInflateZlib); + RUN_TEST_GROUP(TestZipMinizipUnZipMinizip); RUN_TEST_GROUP(TestZipContentInfo); } @@ -21,21 +26,26 @@ int main(int argc, const char **argv) { // Should end with '/' RAII_STRING currentWorkingDirectory; + bool runLongTests = false; // TODO: use getopt(...) - if (argc < 3) { + if (argc < 4) { printf("Not enough arguments provided\n"); return 1; } else { fullPathToBenchmarkTestFiles = RaiiStringCreateFromCString(argv[1]); currentWorkingDirectory = RaiiStringCreateFromCString(argv[2]); + runLongTests = strcmp(argv[3], "true") == 0; } SetupTestDeflateInflateZlib(fullPathToBenchmarkTestFiles.pString); SetupTestFileUtils(fullPathToBenchmarkTestFiles.pString, - currentWorkingDirectory.pString); + currentWorkingDirectory.pString, runLongTests); SetupTestUnZipMinizip(fullPathToBenchmarkTestFiles.pString); + SetupTestZipMinizip(fullPathToBenchmarkTestFiles.pString); SetupTestUnZipMinizipInflateZlib(fullPathToBenchmarkTestFiles.pString); + SetupTestZipMinizipUnZipMinizip(fullPathToBenchmarkTestFiles.pString, + runLongTests); return UnityMain(argc, argv, RunAllTests); } diff --git a/CoDeLib/UnZip_minizip/src/UnZip_minizip.c b/CoDeLib/UnZip_minizip/src/UnZip_minizip.c index c495df91..cd1c9b38 100644 --- a/CoDeLib/UnZip_minizip/src/UnZip_minizip.c +++ b/CoDeLib/UnZip_minizip/src/UnZip_minizip.c @@ -103,9 +103,12 @@ UnZip(ZipContentInfo *const pZipInfo, const RaiiString *const pOutputDirPath) { int readCount = 0; do { - char buffer[256]; - readCount = - unzReadCurrentFile(uzfile, buffer, sizeof(buffer) - 1); + const uint32_t bufSize = 1024; + char buffer[bufSize]; + readCount = unzReadCurrentFile(uzfile, buffer, bufSize - 1); + + // It is safe to index with `readCount`. Because it + // will be at most `bufSize - 1`. buffer[readCount] = '\0'; if (readCount < 0) { status = UNZIP_ERROR; diff --git a/CoDeLib/Zip_minizip/CMakeLists.txt b/CoDeLib/Zip_minizip/CMakeLists.txt new file mode 100644 index 00000000..01a53c21 --- /dev/null +++ b/CoDeLib/Zip_minizip/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(Zip_minizip STATIC + src/Zip_minizip.c) + +target_include_directories(Zip_minizip PUBLIC + $ + $ +) + +find_package(MINIZIP REQUIRED) +target_link_libraries(Zip_minizip PRIVATE MINIZIP::minizip) + +set(Zip_minizip_PUBLIC_INCLUDE_PATH ${CoDeLib_PUBLIC_INCLUDE_PATH}/Zip_minizip) +set(Zip_minizip_PUBLIC_HEADERS + ${Zip_minizip_PUBLIC_INCLUDE_PATH}/Zip_minizip.h +) +install(FILES ${Zip_minizip_PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CoDeLib/Zip_minizip) diff --git a/CoDeLib/Zip_minizip/src/Zip_minizip.c b/CoDeLib/Zip_minizip/src/Zip_minizip.c new file mode 100644 index 00000000..e4fd045f --- /dev/null +++ b/CoDeLib/Zip_minizip/src/Zip_minizip.c @@ -0,0 +1,330 @@ +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +// minizip +#include +#include + +//============================== +// Helper functions +//============================== + +/* +@brief Zips the content of a path into a zip file. It also handles directories +by recursively going through them. +@param zFile The zip file to write to. This should already have been opened with +zipOpen64. +@param pInputPath The full path to zip. This can be a file or a directory path. +@param level This is the level at which the file or directory is in the zip +file. +*/ +ZIP_RETURN_CODES ZipPath(zipFile zFile, const char *const pInputPath, + uint8_t level); + +#ifdef _WIN32 +ZIP_RETURN_CODES ZipDirectory_Windows(zipFile zFile, + const char *const pInputPath, + uint8_t level); +#else +ZIP_RETURN_CODES ZipDirectory_Posix(zipFile zFile, + const char *const pInputPath); +#endif +//============================== +// Interface functions +//============================== + +ZIP_RETURN_CODES +Zip(const char *pOutputZipPath, const char **pInputPathArray, + const size_t inputPathArraySize) { + if (pOutputZipPath == NULL || pInputPathArray == NULL || + inputPathArraySize == 0) { + return ZIP_ERROR; + } + + if (PathExists(pOutputZipPath)) { + printf("Output zip file already exists: %s\n", pOutputZipPath); + return ZIP_ERROR; + } + + ZIP_RETURN_CODES returnCode = ZIP_SUCCESS; + + // It doesn't matter that the path contains the file name. This is + // simply ignored. + RecursiveMkdir(pOutputZipPath); + + const int append = 0; + zipFile zFile = zipOpen64(pOutputZipPath, append); + if (zFile == NULL) { + // TODO: make the string handling safer by limiting the size + printf("Could not open %s for zipping\n", pOutputZipPath); + zipClose(zFile, NULL); + return false; + } + + for (size_t i = 0; i < inputPathArraySize && returnCode == ZIP_SUCCESS; + ++i) { + const size_t pathLength = + strnlen(pInputPathArray[i], MAX_PATH_LENGTH_WTH_TERMINATOR); + if (pathLength == MAX_PATH_LENGTH_WTH_TERMINATOR) { + printf("Path is too long: %s\n", pInputPathArray[i]); + returnCode = ZIP_ERROR; + break; + } + + const char lastChar = pInputPathArray[i][pathLength - 1]; + bool isDir = lastChar == '/' || lastChar == '\\'; + + if (!PathExists(pInputPathArray[i])) { + printf("Path does not exist: %s\n", pInputPathArray[i]); + returnCode = ZIP_ERROR; + break; + } + + if (!isDir) { + returnCode = ZipPath(zFile, pInputPathArray[i], 0); + } else { +#ifdef _WIN32 + returnCode = ZipDirectory_Windows(zFile, pInputPathArray[i], 0); +#else + returnCode = ZipDirectory_Posix(zFile, pInputPathArray[i]); +#endif + } + } + + const char *global_comment = NULL; + zipClose(zFile, global_comment); + + return returnCode; +} + +const struct IZip zip_minizip = { + .Zip = Zip, +}; + +//============================== +// Helper functions +//============================== + +ZIP_RETURN_CODES ZipPath(zipFile zFile, const char *const pInputPath, + uint8_t level) { + ZIP_RETURN_CODES returnCode = ZIP_SUCCESS; + + RAII_STRING pathToSaveInZip = RaiiStringCreateFromCString(""); + + RAII_STRING tmpInputPath = RaiiStringCreateFromCString(pInputPath); + for (size_t i = 0; i <= level; ++i) { + char lastPartOfPath[MAX_PATH_LENGTH_WTH_TERMINATOR]; + size_t startIndexOfLastPart = + ExtractLastPartOfPath(tmpInputPath.pString, lastPartOfPath, + MAX_PATH_LENGTH_WTH_TERMINATOR); + + if (startIndexOfLastPart == SIZE_MAX) { + printf("startIndexOfLastPart == SIZE_MAX\n"); + return ZIP_ERROR; + } + + char tmpBuf[MAX_PATH_LENGTH_WTH_TERMINATOR]; + const size_t charsWritten = + snprintf(&tmpBuf[0], MAX_PATH_LENGTH_WTH_TERMINATOR, "%s%s", + lastPartOfPath, pathToSaveInZip.pString); + if (charsWritten < strlen(lastPartOfPath)) { + printf("The whole path was not written. \n Expected: '%s%s'\n " + "Actual: '%s'\n", + lastPartOfPath, pathToSaveInZip.pString, tmpBuf); + return ZIP_ERROR; + } + RaiiStringClean(&pathToSaveInZip); + pathToSaveInZip = RaiiStringCreateFromCString(tmpBuf); + + tmpInputPath.pString[startIndexOfLastPart] = '\0'; + tmpInputPath.lengthWithTermination = startIndexOfLastPart + 1; + } + RaiiStringClean(&tmpInputPath); + + RAII_STRING inputPath = RaiiStringCreateFromCString(pInputPath); + + const size_t lastPartOfPathLenght = + pathToSaveInZip.lengthWithTermination - 1; + if (lastPartOfPathLenght == MAX_PATH_LENGTH_WTH_TERMINATOR) { + printf("lastPartOfPathLenght == MAX_PATH_LENGTH_WTH_TERMINATOR\n"); + return ZIP_ERROR; + } + + const char lastChar = pathToSaveInZip.pString[lastPartOfPathLenght - 1]; + bool isDir = lastChar == '/' || lastChar == '\\'; + + uint64_t fileSize = 0; + + if (!isDir) { + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &inputPath, "rb"); + fileSize = GetFileSizeInBytes(pFile); + fclose(pFile); + } else if (lastChar == '\\') { + // Zip requeres '/' as a separator to indicate directories + pathToSaveInZip.pString[lastPartOfPathLenght - 1] = '/'; + } + + const int compressoinsMethod = MZ_COMPRESS_METHOD_STORE; + const int compressionLevel = 0; + // Don't store as raw file. Otherwise the CRC is not properly handled. + const int raw = 0; + const int requiresZip64 = (fileSize > 0xffffffff) ? 1 : 0; + + int status = zipOpenNewFileInZip2_64( + zFile, pathToSaveInZip.pString, NULL, NULL, 0, NULL, 0, NULL, + compressoinsMethod, compressionLevel, raw, requiresZip64); + + if (status != MZ_OK) { + zipCloseFileInZip(zFile); + printf("Failed to zip with zipOpenNewFileInZip2_64(...)\n"); + return ZIP_ERROR; + } + + if (!isDir) { + FILE *pFile = NULL; + OpenFileWithMode(&pFile, &inputPath, "rb"); + + const size_t bufSize = 1024; + char buf[bufSize]; + + bool stopWriting = false; + do { + size_t charsToWrite = fread(buf, 1, bufSize, pFile); + + if (charsToWrite != bufSize) { + if (feof(pFile)) { + stopWriting = true; + } else if (ferror(pFile)) { + perror("Error reading file to be written to zip\n"); + returnCode = ZIP_ERROR; + break; + } + } + + status = zipWriteInFileInZip(zFile, buf, charsToWrite); + if (status != MZ_OK) { + perror("Failed to write to zip with zipWriteInFileInZip(...)"); + returnCode = ZIP_ERROR; + break; + } + } while (!stopWriting); + + fclose(pFile); + } + + zipCloseFileInZip(zFile); + return returnCode; +} + +#ifdef _WIN32 +ZIP_RETURN_CODES ZipDirectory_Windows(zipFile zFile, + const char *const pInputPath, + uint8_t level) { + ZIP_RETURN_CODES returnCode = ZIP_SUCCESS; + + // Add the direcotry to the zip + returnCode = ZipPath(zFile, pInputPath, level); + if (returnCode != ZIP_SUCCESS) { + printf("Failed to zip: %s\n", pInputPath); + return ZIP_ERROR; + } + + RAII_STRING searchPath = RaiiStringCreateFromCString(pInputPath); + RaiiStringAppend_cString(&searchPath, "*"); + + WIN32_FIND_DATA findFileData; + HANDLE hFind = FindFirstFile(searchPath.pString, &findFileData); + if (hFind == INVALID_HANDLE_VALUE) { + printf("Failed to find first file in directory: %s\n", pInputPath); + return ZIP_ERROR; + } + + do { + const char *const name = findFileData.cFileName; + + // Skip the special entries "." and ".." + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + + // Construct the full path of the current file or directory + RAII_STRING fullPath = RaiiStringCreateFromCString(pInputPath); + RaiiStringAppend_cString(&fullPath, name); + + // Check if the entry is a directory + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + // Directory found does not have a trailing '/' or '\\' by default + RaiiStringAppend_cString(&fullPath, "/"); + returnCode = + ZipDirectory_Windows(zFile, fullPath.pString, level + 1); + if (returnCode != ZIP_SUCCESS) { + printf("Failed to zip directory: %s\n", fullPath.pString); + break; + } + } else { + returnCode = ZipPath(zFile, fullPath.pString, level + 1); + if (returnCode != ZIP_SUCCESS) { + printf("Failed to zip file: %s\n", fullPath.pString); + break; + } + } + } while (FindNextFile(hFind, &findFileData) != 0); + + FindClose(hFind); + + return returnCode; +} + +#else + +ZIP_RETURN_CODES ZipDirectory_Posix(zipFile zFile, + const char *const pInputPath) { + ZIP_RETURN_CODES returnCode = ZIP_SUCCESS; + + // TODO: Determine if pInputPath should be converted to an + // absolute path. + RAII_STRING inputPathCopy = RaiiStringCreateFromCString(pInputPath); + char *const inputPathArray[] = {inputPathCopy.pString, NULL}; + FTS *pFileSystem = + fts_open(&inputPathArray[0], FTS_COMFOLLOW | FTS_NOCHDIR, NULL); + if (pFileSystem == NULL) { + printf("Failed to open file system for: %s\n", pInputPath); + return ZIP_ERROR; + } + + FTSENT *node = fts_read(pFileSystem); + while (node != NULL && returnCode == ZIP_SUCCESS) { + const char *const pFpath = node->fts_path; + const int level = node->fts_level; + const int typeflag = node->fts_info; + + RAII_STRING fpath = RaiiStringCreateFromCString(pFpath); + + if (typeflag == FTS_D && + fpath.pString[fpath.lengthWithTermination - 2] != '/') { + RaiiStringAppend_cString(&fpath, "/"); + } + + if (typeflag == FTS_F || typeflag == FTS_D) { + returnCode = ZipPath(zFile, fpath.pString, level); + if (returnCode != ZIP_SUCCESS) { + printf("Failed to zip: %s\n", fpath.pString); + } + } + + node = fts_read(pFileSystem); + } + fts_close(pFileSystem); + + return returnCode; +} +#endif diff --git a/CoDeLib/include/CoDeLib/FileUtils/FileUtils.h b/CoDeLib/include/CoDeLib/FileUtils/FileUtils.h index 744c1f84..398f53c6 100644 --- a/CoDeLib/include/CoDeLib/FileUtils/FileUtils.h +++ b/CoDeLib/include/CoDeLib/FileUtils/FileUtils.h @@ -2,6 +2,7 @@ #include #include +#include #include #define MAX_PATH_LENGTH 256 @@ -81,6 +82,7 @@ returned. size_t ExtractLastPartOfPath(const char *const pPath, char *const pDestBuffer, size_t destBufferSize); -void OpenFileWithMode(FILE **pInFile, RaiiString *pFullPath, char *pOpenMode); -size_t GetFileSizeInBytes(FILE *pFile); +void OpenFileWithMode(FILE **pInFile, const RaiiString *const pFullPath, + const char *const pOpenMode); +uint64_t GetFileSizeInBytes(FILE *pFile); bool FilesAreEqual(FILE *pFile1, FILE *pFile2); diff --git a/CoDeLib/include/CoDeLib/IZip.h b/CoDeLib/include/CoDeLib/IZip.h new file mode 100644 index 00000000..4d773525 --- /dev/null +++ b/CoDeLib/include/CoDeLib/IZip.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +typedef enum { ZIP_SUCCESS, ZIP_ERROR } ZIP_RETURN_CODES; + +struct IZip { + /*! + * @brief Zips the input file(s) and write the output to the output zip + * file. + * @param pOutputZipPath The path to the output zip file. If the directory + * does not exist, it will be created. + * @param pInputPathArray The array of paths to the files or directories to + * be zipped. Both absolute and relative paths are supported. Path to + * directories will zip all files and sub-directories in the directory. Path + * to files will zip the file without any directories. The array can be a + * mix of absolute, relative, directies and files. + * @param inputPathArraySize The number of elements in pInputPathArray. + * @return ZIP_SUCCESS if the zipping was successful, ZIP_ERROR + * otherwise. + */ + ZIP_RETURN_CODES(*Zip) + (const char *pOutputZipPath, const char **pInputPathArray, + const size_t inputPathArraySize); +}; diff --git a/CoDeLib/include/CoDeLib/Zip_minizip/Zip_minizip.h b/CoDeLib/include/CoDeLib/Zip_minizip/Zip_minizip.h new file mode 100644 index 00000000..86f52414 --- /dev/null +++ b/CoDeLib/include/CoDeLib/Zip_minizip/Zip_minizip.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const struct IZip zip_minizip; diff --git a/DevEnvSetup.md b/DevEnvSetup.md index 96960377..c66a2cff 100644 --- a/DevEnvSetup.md +++ b/DevEnvSetup.md @@ -54,3 +54,5 @@ $ python ./Scripts/BuildBenchmark.py --TargetPlatform zynq ``` Additionally, you should be able to set breakpoints in the source code and run the code and debugger via the Visual Studio Code UI. + +To run all the tests on the zybo board run `./Test_Release ./Files/ $(pwd)/ true` diff --git a/External/minizip-ng b/External/minizip-ng index 6d45beb9..ca7b638b 160000 --- a/External/minizip-ng +++ b/External/minizip-ng @@ -1 +1 @@ -Subproject commit 6d45beb98d713ce191c371b625cc9421ff56acd0 +Subproject commit ca7b638b791e577590f22656df5ad615287dbafe diff --git a/External/zlib b/External/zlib index d4768283..5a82f71e 160000 --- a/External/zlib +++ b/External/zlib @@ -1 +1 @@ -Subproject commit d476828316d05d54c6fd6a068b121b30c147b5cd +Subproject commit 5a82f71ed1dfc0bec044d9702463dbdf84ea3b71 diff --git a/Scripts/BuildAndInstallExternalLibs.py b/Scripts/BuildAndInstallExternalLibs.py index 916ca13d..021ba629 100644 --- a/Scripts/BuildAndInstallExternalLibs.py +++ b/Scripts/BuildAndInstallExternalLibs.py @@ -74,7 +74,7 @@ def BuildAndInstallZlib( print("==============================") print(ProjectName + ": Configuring ({})".format(BuildTypeString)) print("==============================") - configureCommand = 'cmake -G "{0}" -DCMAKE_TOOLCHAIN_FILE="{1}" -S {2} -B {3} -DCMAKE_INSTALL_PREFIX="{4}" -DZLIB_BUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE={5}'.format( + configureCommand = 'cmake -G "{0}" -DCMAKE_TOOLCHAIN_FILE="{1}" -S {2} -B {3} -DCMAKE_INSTALL_PREFIX="{4}" -DZLIB_BUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE={5} -DZLIB_BUILD_SHARED=OFF'.format( buildEnv.GetCmakeGenerator(), buildEnv.GetCustomToolChainPath(), TopLevelCMakeListsDirectory, @@ -152,7 +152,7 @@ def BuildAndInstallMinizipNg( print("==============================") print(ProjectName + ": Configuring ({})".format(BuildTypeString)) print("==============================") - configureCommand = 'cmake -G "{0}" -DCMAKE_TOOLCHAIN_FILE="{1}" -S {2} -B {3} -DCMAKE_INSTALL_PREFIX="{4}" -DCMAKE_BUILD_TYPE={5} -DBUILD_SHARED_LIBS=OFF -DMZ_COMPAT=ON -DMZ_ZLIB=OFF -DMZ_BZIP2=OFF -DMZ_LZMA=OFF -DMZ_ZSTD=OFF -DMZ_LIBCOMP=OFF -DMZ_FETCH_LIBS=OFF -DMZ_PKCRYPT=OFF -DMZ_WZAES=OFF -DMZ_OPENSSL=OFF -DMZ_LIBBSD=OFF -DMZ_ICONV=OFF'.format( + configureCommand = 'cmake -G "{0}" -DCMAKE_TOOLCHAIN_FILE="{1}" -S {2} -B {3} -DCMAKE_INSTALL_PREFIX="{4}" -DCMAKE_BUILD_TYPE={5} -DBUILD_SHARED_LIBS=OFF -DMZ_COMPAT=ON -DMZ_ZLIB=OFF -DMZ_BZIP2=OFF -DMZ_LZMA=OFF -DMZ_ZSTD=OFF -DMZ_LIBCOMP=OFF -DMZ_FETCH_LIBS=OFF -DMZ_PKCRYPT=OFF -DMZ_WZAES=OFF -DMZ_OPENSSL=OFF -DMZ_LIBBSD=OFF -DMZ_ICONV=OFF -DUSE_FILE_OFFSET_BITS64=ON'.format( buildEnv.GetCmakeGenerator(), buildEnv.GetCustomToolChainPath(), TopLevelCMakeListsDirectory, diff --git a/Scripts/RunTest.py b/Scripts/RunTest.py index cb0a58a4..abae950e 100644 --- a/Scripts/RunTest.py +++ b/Scripts/RunTest.py @@ -9,6 +9,7 @@ parser = argparse.ArgumentParser(description="Build and install external libraries") +targetPlatformOptions = ["host", "zynq"] targetPlatformOptions = ["host", "zynq"] parser.add_argument( @@ -20,6 +21,13 @@ help="Target platform (default: %(default)s)", ) +parser.add_argument( + "--RunLongTests", + action="store_true", + dest="runLongTests", + help="Add this flag if long running unit tests should be run (default: not selected)", +) + args = parser.parse_args() targetPlatform: EnvironmentConfig.Platform @@ -123,6 +131,9 @@ def RunTests( + ' "' + CurrentWorkingDirectory + '" ' + + ' "' + + ("true" if args.runLongTests else "false") + + '" ' + "-v" ) TestCommand = str(TestExecutablePath) + " " + TestOptions diff --git a/ZyboEmbeddedLinux/Petalinux/ZyboPetalinuxGuide.md b/ZyboEmbeddedLinux/Petalinux/ZyboPetalinuxGuide.md index b39765b5..f6f2c075 100644 --- a/ZyboEmbeddedLinux/Petalinux/ZyboPetalinuxGuide.md +++ b/ZyboEmbeddedLinux/Petalinux/ZyboPetalinuxGuide.md @@ -237,6 +237,7 @@ $ petalinux-config -c rootfs ``` - In the config enable `user packages > peekpoke`. +- In the config enable `Filesystem Packages > misc > gdb > gdb`. - Exit and save #### 8.6 Build the project @@ -329,9 +330,10 @@ By using wic, prepering the SD card and moving files is all handled automaticall ```sh $ cd ~/petalinux/BasicLinuxZybo +$ source settings.sh ``` -The command below uses `~/petalinux/BasicLinuxZybo/build/rootfs.wks` for creating the wic image. By default (in the version used in these steps) this will create a `2GB` boot partition and a `4G` root partition. So make sure that your SD card is large enough. If you want to change this, see the [petalinux-package wic Command Options](https://docs.amd.com/r/en-US/ug1144-petalinux-tools-reference-guide/petalinux-package-wic-Command-Options) documentation. +The command below uses `~/petalinux/BasicLinuxZybo/build/rootfs.wks` for creating the wic image. By default (in the version used in these steps) this will create a `2GB` boot partition and a `4G` root partition. So make sure that your SD card is large enough. ```sh $ petalinux-package wic @@ -339,6 +341,21 @@ $ petalinux-package wic The console output will show where the wic image is created. +To see al the options for the wic generation command use: + +```sh +$ petalinux-package wic -h +``` + +If you want a bigger rootfs partition size for example run: + +```sh +$ petalinux-package wic --size ,32G +``` + +For more information see the [petalinux-package wic Command Options](https://docs.amd.com/r/en-US/ug1144-petalinux-tools-reference-guide/petalinux-package-wic-Command-Options) documentation. + +
(Optional) 9.1.1 Inspect the wic image