From 84149d0ad1302244f62a1b07398042c9fd334cbb Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Thu, 22 Jun 2017 01:26:08 +1000 Subject: [PATCH] Add file write tests and delete functionality --- dqn.h | 31 ++++++++++-- dqn_unit_test.cpp | 120 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 136 insertions(+), 15 deletions(-) diff --git a/dqn.h b/dqn.h index 98ff3bc..687cf96 100644 --- a/dqn.h +++ b/dqn.h @@ -801,7 +801,6 @@ DQN_FILE_SCOPE u32 DqnRnd_PCGNext (DqnRandPCGState *pcg); DQN_FILE_SCOPE f32 DqnRnd_PCGNextf(DqnRandPCGState *pcg); // Returns a random integer N between [min, max] DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max); - #endif /* DQN_H */ //////////////////////////////////////////////////////////////////////////////// @@ -817,6 +816,7 @@ DQN_FILE_SCOPE i32 DqnRnd_PCGRange(DqnRandPCGState *pcg, i32 min, i32 max); #ifdef DQN_UNIX_PLATFORM #include + #include // unlink() #include // Basic File I/O #include // readdir()/opendir()/closedir() #endif @@ -854,6 +854,7 @@ typedef struct DqnFile u32 permissionFlags; } DqnFile; +// NOTE: W(ide) versions of functions only work on Win32, since Unix is UTF-8 compatible. // Open a handle to the file DQN_FILE_SCOPE bool DqnFile_Open (const char *const path, DqnFile *const file, const u32 permissionFlags, const enum DqnFileAction action); DQN_FILE_SCOPE bool DqnFile_OpenW(const wchar_t *const path, DqnFile *const file, const u32 permissionFlags, const enum DqnFileAction action); @@ -864,6 +865,10 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, u8 *const buffer, DQN_FILE_SCOPE size_t DqnFile_Read (const DqnFile file, u8 *const buffer, const size_t numBytesToRead); DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file); +// NOTE: You can't delete a file unless the handle has been closed to it on Win32. +DQN_FILE_SCOPE bool DqnFile_Delete (const char *const path); +DQN_FILE_SCOPE bool DqnFile_DeleteW(const wchar_t *const path); + // Return an array of strings of the files in the directory in UTF-8. numFiles // returns the number of strings read. // This is allocated using malloc and MUST BE FREED! Can be done manually or @@ -1704,9 +1709,10 @@ DQN_FILE_SCOPE bool DqnMemStack_Pop(DqnMemStack *const stack, void *ptr, size_t "'ptr' to pop does not belong to current memStack attached block")) { size_t calcSize = (size_t)currPtr - (size_t)ptr; - if (DQN_ASSERT_MSG(calcSize == size, "'ptr' was not the last item allocated to memStack")) + size_t sizeAligned = DQN_ALIGN_POW_N(size, stack->byteAlign); + if (DQN_ASSERT_MSG(calcSize == sizeAligned, "'ptr' was not the last item allocated to memStack")) { - stack->block->used -= size; + stack->block->used -= sizeAligned; return true; } } @@ -3878,7 +3884,7 @@ DQN_FILE_SCOPE size_t DqnFile_Write(const DqnFile *const file, { size_t numBytesWritten = 0; // TODO(doyle): Implement when it's needed - if (DQN_ASSERT_MSG(fileOffset != 0, "'fileOffset' not implemented yet")) return 0; + if (!DQN_ASSERT_MSG(fileOffset == 0, "'fileOffset' not implemented yet")) return 0; if (!file || !buffer) return numBytesToWrite; #if defined(DQN_WIN32_PLATFORM) @@ -3963,6 +3969,23 @@ DQN_FILE_SCOPE void DqnFile_Close(DqnFile *const file) } } +DQN_FILE_SCOPE bool DqnFile_Delete(const char *const path) +{ + if (!path) return false; + + // TODO(doyle): Logging +#if defined(DQN_WIN32_PLATFORM) + return DeleteFile(path); + +#elif defined(DQN_UNIX_PLATFORM) + i32 result = unlink(path); + + if (result == 0) return true; + return false; + +#endif +} + DQN_FILE_SCOPE char **DqnDir_Read(const char *const dir, u32 *const numFiles) { char **result = DqnDirInternal_PlatformRead(dir, numFiles); diff --git a/dqn_unit_test.cpp b/dqn_unit_test.cpp index 3f56aba..29cadac 100644 --- a/dqn_unit_test.cpp +++ b/dqn_unit_test.cpp @@ -1,5 +1,6 @@ #if (defined(_WIN32) || defined(_WIN64)) #define DQN_WIN32_IMPLEMENTATION + #include #endif #if defined(__linux__) @@ -1445,21 +1446,46 @@ void FileTest() { // File i/o { - { - DqnFile file = {}; - DQN_ASSERT(DqnFile_Open( - ".clang-format", &file, - (DqnFilePermissionFlag_Write | DqnFilePermissionFlag_Read), - DqnFileAction_OpenOnly)); + // Test file open + { + const char *const FILE_TO_OPEN = ".clang-format"; + u32 expectedSize = 0; #if defined(DQN_UNIX_IMPLEMENTATION) - const u32 EXPECTED_SIZE = 1274; + { + struct stat fileStat = {0}; + DQN_ASSERT(stat(FILE_TO_OPEN, &fileStat) == 0); + expectedSize = fileStat.st_size; + } + #elif defined(DQN_WIN32_IMPLEMENTATION) - const u32 EXPECTED_SIZE = 1320; + { + HANDLE handle = + CreateFile(FILE_TO_OPEN, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + { + DqnWin32_DisplayLastError("CreateFile() failed"); + } + DQN_ASSERT(handle != INVALID_HANDLE_VALUE); + + LARGE_INTEGER size; + DQN_ASSERT(GetFileSizeEx(handle, &size)); + + CloseHandle(handle); + expectedSize = size.LowPart; + } #endif - DQN_ASSERT_MSG(file.size == EXPECTED_SIZE, + + DqnFile file = {}; + DQN_ASSERT(DqnFile_Open( + ".clang-format", &file, + (DqnFilePermissionFlag_Write | DqnFilePermissionFlag_Read), + DqnFileAction_OpenOnly)); + + DQN_ASSERT_MSG(file.size == expectedSize, "DqnFileOpen() failed: file.size: %d, expected:%d\n", - file.size, EXPECTED_SIZE); + file.size, expectedSize); u8 *buffer = (u8 *)calloc(1, (size_t)file.size * sizeof(u8)); DQN_ASSERT(DqnFile_Read(file, buffer, (u32)file.size) == file.size); @@ -1470,6 +1496,7 @@ void FileTest() file.permissionFlags == 0); } + // Test invalid file { DqnFile file = {}; DQN_ASSERT(!DqnFile_Open( @@ -1482,10 +1509,77 @@ void FileTest() printf("FileTest(): FileIO: Completed successfully\n"); } - // TODO(doyle): Write tests for writing out to file } + //////////////////////////////////////////////////////////////////////////// + // Write Test + //////////////////////////////////////////////////////////////////////////// { + const char *fileNames[] = {"dqn_1", "dqn_2", "dqn_3", "dqn_4", "dqn_5"}; + const char *writeData[] = {"1234", "2468", "36912", "481216", + "5101520"}; + DqnFile files[DQN_ARRAY_COUNT(fileNames)] = {}; + + // Write data out to some files + for (u32 i = 0; i < DQN_ARRAY_COUNT(fileNames); i++) + { + u32 permissions = + DqnFilePermissionFlag_Read | DqnFilePermissionFlag_Write; + if (!DqnFile_Open(fileNames[i], files + i, permissions, + DqnFileAction_ClearIfExist)) + { + bool result = DqnFile_Open(fileNames[i], files + i, permissions, + DqnFileAction_CreateIfNotExist); + DQN_ASSERT(result); + } + + size_t bytesToWrite = DqnStr_Len(writeData[i]); + u8 *dataToWrite = (u8 *)(writeData[i]); + size_t bytesWritten = DqnFile_Write(files + i, dataToWrite, bytesToWrite, 0); + DQN_ASSERT(bytesWritten == bytesToWrite); + DqnFile_Close(&files[i]); + } + + DqnMemStack memStack = {}; + DQN_ASSERT(DqnMemStack_Init(&memStack, DQN_MEGABYTE(1), true)); + // Read data back in + for (u32 i = 0; i < DQN_ARRAY_COUNT(fileNames); i++) + { + u32 permissions = DqnFilePermissionFlag_Read; + DqnFile *file = files + i; + bool result = DqnFile_Open(fileNames[i], file, permissions, + DqnFileAction_OpenOnly); + DQN_ASSERT(result); + + u8 *buffer = (u8 *)DqnMemStack_Push(&memStack, file->size); + DQN_ASSERT(buffer); + + size_t bytesRead = DqnFile_Read(files[i], buffer, file->size); + DQN_ASSERT(bytesRead == file->size); + + // Verify the data is the same as we wrote out + DQN_ASSERT(DqnStr_Cmp((char *)buffer, (writeData[i])) == 0); + + // Delete when we're done with it + DQN_ASSERT(DqnMemStack_Pop(&memStack, buffer, file->size)); + DqnFile_Close(file); + + DQN_ASSERT(DqnFile_Delete(fileNames[i])); + } + + // Then check delete actually worked, files should not exist. + for (u32 i = 0; i < DQN_ARRAY_COUNT(fileNames); i++) + { + DqnFile dummy = {}; + u32 permissions = DqnFilePermissionFlag_Read; + bool fileExists = DqnFile_Open(fileNames[i], &dummy, permissions, + DqnFileAction_OpenOnly); + DQN_ASSERT(!fileExists); + } + DqnMemStack_Free(&memStack); + } + + { u32 numFiles; #if defined(DQN_UNIX_IMPLEMENTATION) char **filelist = DqnDir_Read(".", &numFiles); @@ -1559,6 +1653,10 @@ FILE_SCOPE void JobQueueTest() for (i32 i = 0; i < DQN_ARRAY_COUNT(globalDebugCounterMemoize); i++) DQN_ASSERT(globalDebugCounterMemoize[i]); + while (DqnJobQueue_TryExecuteNextJob(jobQueue) && + !DqnJobQueue_AllJobsComplete(jobQueue)) + ; + printf("\nJobQueueTest(): Final incremented value: %d\n", globalDebugCounter); DQN_ASSERT(globalDebugCounter == DQN_ARRAY_COUNT(globalDebugCounterMemoize)); }