#include "unittest/TestSuite.h"
#include "utilities/printfFormats.h"
#include "utilities/IOUtilities.h"
#include <float.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#ifdef WIN32
#include <fcntl.h>
static int mkstemp(char * template) {
	int result = -1;
	mktemp(template); 
	result = open(template, O_RDWR | O_BINARY | O_CREAT | O_EXCL | _O_SHORT_LIVED, _S_IREAD | _S_IWRITE); 
	return result;
}
#endif

static void testMemreadContextInit() {
	struct memreadContext context;
	char * data1 = "abcd", * data2 = "efg";
	
	context = memreadContextInit(data1, 4);
	TestCase_assert(context.data == data1, "Expected %p but got %p", data1, context.data);
	TestCase_assert(context.length == 4, "Expected 4 but got %d", (int) context.length);
	TestCase_assert(context.position == 0, "Expected 0 but got %d", (int) context.position);
	
	context = memreadContextInit(data2, 3);
	TestCase_assert(context.data == data2, "Expected %p but got %p", data2, context.data);
	TestCase_assert(context.length == 3, "Expected 3 but got %d", (int) context.length);
	TestCase_assert(context.position == 0, "Expected 0 but got %d", (int) context.position);
}

static void testMemread() {
	char target[4];
	char expectedTarget1[4] = {'a', 'b', 'c', 'd'}, expectedTarget2[4] = {'1', '2', '3', '\x00'};
	struct memreadContext context;
	bool result;
	
	context = memreadContextInit("abcd1234", 8);
	memset(target, 0, 4);
	result = memread(&context, 4, target);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.position == 4, "Expected 4 but got %d", (int) context.position);
	TestCase_assertBytesEqual(expectedTarget1, target, 4);
	
	memset(target, 0, 4);
	result = memread(&context, 3, target);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.position == 7, "Expected 7 but got %d", (int) context.position);
	TestCase_assertBytesEqual(expectedTarget2, target, 4);
	
	result = memread(&context, 2, NULL);
	TestCase_assert(!result, "Expected false but got true");
	TestCase_assert(context.position == 7, "Expected 7 but got %d", (int) context.position);
	
	result = memread(&context, 1, NULL);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.position == 8, "Expected 8 but got %d", (int) context.position);
}

static void testMemwriteContextInit() {
	struct memwriteContext context;
	char * data1 = "abcd", * data2 = "efg";
	
	context = memwriteContextInit(data1, 4, 5, false);
	TestCase_assert(context.data == data1, "Expected %p but got %p", data1, context.data);
	TestCase_assert(context.length == 4, "Expected 4 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize == 5, "Expected 5 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 0, "Expected 0 but got %d", (int) context.position);
	TestCase_assert(!context.realloc, "Expected false but got true");
	
	context = memwriteContextInit(data2, 3, 4, true);
	TestCase_assert(context.data == data2, "Expected %p but got %p", data2, context.data);
	TestCase_assert(context.length == 3, "Expected 3 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize == 4, "Expected 4 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 0, "Expected 0 but got %d", (int) context.position);
	TestCase_assert(context.realloc, "Expected true but got false");
}

static void testMemwrite() {
	struct memwriteContext context;
	char target[4];
	bool result;
	
	memset(target, 0, 4);
	context = memwriteContextInit(target, 3, 4, false);
	result = memwrite(&context, 2, "fg");
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 3, "Expected 3 but got %d", (int) context.length);
	TestCase_assert(context.position == 2, "Expected 2 but got %d", (int) context.position);
	TestCase_assertBytesEqual("fg\x00\x00", target, 4);
	
	result = memwrite(&context, 3, "sfd");
	TestCase_assert(!result, "Expected false but got true");
	TestCase_assert(context.length == 3, "Expected 3 but got %d", (int) context.length);
	TestCase_assert(context.position == 2, "Expected 2 but got %d", (int) context.position);
	TestCase_assertBytesEqual("fg\x00\x00", target, 4);
	
	result = memwrite(&context, 1, "s");
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 3, "Expected 3 but got %d", (int) context.length);
	TestCase_assert(context.position == 3, "Expected 3 but got %d", (int) context.position);
	TestCase_assertBytesEqual("fgs\x00", target, 4);
	
	target[3] = 0xFF;
	result = memwrite(&context, 1, NULL);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 4, "Expected 4 but got %d", (int) context.length);
	TestCase_assert(context.position == 4, "Expected 4 but got %d", (int) context.position);
	TestCase_assertBytesEqual("fgs\x00", target, 4);
	
	context = memwriteContextInit(malloc(1), 0, 1, true);
	result = memwrite(&context, 2, "aa");
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 2, "Expected 2 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize >= 2, "Expected >= 2 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 2, "Expected 2 but got %d", (int) context.position);
	TestCase_assertBytesEqual("aa", context.data, 2);
	
	result = memwrite(&context, 5, NULL);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 7, "Expected 7 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize >= 7, "Expected >= 7 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 7, "Expected 7 but got %d", (int) context.position);
	TestCase_assertBytesEqual("aa\x00\x00\x00\x00\x00", context.data, 7);
	
	context.position = 6;
	result = memwrite(&context, 3, "baz");
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 9, "Expected 9 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize >= 9, "Expected >= 9 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 9, "Expected 9 but got %d", (int) context.position);
	TestCase_assertBytesEqual("aa\x00\x00\x00\x00""baz", context.data, 9);
	
	free(context.data);
	
	context = memwriteContextInit(NULL, 0, 0, true);
	result = memwrite(&context, 2, "bb");
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(context.length == 2, "Expected 2 but got %d", (int) context.length);
	TestCase_assert(context.allocatedSize == 2, "Expected 2 but got %d", (int) context.allocatedSize);
	TestCase_assert(context.position == 2, "Expected 2 but got %d", (int) context.position);
	TestCase_assertBytesEqual("bb", context.data, 2);
	
	char * string = "Hello";
	result = memwrite_str(&context, string);
	TestCase_assertBoolTrue(result);
	TestCase_assertSizeEqual(context.length, 7);
	TestCase_assertSizeEqual(context.position, 7);
	TestCase_assertBytesEqual("bbHello", context.data, 7);
	
	result = memwrite_strl(&context, " world");
	TestCase_assertBoolTrue(result);
	TestCase_assertSizeEqual(context.length, 13);
	TestCase_assertSizeEqual(context.position, 13);
	TestCase_assertBytesEqual("bbHello world", context.data, 13);
	
	free(context.data);
}

static void testReadFileSimple() {
	char fileName[10];
	strcpy(fileName, "tmpXXXXXX");
	int fileHandle = mkstemp(fileName);
	close(fileHandle);
	unlink(fileName);
	size_t length;
	void * result = readFileSimple(fileName, &length);
	TestCase_assert(result == NULL, "Expected NULL but got %p", result);
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	FILE * file = fdopen(fileHandle, "wb");
	fprintf(file, "foo bar\nbaz");
	fclose(file);
	result = readFileSimple(fileName, &length);
	unlink(fileName);
	TestCase_assert(result != NULL, "Expected non-NULL but got NULL");
	TestCase_assert(length == 11, "Expected 11 but got %d", (int) length);
	TestCase_assertBytesEqual("foo bar\nbaz", result, 11);
	free(result);
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	file = fdopen(fileHandle, "wb");
	fprintf(file, "Hello, world!");
	fclose(file);
	result = readFileSimple(fileName, &length);
	unlink(fileName);
	TestCase_assert(result != NULL, "Expected non-NULL but got NULL");
	TestCase_assert(length == 13, "Expected 13 but got %d", (int) length);
	TestCase_assertBytesEqual("Hello, world!", result, 13);
	free(result);
}

static void testWriteFileSimple() {
	char fileName[10];
	int fileHandle;
	bool result;
	FILE * file;
	size_t fileLength;
	char * fileContents;
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	result = writeFileSimple(fileName, "foo bar\nbaz", 11);
	file = fdopen(fileHandle, "rb");
	TestCase_assert(file != NULL, "writeFileSimple failed to create target file");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	fclose(file);
	unlink(fileName);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(fileLength == 11, "Expected 11 but got %d", (int) fileLength);
	TestCase_assertBytesEqual("foo bar\nbaz", fileContents, 11);
	free(fileContents);
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	result = writeFileSimple(fileName, "Hello, world!", 13);
	file = fdopen(fileHandle, "rb");
	TestCase_assert(file != NULL, "writeFileSimple failed to create target file");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	fclose(file);
	unlink(fileName);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(fileLength == 13, "Expected 13 but got %d", (int) fileLength);
	TestCase_assertBytesEqual("Hello, world!", fileContents, 13);
	free(fileContents);
}

static void testWriteFileAtomic() {
	char fileName[10];
	int fileHandle;
	bool result;
	FILE * file;
	size_t fileLength;
	char * fileContents;
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	close(fileHandle);
	result = writeFileAtomic(fileName, "foo bar\nbaz", 11);
	file = fopen(fileName, "rb");
	TestCase_assert(file != NULL, "writeFileAtomic failed to create target file");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	fclose(file);
	unlink(fileName);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(fileLength == 11, "Expected 11 but got %d", (int) fileLength);
	TestCase_assertBytesEqual("foo bar\nbaz", fileContents, 11);
	free(fileContents);
	
	strcpy(fileName, "tmpXXXXXX");
	fileHandle = mkstemp(fileName);
	close(fileHandle);
	result = writeFileAtomic(fileName, "Hello, world!", 13);
	file = fopen(fileName, "rb");
	TestCase_assert(file != NULL, "writeFileAtomic failed to create target file");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	fclose(file);
	unlink(fileName);
	TestCase_assert(result, "Expected true but got false");
	TestCase_assert(fileLength == 13, "Expected 13 but got %d", (int) fileLength);
	TestCase_assertBytesEqual("Hello, world!", fileContents, 13);
	free(fileContents);
}

static void testTemporaryFilePath() {
	int fd;
	const char * template, * path;
	const char * uniqueString;
	bool success;
	FILE * file;
	size_t fileLength;
	char * fileContents;
	
	template = "tmpXXXXXX";
	uniqueString = "Unique string 1";
	fd = -1;
	path = temporaryFilePath(template, &fd);
	TestCase_assert(path != NULL, "temporaryFilePath unexpectedly returned NULL");
	TestCase_assert(strlen(path) >= strlen(template), "temporaryFilePath returned a shorter path (\"%s\") than the provided template", path);
	TestCase_assert(!strncmp(path + strlen(path) - strlen(template), template, 3), "Template prefix not preserved in path (\"%s\")", path);
	TestCase_assert(fd != -1, "temporaryFilePath didn't return a file descriptor");
	success = writeFileSimple(path, uniqueString, strlen(uniqueString));
	TestCase_assert(success, "Failed to write to returned path \"%s\"", path);
	file = fdopen(fd, "rb");
	TestCase_assert(file != NULL, "temporaryFilePath didn't return a file descriptor that could be opened with fdopen");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	TestCase_assert(fileLength == strlen(uniqueString), "File length (%d) didn't match length of string written to path (%d)", (int) fileLength, (int) strlen(uniqueString));
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	TestCase_assert(!memcmp(fileContents, uniqueString, fileLength), "File contents \"%.*s\" didn't match expected string \"%s\"", (int) fileLength, fileContents, uniqueString);
	fclose(file);
	unlink(path);
	
	template = "helloXXXXXX";
	uniqueString = "Unique string 2";
	fd = -1;
	path = temporaryFilePath(template, &fd);
	TestCase_assert(path != NULL, "temporaryFilePath unexpectedly returned NULL");
	TestCase_assert(strlen(path) >= strlen(template), "temporaryFilePath returned a shorter path (\"%s\") than the provided template", path);
	TestCase_assert(!strncmp(path + strlen(path) - strlen(template), template, 5), "Template prefix not preserved in path (\"%s\")", path);
	TestCase_assert(fd != -1, "temporaryFilePath didn't return a file descriptor");
	success = writeFileSimple(path, uniqueString, strlen(uniqueString));
	TestCase_assert(success, "Failed to write to returned path \"%s\"", path);
	file = fdopen(fd, "rb");
	TestCase_assert(file != NULL, "temporaryFilePath didn't return a file descriptor that could be opened with fdopen");
	fseek(file, 0, SEEK_END);
	fileLength = ftell(file);
	TestCase_assert(fileLength == strlen(uniqueString), "File length (%d) didn't match length of string written to path (%d)", (int) fileLength, (int) strlen(uniqueString));
	fseek(file, 0, SEEK_SET);
	fileContents = malloc(fileLength);
	fread(fileContents, 1, fileLength, file);
	TestCase_assert(!memcmp(fileContents, uniqueString, fileLength), "File contents \"%.*s\" didn't match expected string \"%s\"", (int) fileLength, fileContents, uniqueString);
	fclose(file);
	unlink(path);
}

static void testFilePathFunctions() {
	const char * path, * result;
	
	path = "abcd";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 4);
	path = "Hello.txt";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 6);
	path = "/path/to/Hello.txt";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 15);
	path = "/test.dir/";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 10);
	path = ".ext";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 1);
	path = "/hello/test.dir/name";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 20);
	
	path = "test.a";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "test");
	path = "test2.txt";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "test2");
	path = "test";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "test");
	path = "/hello/test.dir/name.ext";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "/hello/test.dir/name");
	path = "/hello/test.dir/name";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "/hello/test.dir/name");
	path = "";
	result = getBaseName(path);
	TestCase_assertStringEqual(result, "");
	
	path = "abcd";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path);
	path = "/path/to/Hello.txt";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path + 9);
	path = "local/path/to/a directory/";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path + 14);
	path = "/";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path);
	
	path = "abcd";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "");
	path = "abcd/efgh";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "abcd/");
	path = "/path/to/something";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "/path/to/");
	path = "/path";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "/");
	path = "/";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "/");
	
#if defined(STEM_PLATFORM_windows)
	path = "C:\\path\\to\\Hello.txt";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 17);
	path = "\\test.dir\\";
	result = getFileExtension(path);
	TestCase_assertStringPointerEqual(result, path + 10);
	
	path = "D:\\path\\to\\Hello.txt";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path + 11);
	path = "local\\path\\to\\a directory\\";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path + 14);
	path = "F:\\";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path);
	path = "G:";
	result = getLastPathComponent(path);
	TestCase_assertStringPointerEqual(result, path);
	
	path = "abcd\\efgh";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "abcd\\");
	path = "\\path\\to\\something";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "\\path\\to\\");
	path = "C:\\path\\to\\something";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "C:\\path\\to\\");
	path = "\\path";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "\\");
	path = "D:\\path";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "D:\\");
	path = "\\";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "\\");
	path = "A:\\";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "A:\\");
	path = "a:\\";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "a:\\");
	path = "Z:";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "Z:");
	path = "z:";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "z:");
	path = "D:/";
	result = getDirectory(path);
	TestCase_assertStringEqual(result, "D:/");
#endif
}

static void testEndianSwapping() {
	uint16_t value16;
	uint32_t value32;
	uint64_t value64;
	
	value16 = swapEndian16(0x0100);
	TestCase_assert(value16 == 0x1, "Expected 0x1 but got 0x%X", value16);
	value16 = swapEndian16(0x0203);
	TestCase_assert(value16 == 0x302, "Expected 0x302 but got 0x%X", value16);
	value16 = swapEndian16(0x0001);
	TestCase_assert(value16 == 0x100, "Expected 0x100 but got 0x%X", value16);
	value16 = swapEndian16(0x0302);
	TestCase_assert(value16 == 0x203, "Expected 0x203 but got 0x%X", value16);
	
	value32 = swapEndian32(0x01000000);
	TestCase_assert(value32 == 0x1, "Expected 0x1 but got 0x%X", value32);
	value32 = swapEndian32(0x02030405);
	TestCase_assert(value32 == 0x5040302, "Expected 0x5040302 but got 0x%X", value32);
	value32 = swapEndian32(0x00000001);
	TestCase_assert(value32 == 0x1000000, "Expected 0x1000000 but got 0x%X", value32);
	value32 = swapEndian32(0x05040302);
	TestCase_assert(value32 == 0x2030405, "Expected 0x2030405 but got 0x%X", value32);
	
	value64 = swapEndian64(0x0100000000000000ull);
	TestCase_assert(value64 == 0x1, "Expected 0x1 but got 0x" XINT64_FORMAT, value64);
	value64 = swapEndian64(0x0203040506070809ull);
	TestCase_assert(value64 == 0x908070605040302ull, "Expected 0x908070605040302 but got 0x" XINT64_FORMAT, value64);
	value64 = swapEndian64(0x0000000000000001ull);
	TestCase_assert(value64 == 0x100000000000000ull, "Expected 0x100000000000000 but got 0x" XINT64_FORMAT, value64);
	value64 = swapEndian64(0x0908070605040302ull);
	TestCase_assert(value64 == 0x203040506070809ull, "Expected 0x203040506070809 but got 0x" XINT64_FORMAT, value64);
}

static void vsnprintfTest(char * string, size_t size, const char * format, ...) {
	va_list args;
	
	va_start(args, format);
	vsnprintf_safe(string, size, format, args);
	va_end(args);
}

static void testSafeStringFunctions() {
	char string[16];
	
	string[15] = 0;
	
	memset(string, '*', 15);
	snprintf_safe(string, 14, "Hello, %s!", "world");
	TestCase_assertStringEqual(string, "Hello, world!");
	
	memset(string, '*', 15);
	snprintf_safe(string, 6, "Hello, %s!", "world");
	TestCase_assertStringEqual(string, "Hello");
	
	memset(string, '*', 15);
	vsnprintfTest(string, 14, "Hello, %s!", "world");
	TestCase_assertStringEqual(string, "Hello, world!");
	
	memset(string, '*', 15);
	vsnprintfTest(string, 6, "Hello, %s!", "world");
	TestCase_assertStringEqual(string, "Hello");
	
	memset(string, '*', 15);
	string[2] = 0;
	snprintf_append(string, 15, "%d", 1);
	TestCase_assertStringEqual(string, "**1");
	
	memset(string, '*', 15);
	string[3] = 0;
	snprintf_append(string, 15, "%d", 20);
	TestCase_assertStringEqual(string, "***20");
	
	memset(string, '*', 15);
	string[3] = 0;
	snprintf_append(string, 5, "%d", 20);
	TestCase_assertStringEqual(string, "***2");
	
	memset(string, '*', 15);
	strncpy_safe(string, "Hello, world!", 14);
	TestCase_assertStringEqual(string, "Hello, world!");
	
	memset(string, '*', 15);
	strncpy_safe(string, "Hello, world!", 6);
	TestCase_assertStringEqual(string, "Hello");
	
	char * duplicatedString;
	duplicatedString = strdup_nullSafe(NULL);
	TestCase_assertPointerNULL(duplicatedString);
	duplicatedString = strdup_nullSafe(string);
	TestCase_assertPointerNonNULL(duplicatedString);
	TestCase_assertPointerUnequal(duplicatedString, (char *) string);
	TestCase_assertStringEqual(duplicatedString, "Hello");
	free(duplicatedString);
	
	int cmpResult = strcmp_nullSafe(NULL, NULL);
	TestCase_assertIntEqual(cmpResult, 0);
	cmpResult = strcmp_nullSafe(NULL, "a");
	TestCase_assertIntEqual(cmpResult, -1);
	cmpResult = strcmp_nullSafe("a", NULL);
	TestCase_assertIntEqual(cmpResult, 1);
	cmpResult = strcmp_nullSafe("a", "a");
	TestCase_assertIntEqual(cmpResult, 0);
	cmpResult = strcmp_nullSafe("a", "b");
	TestCase_assertIntEqual(cmpResult, -1);
	cmpResult = strcmp_nullSafe("b", "a");
	TestCase_assertIntEqual(cmpResult, 1);
	
	char unterminated[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
	duplicatedString = strdup_length(unterminated, 3);
	TestCase_assertStringEqual(duplicatedString, "abc");
	free(duplicatedString);
	duplicatedString = strdup_length(unterminated, 6);
	TestCase_assertStringEqual(duplicatedString, "abcdef");
	free(duplicatedString);
}

static void testMemdup(void) {
	char bytes[4] = {1, 3, 5, 2};
	char * result;
	
	result = memdup(bytes, 0);
	TestCase_assertPointerNULL(result);
	
	result = memdup(bytes, 2);
	TestCase_assertPointerNonNULL(result);
	TestCase_assertPointerUnequal(result, (char *) bytes);
	TestCase_assert(!memcmp(result, bytes, 2), "Expected {1, 3} but got {%d, %d}", bytes[0], bytes[1]);
	free(result);
	
	result = memdup(bytes, 4);
	TestCase_assertPointerNonNULL(result);
	TestCase_assertPointerUnequal(result, (char *) bytes);
	TestCase_assert(!memcmp(result, bytes, 4), "Expected {1, 3, 5, 2} but got {%d, %d, %d, %d}", bytes[0], bytes[1], bytes[2], bytes[3]);
	free(result);
}

static void testMemappend(void) {
	char buffer[12];
	memset(buffer, 0, sizeof(buffer));
	size_t result = memappend(buffer, 0, "abcdef", 5);
	TestCase_assertSizeEqual(result, 5);
	TestCase_assertBytesEqual(buffer, "abcde", 6);
	
	result = memappend(buffer, 5, "fghi", 4);
	TestCase_assertSizeEqual(result, 9);
	TestCase_assertBytesEqual(buffer, "abcdefghi", 10);
	
	const char * string = "Hello";
	result = memappend_str(buffer, 2, string);
	TestCase_assertSizeEqual(result, 7);
	TestCase_assertBytesEqual(buffer, "abHellohi", 10);
	
	result = memappend_strl(buffer, 2, "world");
	TestCase_assertSizeEqual(result, 7);
	TestCase_assertBytesEqual(buffer, "abworldhi", 10);
}

static void testMemnappend(void) {
	char buffer[12];
	memset(buffer, 0, sizeof(buffer));
	size_t result = memnappend(buffer, 0, sizeof(buffer), "abcdef", 5);
	TestCase_assertSizeEqual(result, 5);
	TestCase_assertBytesEqual(buffer, "abcde", 6);
	
	result = memnappend(buffer, 5, sizeof(buffer), "fghi", 4);
	TestCase_assertSizeEqual(result, 9);
	TestCase_assertBytesEqual(buffer, "abcdefghi", 10);
	
	const char * string = "Hello";
	result = memnappend_str(buffer, 2, sizeof(buffer), string);
	TestCase_assertSizeEqual(result, 7);
	TestCase_assertBytesEqual(buffer, "abHellohi", 10);
	
	result = memnappend_strl(buffer, 2, sizeof(buffer), "world");
	TestCase_assertSizeEqual(result, 7);
	TestCase_assertBytesEqual(buffer, "abworldhi", 10);
	
	result = memnappend(buffer, 1, 2, "XX", 1);
	TestCase_assertSizeEqual(result, 2);
	TestCase_assertBytesEqual(buffer, "aXw", 3);
	
	result = memnappend(buffer, 2, 1, "hi", 2);
	TestCase_assertSizeEqual(result, 1);
	TestCase_assertBytesEqual(buffer, "aXw", 3);
	
	result = memnappend_str(buffer, 1, 2, string);
	TestCase_assertSizeEqual(result, 2);
	TestCase_assertBytesEqual(buffer, "aHw", 3);
	
	result = memnappend_strl(buffer, 1, 2, "zz");
	TestCase_assertSizeEqual(result, 2);
	TestCase_assertBytesEqual(buffer, "azw", 3);
	
	result = memnterminate(buffer, 2, 3);
	TestCase_assertSizeEqual(result, 2);
	TestCase_assertBytesEqual(buffer, "az", 3);
	result = memnterminate(buffer, 2, 2);
	TestCase_assertSizeEqual(result, 1);
	TestCase_assertBytesEqual(buffer, "a", 2);
	buffer[1] = ';';
	result = memnterminate(buffer, 1, 2);
	TestCase_assertSizeEqual(result, 1);
	TestCase_assertBytesEqual(buffer, "a", 2);
	
	result = memnappend_snprintf(buffer, 1, 5, "%u", 123);
	TestCase_assertSizeEqual(result, 4);
	TestCase_assertBytesEqual(buffer, "a123", 5);
	
	result = memnappend_snprintf(buffer, 1, 4, "%u", 456);
	TestCase_assertSizeEqual(result, 3);
	TestCase_assertBytesEqual(buffer, "a45", 4);
	
	result = memnappend_double(buffer, 3, 6, 1.625, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertSizeEqual(result, 6);
	TestCase_assertBytesEqual(buffer, "a451.6", 6);
	
	result = memnappend_double(buffer, 1, 6, 2.0, 2, DECIMAL_PRECISION_MAX);
	TestCase_assertSizeEqual(result, 5);
	TestCase_assertBytesEqual(buffer, "a2.00", 5);
	
	result = memnappend_integer(buffer, 2, 6, 123456);
	TestCase_assertSizeEqual(result, 6);
	TestCase_assertBytesEqual(buffer, "a21234", 6);
	
	result = memnappend_integer(buffer, 1, 6, 999);
	TestCase_assertSizeEqual(result, 4);
	TestCase_assertBytesEqual(buffer, "a99934", 6);
}

static void testStrnlen(void) {
	size_t length = strnlen("Hello", 10);
	TestCase_assertSizeEqual(length, 5);
	length = strnlen("Hello", 4);
	TestCase_assertSizeEqual(length, 4);
	length = strnlen("", 0);
	TestCase_assertSizeEqual(length, 0);
	length = strnlen("Hello world", 4);
	TestCase_assertSizeEqual(length, 4);
	length = strnlen("Hello world", 11);
	TestCase_assertSizeEqual(length, 11);
	length = strnlen("Hello world", 500);
	TestCase_assertSizeEqual(length, 11);
}

static void testLexstrcmp(void) {
	TestCase_assertIntEqual(lexstrcmp("a", "b"), -1);
	TestCase_assertIntEqual(lexstrcmp("b", "b"), 0);
	TestCase_assertIntEqual(lexstrcmp("b", "a"), 1);
	TestCase_assertIntEqual(lexstrcmp("Test 5", "Test 6"), -1);
	TestCase_assertIntEqual(lexstrcmp("Test 5", "Test 10"), -1);
	TestCase_assertIntEqual(lexstrcmp("a200b4", "a39b9000"), 1);
	TestCase_assertIntEqual(lexstrcmp("a200b4", "a200b4"), 0);
	TestCase_assertIntEqual(lexstrcmp("a200b4", "a200b"), 1);
	TestCase_assertIntEqual(lexstrcmp("a200b", "a200b4"), -1);
	TestCase_assertIntEqual(lexstrcmp("a1", "ab"), -1);
	TestCase_assertIntEqual(lexstrcmp("ab", "a1"), 1);
	TestCase_assertIntEqual(lexstrcmp("a001", "a000"), 1);
	TestCase_assertIntEqual(lexstrcmp("a000", "a001"), -1);
	TestCase_assertIntEqual(lexstrcmp("a002", "a2"), 1);
	TestCase_assertIntEqual(lexstrcmp("a2", "a002"), -1);
	TestCase_assertIntEqual(lexstrcmp("a002", "a1"), 1);
	TestCase_assertIntEqual(lexstrcmp("a1", "a002"), -1);
	TestCase_assertIntEqual(lexstrcmp("a002", "a3"), -1);
	TestCase_assertIntEqual(lexstrcmp("a3", "a002"), 1);
	TestCase_assertIntEqual(lexstrcmp("a00b", "a00b"), 0);
	TestCase_assertIntEqual(lexstrcmp("a000b", "a00b"), 1);
	TestCase_assertIntEqual(lexstrcmp("a00b", "a000b"), -1);
	TestCase_assertIntEqual(lexstrcmp("Ab", "aB"), 0);
}

static void testPrintHexString() {
	char buffer[32];
	char hex1[] = {0x00, 0x01, 0x02, 0x03}, hex2[] = "Hello";
	char * result;
	
	buffer[31] = 0;
	
	result = printHexString(hex1, sizeof(hex1), buffer, 32);
	TestCase_assert(result == buffer, "Expected %p but got %p", buffer, result);
	TestCase_assert(!strcmp(buffer, "00 01 02 03"), "Expected \"00 01 02 03\" but got \"%s\"", buffer);
	
	result = printHexString(hex2, sizeof(hex2), buffer, 32);
	TestCase_assert(result == buffer, "Expected %p but got %p", buffer, result);
	TestCase_assert(!strcmp(buffer, "48 65 6C 6C 6F 00"), "Expected \"48 65 6C 6C 6F 00\" but got \"%s\"", buffer);
	
	result = printHexString(hex2, sizeof(hex2), buffer, 5);
	TestCase_assert(result == buffer, "Expected %p but got %p", buffer, result);
	TestCase_assert(!strcmp(buffer, "48 6"), "Expected \"48 6\" but got \"%s\"", buffer);
	
	result = printHexString(hex1, 0, buffer, 32);
	TestCase_assert(result == buffer, "Expected %p but got %p", buffer, result);
	TestCase_assert(!strcmp(buffer, ""), "Expected \"\" but got \"%s\"", buffer);
}

static void testFormatDecimalString(void) {
	char result[DECIMAL_STRING_MAX];
	unsigned int length;
	length = formatDecimalString(1, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "1");
	TestCase_assertUIntEqual(length, 1);
	
	length = formatDecimalString(-243.25, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "-243.25");
	TestCase_assertUIntEqual(length, 7);
	
	length = formatDecimalString(0.0000152587890625, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "0.0000152587890625");
	TestCase_assertUIntEqual(length, 18);
	
	length = formatDecimalString(0.0000152587890625, result, 8, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "0.00001");
	TestCase_assertUIntEqual(length, 7);
	
	length = formatDecimalString(INFINITY, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "inf");
	TestCase_assertUIntEqual(length, 3);
	
	length = formatDecimalString(-INFINITY, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "-inf");
	TestCase_assertUIntEqual(length, 4);
	
	length = formatDecimalString(NAN, result, sizeof(result), 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "nan");
	TestCase_assertUIntEqual(length, 3);
	
	length = formatDecimalString(2, result, sizeof(result), 4, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "2.0000");
	TestCase_assertUIntEqual(length, 6);
	
	length = formatDecimalString(2.0625, result, sizeof(result), 0, 2);
	TestCase_assertStringEqual(result, "2.06");
	TestCase_assertUIntEqual(length, 4);
	
	length = formatDecimalString(1.5, result, 0, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "2.06");
	TestCase_assertUIntEqual(length, 0);
	
	length = formatDecimalString(1.5, result, 1, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "");
	TestCase_assertUIntEqual(length, 0);
	
	length = formatDecimalString(1.5, result, 2, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "1");
	TestCase_assertUIntEqual(length, 1);
	
	length = formatDecimalString(1.5, result, 3, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "1.");
	TestCase_assertUIntEqual(length, 2);
	
	length = formatDecimalString(1.5, result, 4, 0, DECIMAL_PRECISION_MAX);
	TestCase_assertStringEqual(result, "1.5");
	TestCase_assertUIntEqual(length, 3);
}

static void testParseDecimalString(void) {
	unsigned int lengthRead = 0;
	double result = 1.0;
	bool success = parseDecimalString(&result, "0", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 0.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 1);
	
	success = parseDecimalString(&result, "aaaa1", UINT_MAX, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalString(&result, "1.5", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 1.5);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalString(&result, "-5.0jjj", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, -5.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 4);
	
	success = parseDecimalString(&result, "123.45678", 3, &lengthRead);
	TestCase_assertDoubleEqual(result, 123.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalString(&result, "0.0000152587890625", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 0.0000152587890625);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 18);
	
	success = parseDecimalString(&result, "inf", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, INFINITY);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalString(&result, "-inf", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, -INFINITY);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 4);
	
	success = parseDecimalString(&result, "nan", UINT_MAX, &lengthRead);
	TestCase_assertBoolTrue(isnan(result));
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalString(&result, "1e+2", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 100.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 4);
	
	success = parseDecimalString(&result, "5e-3", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 0.005);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 4);
	
	success = parseDecimalString(&result, "2e1", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 20.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalString(&result, "0.50000000000000000000000000000000", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 0.5);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 34);
	
	success = parseDecimalString(&result, "", 0, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalString(&result, "000123", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, 123.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 6);
	
	success = parseDecimalString(&result, "-002", UINT_MAX, &lengthRead);
	TestCase_assertDoubleEqual(result, -2.0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 4);
}

static void testFormatDecimalIntegerString(void) {
	char result[DECIMAL_INTEGER_STRING_MAX];
	unsigned int length;
	length = formatDecimalIntegerString(0, result, sizeof(result));
	TestCase_assertStringEqual(result, "0");
	TestCase_assertUIntEqual(length, 1);
	
	length = formatDecimalIntegerString(1, result, sizeof(result));
	TestCase_assertStringEqual(result, "1");
	TestCase_assertUIntEqual(length, 1);
	
	length = formatDecimalIntegerString(-243, result, sizeof(result));
	TestCase_assertStringEqual(result, "-243");
	TestCase_assertUIntEqual(length, 4);
	
	length = formatDecimalIntegerString(9223372036854775807ll, result, sizeof(result));
	TestCase_assertStringEqual(result, "9223372036854775807");
	TestCase_assertUIntEqual(length, 19);
	
	length = formatDecimalIntegerString(9223372036854775806ll, result, sizeof(result));
	TestCase_assertStringEqual(result, "9223372036854775806");
	TestCase_assertUIntEqual(length, 19);
	
	length = formatDecimalIntegerString(INT64_MIN, result, sizeof(result));
	TestCase_assertStringEqual(result, "-9223372036854775808");
	TestCase_assertUIntEqual(length, 20);
	
	length = formatDecimalIntegerString(-9223372036854775807ll, result, sizeof(result));
	TestCase_assertStringEqual(result, "-9223372036854775807");
	TestCase_assertUIntEqual(length, 20);
	
	length = formatDecimalIntegerString(123456789, result, 5);
	TestCase_assertStringEqual(result, "1234");
	TestCase_assertUIntEqual(length, 4);
	
	length = formatDecimalIntegerString(5678, result, 0);
	TestCase_assertStringEqual(result, "1234");
	TestCase_assertUIntEqual(length, 0);
	
	length = formatDecimalIntegerString(5678, result, 1);
	TestCase_assertStringEqual(result, "");
	TestCase_assertUIntEqual(length, 0);
	
	length = formatDecimalIntegerString(5678, result, 2);
	TestCase_assertStringEqual(result, "5");
	TestCase_assertUIntEqual(length, 1);
	
	length = formatDecimalIntegerString(5678, result, 3);
	TestCase_assertStringEqual(result, "56");
	TestCase_assertUIntEqual(length, 2);
	
	length = formatDecimalIntegerString(5678, result, 4);
	TestCase_assertStringEqual(result, "567");
	TestCase_assertUIntEqual(length, 3);
}

static void testParseDecimalIntegerString(void) {
	unsigned int lengthRead = 0;
	int64_t result = 1;
	bool success = parseDecimalIntegerString(&result, "0", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, 0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 1);
	
	success = parseDecimalIntegerString(&result, "aaaa1", UINT_MAX, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalIntegerString(&result, "1.5", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, 1);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 1);
	
	success = parseDecimalIntegerString(&result, "-5jjj", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, -5);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 2);
	
	success = parseDecimalIntegerString(&result, "12345678", 3, &lengthRead);
	TestCase_assertInt64Equal(result, 123);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalIntegerString(&result, "9223372036854775808", UINT_MAX, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalIntegerString(&result, "9223372036854775807", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, 9223372036854775807ll);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 19);
	
	success = parseDecimalIntegerString(&result, "9223372036854775806", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, 9223372036854775806ll);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 19);
	
	success = parseDecimalIntegerString(&result, "-9223372036854775809", UINT_MAX, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalIntegerString(&result, "-9223372036854775808", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, INT64_MIN);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 20);
	
	success = parseDecimalIntegerString(&result, "-9223372036854775807", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, -9223372036854775807ll);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 20);
	
	success = parseDecimalIntegerString(&result, "", 0, &lengthRead);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(lengthRead, 0);
	
	success = parseDecimalIntegerString(&result, "005", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, 5);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 3);
	
	success = parseDecimalIntegerString(&result, "-00023", UINT_MAX, &lengthRead);
	TestCase_assertInt64Equal(result, -23);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(lengthRead, 6);
}

static void testCreateIdentifierFromDisplayString(void) {
	char * result;
	
	result = createIdentifierFromDisplayString("", 40);
	TestCase_assertStringEqual(result, "");
	free(result);
	
	result = createIdentifierFromDisplayString("hello", 40);
	TestCase_assertStringEqual(result, "hello");
	free(result);
	
	result = createIdentifierFromDisplayString("HeLlO", 40);
	TestCase_assertStringEqual(result, "hello");
	free(result);
	
	result = createIdentifierFromDisplayString("I'm going to be an identifier", 40);
	TestCase_assertStringEqual(result, "i_m_going_to_be_an_identifier");
	free(result);
	
	result = createIdentifierFromDisplayString("i_m_already_an_identifier_123", 40);
	TestCase_assertStringEqual(result, "i_m_already_an_identifier_123");
	free(result);
	
	result = createIdentifierFromDisplayString("  space    space  space     ", 40);
	TestCase_assertStringEqual(result, "space_space_space");
	free(result);
	
	result = createIdentifierFromDisplayString("`~!@#$%^&*()-=+[{]}\\|;:'\",<.>/?", 40);
	TestCase_assertStringEqual(result, "");
	free(result);
	
	result = createIdentifierFromDisplayString("too long", 5);
	TestCase_assertStringEqual(result, "too_l");
	free(result);
	
	result = createIdentifierFromDisplayString("_underscore_", 40);
	TestCase_assertStringEqual(result, "_underscore_");
	free(result);
	
	result = createIdentifierFromDisplayString("@nounderscore@", 40);
	TestCase_assertStringEqual(result, "nounderscore");
	free(result);
}

static void testGetFileExtension(void) {
	const char * result = getFileExtension("/a/b/c.d");
	TestCase_assertStringEqual(result, "d");
	result = getFileExtension("a/b/ccc.ddd.ee");
	TestCase_assertStringEqual(result, "ee");
	result = getFileExtension("/abc/def/ghi");
	TestCase_assertStringEqual(result, "");
	result = getFileExtension("/abc/de.f/ghi");
	TestCase_assertStringEqual(result, "");
	result = getFileExtension("hello.txt");
	TestCase_assertStringEqual(result, "txt");
	result = getFileExtension("");
	TestCase_assertStringEqual(result, "");
#ifdef WIN32
	result = getFileExtension("C:\\a\\b\\c.d");
	TestCase_assertStringEqual(result, "d");
	result = getFileExtension("D:\\a\\b\\ccc.ddd.ee");
	TestCase_assertStringEqual(result, "ee");
	result = getFileExtension("C:\\abc\\def\\ghi");
	TestCase_assertStringEqual(result, "");
	result = getFileExtension("C:\\abc\\de.f\\ghi");
	TestCase_assertStringEqual(result, "");
#endif
}

static void testGetLastPathComponent(void) {
	const char * result = getLastPathComponent("/a/b/c.d");
	TestCase_assertStringEqual(result, "c.d");
	result = getLastPathComponent("/abc/def/");
	TestCase_assertStringEqual(result, "def/");
	result = getLastPathComponent("hello");
	TestCase_assertStringEqual(result, "hello");
	result = getLastPathComponent("/");
	TestCase_assertStringEqual(result, "/");
	result = getLastPathComponent("");
	TestCase_assertStringEqual(result, "");
#ifdef WIN32
	result = getLastPathComponent("\\a\\b\\c.d");
	TestCase_assertStringEqual(result, "c.d");
	result = getLastPathComponent("\\abc\\def\\");
	TestCase_assertStringEqual(result, "def\\");
	result = getLastPathComponent("hello");
	TestCase_assertStringEqual(result, "hello");
	result = getLastPathComponent("\\");
	TestCase_assertStringEqual(result, "\\");
#endif
}

static void testGetDirectory(void) {
	const char * result = getDirectory("/a/b/c.d");
	TestCase_assertStringEqual(result, "/a/b/");
	result = getDirectory("abc/def");
	TestCase_assertStringEqual(result, "abc/");
	result = getDirectory("hello");
	TestCase_assertStringEqual(result, "");
	result = getDirectory("/");
	TestCase_assertStringEqual(result, "/");
	result = getDirectory("");
	TestCase_assertStringEqual(result, "");
#ifdef WIN32
	result = getDirectory("\\a\\b\\c.d");
	TestCase_assertStringEqual(result, "\\a\\b\\");
	result = getDirectory("abc\\def");
	TestCase_assertStringEqual(result, "abc\\");
	result = getDirectory("hello");
	TestCase_assertStringEqual(result, "");
	result = getDirectory("\\");
	TestCase_assertStringEqual(result, "\\");
	
	result = getDirectory("C:\\a\\b\\c.d");
	TestCase_assertStringEqual(result, "C:\\a\\b\\");
	result = getDirectory("C:\\a");
	TestCase_assertStringEqual(result, "C:\\");
	result = getDirectory("C:\\");
	TestCase_assertStringEqual(result, "C:\\");
	result = getDirectory("C:");
	TestCase_assertStringEqual(result, "C:");
#endif
}

static void testGetBaseName(void) {
	const char * result = getBaseName("abc.def");
	TestCase_assertStringEqual(result, "abc");
	result = getBaseName("aa.bb.cc");
	TestCase_assertStringEqual(result, "aa.bb");
	result = getBaseName("/aa/aa.bb.cc");
	TestCase_assertStringEqual(result, "/aa/aa.bb");
	result = getBaseName("/aa.bb/cc");
	TestCase_assertStringEqual(result, "/aa.bb/cc");
	result = getBaseName("/a/b");
	TestCase_assertStringEqual(result, "/a/b");
	result = getBaseName("");
	TestCase_assertStringEqual(result, "");
#ifdef WIN32
	result = getBaseName("\\aa\\aa.bb.cc");
	TestCase_assertStringEqual(result, "\\aa\\aa.bb");
	result = getBaseName("\\aa.bb\\cc");
	TestCase_assertStringEqual(result, "\\aa.bb\\cc");
	result = getBaseName("\\a\\b");
	TestCase_assertStringEqual(result, "\\a\\b");
#endif
}

static void testGetSiblingFilePath(void) {
	const char * result = getSiblingFilePath("/a/b/c", "d");
	TestCase_assertStringEqual(result, "/a/b/d");
	result = getSiblingFilePath("aa/bb", "hello");
	TestCase_assertStringEqual(result, "aa/hello");
	result = getSiblingFilePath("aa/bb", "");
	TestCase_assertStringEqual(result, "aa/");
	result = getSiblingFilePath("", "a");
	TestCase_assertStringEqual(result, "a");
	result = getSiblingFilePath("", "");
	TestCase_assertStringEqual(result, "");
#ifdef WIN32
	result = getSiblingFilePath("\\a\\b\\c", "d");
	TestCase_assertStringEqual(result, "\\a\\b\\d");
	result = getSiblingFilePath("aa\\bb", "hello");
	TestCase_assertStringEqual(result, "aa\\hello");
	result = getSiblingFilePath("aa\\bb", "");
	TestCase_assertStringEqual(result, "aa\\");
#endif
}

static void testGetPathRelativeTo(void) {
	const char * result = getPathRelativeTo("/a/", "/a/c");
	TestCase_assertStringEqual(result, "c");
	result = getPathRelativeTo("/a/b/", "/a/c");
	TestCase_assertStringEqual(result, "../c");
	result = getPathRelativeTo("a/b/", "a/c");
	TestCase_assertStringEqual(result, "../c");
	result = getPathRelativeTo("a/b/c/", "a/d/e");
	TestCase_assertStringEqual(result, "../../d/e");
	result = getPathRelativeTo("/base/subdir/subdir2", "/base/subdir/subdir2/file");
	TestCase_assertStringEqual(result, "subdir2/file");
	result = getPathRelativeTo("/base/subdir/subdir2/", "/base/subdir/subdir2/file");
	TestCase_assertStringEqual(result, "file");
#ifdef WIN32
	result = getPathRelativeTo("\\a\\", "\\a\\c");
	TestCase_assertStringEqual(result, "c");
	result = getPathRelativeTo("\\a\\b\\", "\\a\\c");
	TestCase_assertStringEqual(result, "../c");
	result = getPathRelativeTo("a\\b\\", "a\\c");
	TestCase_assertStringEqual(result, "../c");
	result = getPathRelativeTo("a\\b\\c\\", "a\\d\\e");
	TestCase_assertStringEqual(result, "../../d\\e");
	result = getPathRelativeTo("\\base\\subdir\\subdir2", "\\base\\subdir\\subdir2\\file");
	TestCase_assertStringEqual(result, "subdir2\\file");
	result = getPathRelativeTo("\\base\\subdir\\subdir2\\", "\\base\\subdir\\subdir2\\file");
	TestCase_assertStringEqual(result, "file");
#endif
}

static void testGetAbsolutePath(void) {
	const char * result = getAbsolutePath("/a/", "b");
	TestCase_assertStringEqual(result, "/a/b");
	result = getAbsolutePath("/a/b", "c");
	TestCase_assertStringEqual(result, "/a/c");
	result = getAbsolutePath("/a/b", "/d/c");
	TestCase_assertStringEqual(result, "/d/c");
	result = getAbsolutePath("/a/b/", "../e/f");
	TestCase_assertStringEqual(result, "/a/e/f");
	result = getAbsolutePath("/a/b/", "../../e/f");
	TestCase_assertStringEqual(result, "/e/f");
	result = getAbsolutePath("/a/b/", "../../../e/f");
	TestCase_assertStringEqual(result, "/e/f");
	result = getAbsolutePath("/i/", "./g");
	TestCase_assertStringEqual(result, "/i/g");
	result = getAbsolutePath("/base/subdir/subdir/", "../r/../g");
	TestCase_assertStringEqual(result, "/base/subdir/g");
	result = getAbsolutePath("/base/subdir/subdir2/", "..");
	TestCase_assertStringEqual(result, "/base/subdir/");
	result = getAbsolutePath("/a/b/", "/q/../r/");
	TestCase_assertStringEqual(result, "/r/");
#ifdef WIN32
	result = getAbsolutePath("\\a\\", "b");
	TestCase_assertStringEqual(result, "\\a\\b");
	result = getAbsolutePath("\\a\\b", "c");
	TestCase_assertStringEqual(result, "\\a\\c");
	result = getAbsolutePath("C:\\a\\b", "C:\\d\\c");
	TestCase_assertStringEqual(result, "C:\\d\\c");
	result = getAbsolutePath("C:\\a\\b", "\\d\\c");
	TestCase_assertStringEqual(result, "\\d\\c");
	result = getAbsolutePath("\\a\\b\\", "../e\\f");
	TestCase_assertStringEqual(result, "\\a\\e\\f");
	result = getAbsolutePath("\\a\\b\\", "../../e\\f");
	TestCase_assertStringEqual(result, "\\e\\f");
	result = getAbsolutePath("\\a\\b\\", "../../../e\\f");
	TestCase_assertStringEqual(result, "\\e\\f");
	result = getAbsolutePath("\\i\\", ".\\g");
	TestCase_assertStringEqual(result, "\\i\\g");
	result = getAbsolutePath("\\base\\subdir\\subdir\\", "..\\r\\..\\g");
	TestCase_assertStringEqual(result, "\\base\\subdir\\g");
	result = getAbsolutePath("\\base\\subdir\\subdir2\\", "..");
	TestCase_assertStringEqual(result, "\\base\\subdir\\");
	result = getAbsolutePath("\\a\\b\\", "\\q\\..\\r\\");
	TestCase_assertStringEqual(result, "\\r\\");
#endif
}

static void testIsAbsolutePath(void) {
	TestCase_assertBoolTrue(isAbsolutePath("/"));
	TestCase_assertBoolTrue(isAbsolutePath("/a/b"));
	TestCase_assertBoolFalse(isAbsolutePath("a/b"));
	TestCase_assertBoolFalse(isAbsolutePath("a"));
	TestCase_assertBoolFalse(isAbsolutePath(""));
#ifdef WIN32
	TestCase_assertBoolTrue(isAbsolutePath("\\"));
	TestCase_assertBoolTrue(isAbsolutePath("\\a\\b"));
	TestCase_assertBoolFalse(isAbsolutePath("a\\b"));
	TestCase_assertBoolTrue(isAbsolutePath("a:"));
	TestCase_assertBoolTrue(isAbsolutePath("C:"));
	TestCase_assertBoolTrue(isAbsolutePath("C:\\"));
	TestCase_assertBoolTrue(isAbsolutePath("C:/"));
	TestCase_assertBoolTrue(isAbsolutePath("C:/a"));
	TestCase_assertBoolTrue(isAbsolutePath("C:\\a"));
	TestCase_assertBoolFalse(isAbsolutePath("C/a"));
	TestCase_assertBoolFalse(isAbsolutePath("C\\a"));
#endif
}

struct enumerateFilesContext {
	unsigned int fileCount;
	unsigned int fileCountMax;
	char ** fileNames;
	const char * expectedDirectoryPath;
};

static bool enumerateDirectoryCallback(const char * directoryPath, const char * fileName, void * context) {
	struct enumerateFilesContext * contextStruct = context;
	TestCase_assertStringEqual(directoryPath, contextStruct->expectedDirectoryPath);
	contextStruct->fileNames[contextStruct->fileCount++] = strdup(fileName);
	return contextStruct->fileCount < contextStruct->fileCountMax;
}

static void testEnumerateFilesInDirectory(void) {
	mkdir("testdir1"
#ifndef WIN32
	, 0777
#endif
	);
	fclose(fopen("testdir1/testfile1", "w"));
	mkdir("testdir2"
#ifndef WIN32
	, 0777
#endif
	);
	fclose(fopen("testdir2/a", "w"));
	fclose(fopen("testdir2/b", "w"));
	
	char * fileNames[3] = {NULL, NULL, NULL};
	struct enumerateFilesContext contextStruct = {0, 3, fileNames, "testdir1"};
	enumerateFilesInDirectory("testdir1", false, enumerateDirectoryCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.fileCount, 1);
	TestCase_assertStringEqual(contextStruct.fileNames[0], "testfile1");
	free(contextStruct.fileNames[0]);
	contextStruct.fileNames[0] = NULL;
	
	contextStruct.fileCount = 0;
	contextStruct.expectedDirectoryPath = "testdir2";
	enumerateFilesInDirectory("testdir2", false, enumerateDirectoryCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.fileCount, 2);
	if (!strcmp(contextStruct.fileNames[0], "a")) {
		TestCase_assertStringEqual(contextStruct.fileNames[0], "a");
		TestCase_assertStringEqual(contextStruct.fileNames[1], "b");
	} else {
		TestCase_assertStringEqual(contextStruct.fileNames[0], "b");
		TestCase_assertStringEqual(contextStruct.fileNames[1], "a");
	}
	free(contextStruct.fileNames[0]);
	free(contextStruct.fileNames[1]);
	contextStruct.fileNames[0] = NULL;
	contextStruct.fileNames[1] = NULL;
	
	contextStruct.fileCount = 0;
	contextStruct.fileCountMax = 1;
	enumerateFilesInDirectory("testdir2", false, enumerateDirectoryCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.fileCount, 1);
	free(contextStruct.fileNames[0]);
}

static void testPercentEncode(void) {
	char encodedString[768];
	size_t length = percentEncode(NULL, 0, "a%b", 3);
	TestCase_assertSizeEqual(length, 5);
	length = percentEncode(encodedString, sizeof(encodedString), "a%b", 3);
	TestCase_assertSizeEqual(length, 5);
	TestCase_assertBytesEqual(encodedString, "a%25b", 5);
	
	length = percentEncode(NULL, 0, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	TestCase_assertSizeEqual(length, 66);
	length = percentEncode(encodedString, sizeof(encodedString), "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	TestCase_assertSizeEqual(length, 66);
	TestCase_assertBytesEqual(encodedString, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	
	length = percentEncode(NULL, 0, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
	                                "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
	                                "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
	                                "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
	                                "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
	                                "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F"
	                                "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
	                                "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"
	                                "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
	                                "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
	                                "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
	                                "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
	                                "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
	                                "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
	                                "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
	                                "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 256);
	TestCase_assertSizeEqual(length, 636);
	length = percentEncode(encodedString, sizeof(encodedString), "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
	                                                             "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
	                                                             "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
	                                                             "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
	                                                             "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
	                                                             "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F"
	                                                             "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
	                                                             "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"
	                                                             "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
	                                                             "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
	                                                             "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
	                                                             "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
	                                                             "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
	                                                             "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
	                                                             "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
	                                                             "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 256);
	TestCase_assertSizeEqual(length, 636);
	TestCase_assertBytesEqual(encodedString, "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"
	                                         "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"
	                                         "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F"
	                                         "0123456789%3A%3B%3C%3D%3E%3F"
	                                         "%40ABCDEFGHIJKLMNO"
	                                         "PQRSTUVWXYZ%5B%5C%5D%5E_"
	                                         "%60abcdefghijklmno"
	                                         "pqrstuvwxyz%7B%7C%7D~%7F"
	                                         "%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F"
	                                         "%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F"
	                                         "%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF"
	                                         "%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF"
	                                         "%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF"
	                                         "%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF"
	                                         "%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF"
	                                         "%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF", 636);
	
	encodedString[5] = '?';
	length = percentEncode(encodedString, 5, "0123456789", 10);
	TestCase_assertSizeEqual(length, 5);
	TestCase_assertBytesEqual(encodedString, "01234?", 6);
	length = percentEncode(encodedString, 5, "%%%", 3);
	TestCase_assertSizeEqual(length, 5);
	TestCase_assertBytesEqual(encodedString, "%25%2?", 6);
	
	char * encodedString2 = percentEncodeString("A%B%C", NULL);
	TestCase_assertStringEqual(encodedString2, "A%25B%25C");
	free(encodedString2);
	encodedString2 = percentEncodeString("1*2:3.", &length);
	TestCase_assertStringEqual(encodedString2, "1%2A2%3A3.");
	TestCase_assertSizeEqual(length, 10);
	free(encodedString2);
}

static void testPercentDecode(void) {
	char decodedString[256];
	size_t length = percentDecode(NULL, 0, "a%25b", 5);
	TestCase_assertSizeEqual(length, 3);
	length = percentDecode(decodedString, sizeof(decodedString), "a%25b", 5);
	TestCase_assertSizeEqual(length, 3);
	TestCase_assertBytesEqual(decodedString, "a%b", 3);
	
	length = percentDecode(NULL, 0, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	TestCase_assertSizeEqual(length, 66);
	length = percentDecode(decodedString, sizeof(decodedString), "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	TestCase_assertSizeEqual(length, 66);
	TestCase_assertBytesEqual(decodedString, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~", 66);
	
	length = percentDecode(NULL, 0, "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"
	                                "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"
	                                "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F"
	                                "%30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F"
	                                "%40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F"
	                                "%50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F"
	                                "%60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F"
	                                "%70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F"
	                                "%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F"
	                                "%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F"
	                                "%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF"
	                                "%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF"
	                                "%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF"
	                                "%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF"
	                                "%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF"
	                                "%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF", 768);
	TestCase_assertSizeEqual(length, 256);
	length = percentDecode(decodedString, sizeof(decodedString), "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"
	                                                             "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"
	                                                             "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F"
	                                                             "%30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F"
	                                                             "%40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F"
	                                                             "%50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F"
	                                                             "%60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F"
	                                                             "%70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F"
	                                                             "%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F"
	                                                             "%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F"
	                                                             "%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF"
	                                                             "%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF"
	                                                             "%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF"
	                                                             "%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF"
	                                                             "%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF"
	                                                             "%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF", 768);
	TestCase_assertSizeEqual(length, 256);
	TestCase_assertBytesEqual(decodedString, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
	                                         "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
	                                         "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
	                                         "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
	                                         "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
	                                         "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F"
	                                         "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
	                                         "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"
	                                         "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
	                                         "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
	                                         "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
	                                         "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
	                                         "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
	                                         "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
	                                         "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
	                                         "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 256);
	
	decodedString[5] = '?';
	length = percentDecode(decodedString, 5, "0123456789", 10);
	TestCase_assertSizeEqual(length, 5);
	TestCase_assertBytesEqual(decodedString, "01234?", 6);
	length = percentDecode(decodedString, 5, "%25%25%25%25%25%25", 18);
	TestCase_assertSizeEqual(length, 5);
	TestCase_assertBytesEqual(decodedString, "%%%%%?", 6);
	
	length = percentDecode(decodedString, sizeof(decodedString), "a%", 2);
	TestCase_assertSizeEqual(length, 2);
	TestCase_assertBytesEqual(decodedString, "a%", 2);
	length = percentDecode(decodedString, sizeof(decodedString), "a%2", 3);
	TestCase_assertSizeEqual(length, 3);
	TestCase_assertBytesEqual(decodedString, "a%2", 3);
	length = percentDecode(decodedString, sizeof(decodedString), "a%20", 4);
	TestCase_assertSizeEqual(length, 2);
	TestCase_assertBytesEqual(decodedString, "a ", 2);
	
	length = percentDecode(decodedString, sizeof(decodedString), "a%7a", 4);
	TestCase_assertSizeEqual(length, 2);
	TestCase_assertBytesEqual(decodedString, "az", 2);
	length = percentDecode(decodedString, sizeof(decodedString), "a%7g", 4);
	TestCase_assertSizeEqual(length, 4);
	TestCase_assertBytesEqual(decodedString, "a%7g", 4);
	length = percentDecode(decodedString, sizeof(decodedString), "a%%7A", 5);
	TestCase_assertSizeEqual(length, 3);
	TestCase_assertBytesEqual(decodedString, "a%z", 3);
	
	char * decodedString2 = percentDecodeString("A%25B%25C", &length);
	TestCase_assertStringEqual(decodedString2, "A%B%C");
	TestCase_assertSizeEqual(length, 5);
	free(decodedString2);
	decodedString2 = percentDecodeString("1%2A2%3A3.", &length);
	TestCase_assertStringEqual(decodedString2, "1*2:3.");
	TestCase_assertSizeEqual(length, 6);
	free(decodedString2);
}

TEST_SUITE(IOUtilitiesTest,
           testMemreadContextInit,
           testMemread,
           testMemwriteContextInit,
           testMemwrite,
           testReadFileSimple,
           testWriteFileSimple,
           testWriteFileAtomic,
           testTemporaryFilePath,
           testFilePathFunctions,
           testEndianSwapping,
           testSafeStringFunctions,
           testMemdup,
           testMemappend,
           testMemnappend,
           testStrnlen,
           testLexstrcmp,
           testPrintHexString,
           testFormatDecimalString,
           testParseDecimalString,
           testFormatDecimalIntegerString,
           testParseDecimalIntegerString,
           testCreateIdentifierFromDisplayString,
           testGetFileExtension,
           testGetLastPathComponent,
           testGetDirectory,
           testGetBaseName,
           testGetSiblingFilePath,
           testGetPathRelativeTo,
           testGetAbsolutePath,
           testIsAbsolutePath,
           testEnumerateFilesInDirectory,
           testPercentEncode,
           testPercentDecode)
