/*
 * Unit test for file.h
 */

#include "test.h"
#include "file.h"
#include "tree.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void test_iso_file_new()
{
	struct iso_tree_node_file *file;
	struct iso_file *iso;
	
	file = calloc(1, sizeof(struct iso_tree_node_file) );
	file->node.name = "fileName";
	file->node.attrib.st_size = 12;
	file->node.attrib.st_dev = 15;
	file->node.attrib.st_ino = 204;
	file->loc.path = "/tmp/filename";
	file->sort_weight = 1;
	
	iso = iso_file_new(file);
	
	CU_ASSERT_PTR_NOT_NULL(iso);
	CU_ASSERT_STRING_EQUAL(iso->path, "/tmp/filename");
	CU_ASSERT_EQUAL(iso->size, 12);
	CU_ASSERT_EQUAL(iso->ino, 0);
	CU_ASSERT_EQUAL(iso->nlink, 1);
	CU_ASSERT_EQUAL(iso->sort_weight, 1);
	CU_ASSERT_EQUAL(iso->real_dev, 15);
	CU_ASSERT_EQUAL(iso->real_ino, 204);
}

static void test_add_lookup()
{
	struct iso_file_table *table;
	struct iso_tree_node_file *file1;
	struct iso_tree_node_file *file2;
	struct iso_file *iso1;
	struct iso_file *iso2;
	struct iso_file *iso3;
	int r;
	
	table = iso_file_table_new(1);
	
	CU_ASSERT_PTR_NOT_NULL( table );
	CU_ASSERT_TRUE( table->cache_inodes );
	CU_ASSERT_EQUAL(table->count, 0);
	
	file1 = calloc(1, sizeof(struct iso_tree_node_file) );
	file1->node.name = "fileName";
	file1->node.attrib.st_dev = 15;
	file1->node.attrib.st_ino = 204;
	file1->loc.path = "/tmp/filename";
	
	iso1 = iso_file_new(file1);
	
	r = iso_file_table_add_file(table, iso1);
	CU_ASSERT_EQUAL(r, 1);
	CU_ASSERT_EQUAL(table->count, 1);
	
	iso2 = iso_file_table_lookup(table, file1);
	CU_ASSERT_PTR_NOT_NULL(iso2);
	CU_ASSERT_PTR_EQUAL(iso2, iso1);
	
	file2 = calloc(1, sizeof(struct iso_tree_node_file) );
	file2->node.name = "fileName2";
	file2->node.attrib.st_dev = 152;
	file2->node.attrib.st_ino = 2042;
	file2->loc.path = "/tmp/filename2";
	
	iso3 = iso_file_new(file2);
	r = iso_file_table_add_file(table, iso3);
	CU_ASSERT_EQUAL(r, 1);
	CU_ASSERT_EQUAL(table->count, 2);
	
	/* treat to add the same file again */
	r = iso_file_table_add_file(table, iso3);
	CU_ASSERT_EQUAL(r, 0);
	CU_ASSERT_EQUAL(table->count, 2);
	
	iso2 = iso_file_table_lookup(table, file1);
	CU_ASSERT_PTR_NOT_NULL(iso2);
	CU_ASSERT_PTR_EQUAL(iso2, iso1);
	
	iso2 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_NOT_NULL(iso2);
	CU_ASSERT_PTR_EQUAL(iso2, iso3);
	
	iso3 = iso_file_new(file2);
	r = iso_file_table_add_file(table, iso3);
	CU_ASSERT_EQUAL(r, 0);
	CU_ASSERT_EQUAL(table->count, 2);
	
	iso_file_table_clear(table);
	CU_ASSERT_EQUAL(table->count, 0);
	
	iso2 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_NULL(iso2);
	
	free( file1 );
	free( file2 );
	free( table );
}

static void test_cache_inodes()
{
	struct iso_file_table *table;
	struct iso_tree_node_file *file1;
	struct iso_tree_node_file *file2;
	struct iso_file *iso1;
	struct iso_file *iso2;
	struct iso_file *iso3;
	int r;
	
	table = iso_file_table_new(1);
	
	CU_ASSERT_PTR_NOT_NULL( table );
	CU_ASSERT_TRUE( table->cache_inodes );
	CU_ASSERT_EQUAL(table->count, 0);
	
	file1 = calloc(1, sizeof(struct iso_tree_node_file) );
	file1->node.name = "fileName";
	file1->node.attrib.st_dev = 15;
	file1->node.attrib.st_ino = 204;
	file1->loc.path = "/tmp/filename";
	
	iso1 = iso_file_new(file1);
	
	r = iso_file_table_add_file(table, iso1);
	CU_ASSERT_EQUAL(r, 1);
	
	/* another file, different but with the same inode id */
	file2 = calloc(1, sizeof(struct iso_tree_node_file) );
	file2->node.name = "another file";
	file2->node.attrib.st_dev = 15;
	file2->node.attrib.st_ino = 204;
	file2->loc.path = "/tmp/another";
	iso2 = iso_file_new(file2);
	
	/* ensure it's not added again... */
	r = iso_file_table_add_file(table, iso2);
	CU_ASSERT_EQUAL(r, 0);
	
	/* ...and the lookup returns the first */
	iso3 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_EQUAL(iso1, iso3);
	
	free(iso2);
	free(file2);

	/* and now a file with same inode but different device */
	file2 = calloc(1, sizeof(struct iso_tree_node_file) );
	file2->node.name = "different file";
	file2->node.attrib.st_dev = 16; /* different dev id */
	file2->node.attrib.st_ino = 204;
	file2->loc.path = "/tmp/different";
	iso2 = iso_file_new(file2);
	
	r = iso_file_table_add_file(table, iso2);
	CU_ASSERT_EQUAL(r, 1);
	iso3 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_NOT_EQUAL(iso3, iso1);
	CU_ASSERT_PTR_EQUAL(iso3, iso2);

	iso_file_table_clear(table);
	free( file1 );
	free( file2 );
	free( table );
}

static void test_no_cache_inodes()
{
	struct iso_file_table *table;
	struct iso_tree_node_file *file1;
	struct iso_tree_node_file *file2;
	struct iso_tree_node_file *file3;
	struct iso_file *iso1;
	struct iso_file *iso2;
	struct iso_file *iso3;
	int r;
	
	table = iso_file_table_new(0);
	
	CU_ASSERT_PTR_NOT_NULL( table );
	CU_ASSERT_FALSE( table->cache_inodes );
	CU_ASSERT_EQUAL(table->count, 0);
	
	file1 = calloc(1, sizeof(struct iso_tree_node_file) );
	file1->node.name = "fileName";
	file1->node.attrib.st_dev = 15;
	file1->node.attrib.st_ino = 204;
	file1->loc.path = "/tmp/filename";
	
	iso1 = iso_file_new(file1);
	
	r = iso_file_table_add_file(table, iso1);
	CU_ASSERT_EQUAL(r, 1);
	
	/* another file, different but with the same inode id */
	file2 = calloc(1, sizeof(struct iso_tree_node_file) );
	file2->node.name = "another file";
	file2->node.attrib.st_dev = 15;
	file2->node.attrib.st_ino = 204;
	file2->loc.path = "/tmp/another";
	iso2 = iso_file_new(file2);
	
	/* ensure is added */
	r = iso_file_table_add_file(table, iso2);
	CU_ASSERT_EQUAL(r, 1);
	
	iso3 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_EQUAL(iso3, iso2);

	/* and now a file with same inode and path */
	file3 = calloc(1, sizeof(struct iso_tree_node_file) );
	file3->node.name = "different file";
	file3->node.attrib.st_dev = 15; 
	file3->node.attrib.st_ino = 204;
	file3->loc.path = "/tmp/filename";
	iso3 = iso_file_new(file3);
	
	r = iso_file_table_add_file(table, iso3);
	CU_ASSERT_EQUAL(r, 0);
	iso3 = iso_file_table_lookup(table, file3);
	CU_ASSERT_PTR_EQUAL(iso3, iso1);

	iso_file_table_clear(table);
	free(file1);
	free(file2);
	free(file3);
	free(table);
}

static void test_prev_img_files()
{
	struct iso_file_table *table;
	struct iso_tree_node_file *file1;
	struct iso_tree_node_file *file2;
	struct iso_tree_node_file *file3;
	struct iso_file *iso1;
	struct iso_file *iso2;
	struct iso_file *iso3;
	int r;
	
	table = iso_file_table_new(0);
	
	CU_ASSERT_PTR_NOT_NULL( table );
	CU_ASSERT_FALSE( table->cache_inodes );
	CU_ASSERT_EQUAL(table->count, 0);
	
	file1 = calloc(1, sizeof(struct iso_tree_node_file) );
	file1->node.name = "fileName";
	file1->node.procedence = LIBISO_PREVIMG;
	file1->node.attrib.st_dev = 0;
	file1->node.attrib.st_ino = 204;
	file1->loc.block = 567;
	
	iso1 = iso_file_new(file1);
	
	r = iso_file_table_add_file(table, iso1);
	CU_ASSERT_EQUAL(r, 1);
	
	/* another file, different but with the same inode id */
	file2 = calloc(1, sizeof(struct iso_tree_node_file) );
	file2->node.name = "another file";
	file2->node.procedence = LIBISO_PREVIMG;
	file2->node.attrib.st_dev = 0;
	file2->node.attrib.st_ino = 204;
	file2->loc.block = 567;
	iso2 = iso_file_new(file2);
	
	/* ensure is not added */
	r = iso_file_table_add_file(table, iso2);
	CU_ASSERT_EQUAL(r, 0);
	
	iso3 = iso_file_table_lookup(table, file2);
	CU_ASSERT_PTR_EQUAL(iso3, iso1);

	/* and now a file added new */
	file3 = calloc(1, sizeof(struct iso_tree_node_file) );
	file3->node.name = "different file";
	file3->node.attrib.st_dev = 0; 
	file3->node.attrib.st_ino = 204;
	file3->loc.path = "/tmp/filename";
	iso3 = iso_file_new(file3);
	
	/* assert it's added */
	r = iso_file_table_add_file(table, iso3);
	CU_ASSERT_EQUAL(r, 1);
	iso1 = iso_file_table_lookup(table, file3);
	CU_ASSERT_PTR_EQUAL(iso1, iso3);

	iso_file_table_clear(table);
	free(file1);
	free(file2);
	free(file3);
	free(table);
}


void add_file_hashtable_suite()
{
	CU_pSuite pSuite = CU_add_suite("FileHashtableSuite", NULL, NULL);
	CU_add_test(pSuite, "iso_file_new()", test_iso_file_new);
	CU_add_test(pSuite, "add and lookup", test_add_lookup);
	CU_add_test(pSuite, "with cache_inodes", test_cache_inodes);
	CU_add_test(pSuite, "with files from previous img", test_prev_img_files);
}