/*
	Test-SST_FileSys.cpp
	Author: Patrick Baggett <ptbaggett@762studios.com>
	Created: 3/29/2012

	Purpose: 

	Tests libsst-os file system functions

	License:

	This program is free software. It comes without any warranty, to
	the extent permitted by applicable law. You can redistribute it
	and/or modify it under the terms of the Do What The Fuck You Want
	To Public License, Version 2, as published by Sam Hocevar. See
	http://sam.zoy.org/wtfpl/COPYING for more details.

*/

#include "ZUnitTest.hpp"

#include <SST/SST_FileSys.h>
#include <stdio.h>
#include <stdlib.h>

static const char* testOpenBad();
static const char* testOpenGood();
static const char* testOpenOnFile();
static const char* testOpenOnFile();
static const char* testOpenEmpty();
static const char* testReadNonEmpty();
static const char* testReadEmpty();

//List of unit tests
ZUnitTest SST_FileSysUnitTests[] =
{
	{ "SST_FileSys: OpenDirectory() on non-existent directory", testOpenBad },
	{ "SST_FileSys: OpenDirectory() on valid directory with files", testOpenGood },
	{ "SST_FileSys: OpenDirectory() on a file", testOpenOnFile },
	{ "SST_FileSys: OpenDirectory() on a empty directory", testOpenEmpty },
	{ "SST_FileSys: ReadNextDirectoryEntry() on valid directory with files", testReadNonEmpty },
	{ "SST_FileSys: ReadNextDirectoryEntry() on empty directory", testReadEmpty }
};

DECLARE_ZTESTBLOCK(SST_FileSys);


/*
	These tests assume a working environment that consists of:

	./sst_fs/file1.txt (size == 32 bytes)
	./sst_fs/file2.txt (size == 0 bytes)
	./sst_fs/dir1/
	./sst_fs/dir2/
		

*/

/*************************************************************************/

static const char* testOpenBad()
{
	SST_Dir dir = SST_OS_OpenDirectory("./sst_fsxxxxdoesntexist");
	
	if(dir != NULL)
	{
		SST_OS_CloseDirectory(dir);
		return "Returned non-null on non-existent directory";
	}

	//Should fail due to trailing slash
	dir = SST_OS_OpenDirectory("./sst_fs/");
	if(dir != NULL)
	{
		SST_OS_CloseDirectory(dir);
		return "Returned non-null on directory that ends with a trailing slash";
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testOpenGood()
{
	SST_Dir dir = SST_OS_OpenDirectory("./sst_fs");

	TASSERT(dir != NULL, "Returned NULL on valid directory");

	SST_OS_CloseDirectory(dir);

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testOpenOnFile()
{
	SST_Dir dir = SST_OS_OpenDirectory("./sst_fs/file1.txt");

	if(dir != NULL)
	{
		SST_OS_CloseDirectory(dir);
		return "Returned non-null when opening a file";
	}

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testOpenEmpty()
{
	SST_Dir dir = SST_OS_OpenDirectory("./sst_fs/dir1");

	TASSERT(dir != NULL, "Returned null when opening empty directory");

	SST_OS_CloseDirectory(dir);

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testReadNonEmpty()
{
	SST_Dir dir = SST_OS_OpenDirectory("./sst_fs");
	SST_FileInfo info;
	size_t nrFoundCorrectly = 0;

	#define NR_VALID 4
	static char errorbuf[1024];
	const char* validFiles[NR_VALID] =
	{
		"file1.txt",
		"file2.txt",
		"dir1",
		"dir2"
	};

	const bool validFileIsDir[NR_VALID] =
	{
		false,	/* file1.txt is not a directory */
		false,	/* file2.txt is not a directory */
		true,	/* dir1 is a directory */
		true	/* dir2 is a directory */
	};

	const uint64_t validFileSize[NR_VALID] =
	{
		32,	/* file1.txt == 32 bytes */
		0,	/* file2.txt = 0 bytes */
		0,	/* not examined */
		0	/* not examined */
	};

	TASSERT(dir != NULL, "Returned NULL on valid directory");

	int OK;
	do
	{
		OK = SST_OS_ReadNextDirectoryEntry(dir, &info);



		if(OK)
		{
			//OK, ignore version control stuff
			if(strcmp(info.name, ".svn") == 0)
				continue;

			//Check for "." -- should not exist
			if(strcmp(info.name, ".") == 0)
			{
				SST_OS_CloseDirectory(dir);
				return "Found \".\" entry, but these should be remove by API";
			}

			//Check for ".." -- should not exist
			if(strcmp(info.name, "..") == 0)
			{
				SST_OS_CloseDirectory(dir);
				return "Found \"..\" entry, but these should be remove by API";
			}

			//Check if we've already found all the files required -- we shouldn't find any more
			if(nrFoundCorrectly == NR_VALID)
			{
				SST_OS_CloseDirectory(dir);
				sprintf(errorbuf, "Already found all required files (%d of them) but found another file %s (ensure test filesystem is OK, then retest)", NR_VALID, info.name);
				return errorbuf;
			}
	
			//Try to find a filename match with validFiles[]
			size_t i;
			for(i=0; i<NR_VALID; i++)
			{
				//Does it match?
				if(strcmp(info.name, validFiles[i]) == 0)
				{
					bool isDir = (info.isDir != 0);

					if(isDir == validFileIsDir[i])
					{
						//If this is a file, then check its size
						if(!isDir)
						{
							if(validFileSize[i] == info.size)
							{
								nrFoundCorrectly++;
								break;
							}
							else
							{
								sprintf(errorbuf, "The directory entry for %s says it is %u bytes, but it should be %u",
									info.name,
									(unsigned int)info.size,
									(unsigned int)validFileSize[i]);

								SST_OS_CloseDirectory(dir);
								return errorbuf;
							}
						}
						else
						{
							nrFoundCorrectly++;
							break;
						}

					}
					else
					{
						sprintf(errorbuf, "The directory entry for %s says it is a %s, but it should be a %s",
							info.name,
							(isDir? "directory" : "file"),
							(validFileIsDir[i]? "directory" : "file"));

						SST_OS_CloseDirectory(dir);
						return errorbuf;
					}
				}
			}

			//Check if no filenames matched
			if(i == NR_VALID)
			{
				
				sprintf(errorbuf, "Found invalid file %s (ensure test filesystem is OK, then retest)", info.name);
				SST_OS_CloseDirectory(dir);
				return errorbuf;
			}

		} //if OK
	} while(OK);

	//Ensure all necessary files were found correctly
	if(nrFoundCorrectly != NR_VALID)
	{
		SST_OS_CloseDirectory(dir);
		sprintf(errorbuf, "Only %d/%d entries were found in this directory", nrFoundCorrectly, NR_VALID);
		return errorbuf;
	}

	#undef NR_VALID

	SST_OS_CloseDirectory(dir);
	return ZTEST_SUCCESS;
}


static const char* testReadEmpty()
{
	return ZTEST_SUCCESS;
}