From e6a48b8c0ebe220e29c0c730c02782ef563a275a Mon Sep 17 00:00:00 2001 From: Guillem Date: Tue, 19 Oct 2021 20:04:13 +0200 Subject: [PATCH] Initial Commit --- Makefile | 12 + README.txt | 4 + UOC20211.mk | 143 ++++++++ UOC20211.project | 140 ++++++++ UOC20211.txt | 1 + UOC20211.workspace | 17 + UOCContacts/UOCContacts.mk | 138 ++++++++ UOCContacts/UOCContacts.project | 122 +++++++ UOCContacts/UOCContacts.txt | 1 + UOCContacts/include/api.h | 40 +++ UOCContacts/include/csv.h | 72 ++++ UOCContacts/include/date.h | 28 ++ UOCContacts/include/error.h | 19 ++ UOCContacts/include/person.h | 48 +++ UOCContacts/src/api.c | 114 +++++++ UOCContacts/src/csv.c | 232 +++++++++++++ UOCContacts/src/date.c | 34 ++ UOCContacts/src/person.c | 241 ++++++++++++++ src/main.c | 104 ++++++ test/include/test.h | 11 + test/include/test_pr1.h | 20 ++ test/include/test_suite.h | 151 +++++++++ test/src/test.c | 39 +++ test/src/test_pr1.c | 364 +++++++++++++++++++++ test/src/test_suite.c | 559 ++++++++++++++++++++++++++++++++ 25 files changed, 2654 insertions(+) create mode 100644 Makefile create mode 100644 README.txt create mode 100644 UOC20211.mk create mode 100644 UOC20211.project create mode 100644 UOC20211.txt create mode 100644 UOC20211.workspace create mode 100644 UOCContacts/UOCContacts.mk create mode 100644 UOCContacts/UOCContacts.project create mode 100644 UOCContacts/UOCContacts.txt create mode 100644 UOCContacts/include/api.h create mode 100644 UOCContacts/include/csv.h create mode 100644 UOCContacts/include/date.h create mode 100644 UOCContacts/include/error.h create mode 100644 UOCContacts/include/person.h create mode 100644 UOCContacts/src/api.c create mode 100644 UOCContacts/src/csv.c create mode 100644 UOCContacts/src/date.c create mode 100644 UOCContacts/src/person.c create mode 100644 src/main.c create mode 100644 test/include/test.h create mode 100644 test/include/test_pr1.h create mode 100644 test/include/test_suite.h create mode 100644 test/src/test.c create mode 100644 test/src/test_pr1.c create mode 100644 test/src/test_suite.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ebf7efd --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: clean All + +All: + @echo "----------Building project:[ UOCContacts - Debug ]----------" + @cd "UOCContacts" && "$(MAKE)" -f "UOCContacts.mk" + @echo "----------Building project:[ UOC20211 - Debug ]----------" + @"$(MAKE)" -f "UOC20211.mk" +clean: + @echo "----------Cleaning project:[ UOCContacts - Debug ]----------" + @cd "UOCContacts" && "$(MAKE)" -f "UOCContacts.mk" clean + @echo "----------Cleaning project:[ UOC20211 - Debug ]----------" + @"$(MAKE)" -f "UOC20211.mk" clean diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..fdfe185 --- /dev/null +++ b/README.txt @@ -0,0 +1,4 @@ +gsboeck@uoc.edu +SolĂ  i Boeck, Guillem +Fedora release 34 (Thirty Four) x86_64 +gcc version 11.2.1 20210728 (Red Hat 11.2.1-1) (GCC) diff --git a/UOC20211.mk b/UOC20211.mk new file mode 100644 index 0000000..8e24d7f --- /dev/null +++ b/UOC20211.mk @@ -0,0 +1,143 @@ +## +## Auto Generated makefile by CodeLite IDE +## any manual changes will be erased +## +## Debug +ProjectName :=UOC20211 +ConfigurationName :=Debug +WorkspacePath :=W:/Docencia/PP/20211/PR1/UOC20211 +ProjectPath :=W:/Docencia/PP/20211/PR1/UOC20211 +IntermediateDirectory :=./bin/Debug +OutDir := $(IntermediateDirectory) +CurrentFileName := +CurrentFilePath := +CurrentFileFullPath := +User :=xavie +Date :=11/10/2021 +CodeLitePath :="C:/Program Files/CodeLite" +LinkerName :=C:/TDM-GCC-64/bin/g++.exe +SharedObjectLinkerName :=C:/TDM-GCC-64/bin/g++.exe -shared -fPIC +ObjectSuffix :=.o +DependSuffix :=.o.d +PreprocessSuffix :=.i +DebugSwitch :=-g +IncludeSwitch :=-I +LibrarySwitch :=-l +OutputSwitch :=-o +LibraryPathSwitch :=-L +PreprocessorSwitch :=-D +SourceSwitch :=-c +OutputFile :=./bin/$(ProjectName)d +Preprocessors := +ObjectSwitch :=-o +ArchiveOutputSwitch := +PreprocessOnlySwitch :=-E +ObjectsFileList :="UOC20211.txt" +PCHCompileFlags := +MakeDirCommand :=makedir +RcCmpOptions := +RcCompilerName :=C:/TDM-GCC-64/bin/windres.exe +LinkOptions := +IncludePath := $(IncludeSwitch). $(IncludeSwitch). $(IncludeSwitch)test/include $(IncludeSwitch)UOCContacts/include +IncludePCH := +RcIncludePath := +Libs := $(LibrarySwitch)UOCContactsd +ArLibs := "libUOCContactsd.a" +LibPath := $(LibraryPathSwitch). $(LibraryPathSwitch)./lib + +## +## Common variables +## AR, CXX, CC, AS, CXXFLAGS and CFLAGS can be overriden using an environment variables +## +AR := C:/TDM-GCC-64/bin/ar.exe rcu +CXX := C:/TDM-GCC-64/bin/g++.exe +CC := C:/TDM-GCC-64/bin/gcc.exe +CXXFLAGS := -g -O0 -Wall $(Preprocessors) +CFLAGS := -g -O0 -Wall $(Preprocessors) +ASFLAGS := +AS := C:/TDM-GCC-64/bin/as.exe + + +## +## User defined environment variables +## +CodeLiteDir:=C:\Program Files\CodeLite +Objects0=$(IntermediateDirectory)/src_main.c$(ObjectSuffix) $(IntermediateDirectory)/test_src_test_suite.c$(ObjectSuffix) $(IntermediateDirectory)/test_src_test.c$(ObjectSuffix) $(IntermediateDirectory)/test_src_test_pr1.c$(ObjectSuffix) + + + +Objects=$(Objects0) + +## +## Main Build Targets +## +.PHONY: all clean PreBuild PrePreBuild PostBuild MakeIntermediateDirs +all: $(OutputFile) + +$(OutputFile): $(IntermediateDirectory)/.d ".build-debug\UOCContacts" $(Objects) + @$(MakeDirCommand) $(@D) + @echo "" > $(IntermediateDirectory)/.d + @echo $(Objects0) > $(ObjectsFileList) + $(LinkerName) $(OutputSwitch)$(OutputFile) @$(ObjectsFileList) $(LibPath) $(Libs) $(LinkOptions) + +".build-debug\UOCContacts": + @$(MakeDirCommand) ".build-debug" + @echo stam > ".build-debug\UOCContacts" + + + + +MakeIntermediateDirs: + @$(MakeDirCommand) "./bin/Debug" + + +$(IntermediateDirectory)/.d: + @$(MakeDirCommand) "./bin/Debug" + +PreBuild: + + +## +## Objects +## +$(IntermediateDirectory)/src_main.c$(ObjectSuffix): src/main.c $(IntermediateDirectory)/src_main.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/src/main.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_main.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/src_main.c$(DependSuffix): src/main.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_main.c$(ObjectSuffix) -MF$(IntermediateDirectory)/src_main.c$(DependSuffix) -MM src/main.c + +$(IntermediateDirectory)/src_main.c$(PreprocessSuffix): src/main.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_main.c$(PreprocessSuffix) src/main.c + +$(IntermediateDirectory)/test_src_test_suite.c$(ObjectSuffix): test/src/test_suite.c $(IntermediateDirectory)/test_src_test_suite.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/test/src/test_suite.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/test_src_test_suite.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/test_src_test_suite.c$(DependSuffix): test/src/test_suite.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/test_src_test_suite.c$(ObjectSuffix) -MF$(IntermediateDirectory)/test_src_test_suite.c$(DependSuffix) -MM test/src/test_suite.c + +$(IntermediateDirectory)/test_src_test_suite.c$(PreprocessSuffix): test/src/test_suite.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/test_src_test_suite.c$(PreprocessSuffix) test/src/test_suite.c + +$(IntermediateDirectory)/test_src_test.c$(ObjectSuffix): test/src/test.c $(IntermediateDirectory)/test_src_test.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/test/src/test.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/test_src_test.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/test_src_test.c$(DependSuffix): test/src/test.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/test_src_test.c$(ObjectSuffix) -MF$(IntermediateDirectory)/test_src_test.c$(DependSuffix) -MM test/src/test.c + +$(IntermediateDirectory)/test_src_test.c$(PreprocessSuffix): test/src/test.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/test_src_test.c$(PreprocessSuffix) test/src/test.c + +$(IntermediateDirectory)/test_src_test_pr1.c$(ObjectSuffix): test/src/test_pr1.c $(IntermediateDirectory)/test_src_test_pr1.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/test/src/test_pr1.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/test_src_test_pr1.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/test_src_test_pr1.c$(DependSuffix): test/src/test_pr1.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/test_src_test_pr1.c$(ObjectSuffix) -MF$(IntermediateDirectory)/test_src_test_pr1.c$(DependSuffix) -MM test/src/test_pr1.c + +$(IntermediateDirectory)/test_src_test_pr1.c$(PreprocessSuffix): test/src/test_pr1.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/test_src_test_pr1.c$(PreprocessSuffix) test/src/test_pr1.c + + +-include $(IntermediateDirectory)/*$(DependSuffix) +## +## Clean +## +clean: + $(RM) -r ./bin/Debug/ + + diff --git a/UOC20211.project b/UOC20211.project new file mode 100644 index 0000000..0224a1f --- /dev/null +++ b/UOC20211.project @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /Docencia/PP/20211/PR1/UOC20211 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + diff --git a/UOC20211.txt b/UOC20211.txt new file mode 100644 index 0000000..4513f15 --- /dev/null +++ b/UOC20211.txt @@ -0,0 +1 @@ +./bin/Debug/src_main.c.o ./bin/Debug/test_src_test_suite.c.o ./bin/Debug/test_src_test.c.o ./bin/Debug/test_src_test_pr1.c.o diff --git a/UOC20211.workspace b/UOC20211.workspace new file mode 100644 index 0000000..09ed98b --- /dev/null +++ b/UOC20211.workspace @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/UOCContacts/UOCContacts.mk b/UOCContacts/UOCContacts.mk new file mode 100644 index 0000000..787095e --- /dev/null +++ b/UOCContacts/UOCContacts.mk @@ -0,0 +1,138 @@ +## +## Auto Generated makefile by CodeLite IDE +## any manual changes will be erased +## +## Debug +ProjectName :=UOCContacts +ConfigurationName :=Debug +WorkspacePath :=W:/Docencia/PP/20211/PR1/UOC20211 +ProjectPath :=W:/Docencia/PP/20211/PR1/UOC20211/UOCContacts +IntermediateDirectory :=../bin/Debug +OutDir := $(IntermediateDirectory) +CurrentFileName := +CurrentFilePath := +CurrentFileFullPath := +User :=xavie +Date :=11/10/2021 +CodeLitePath :="C:/Program Files/CodeLite" +LinkerName :=C:/TDM-GCC-64/bin/g++.exe +SharedObjectLinkerName :=C:/TDM-GCC-64/bin/g++.exe -shared -fPIC +ObjectSuffix :=.o +DependSuffix :=.o.d +PreprocessSuffix :=.i +DebugSwitch :=-g +IncludeSwitch :=-I +LibrarySwitch :=-l +OutputSwitch :=-o +LibraryPathSwitch :=-L +PreprocessorSwitch :=-D +SourceSwitch :=-c +OutputFile :=../lib/lib$(ProjectName)d.a +Preprocessors := +ObjectSwitch :=-o +ArchiveOutputSwitch := +PreprocessOnlySwitch :=-E +ObjectsFileList :="UOCContacts.txt" +PCHCompileFlags := +MakeDirCommand :=makedir +RcCmpOptions := +RcCompilerName :=C:/TDM-GCC-64/bin/windres.exe +LinkOptions := +IncludePath := $(IncludeSwitch). $(IncludeSwitch). $(IncludeSwitch)include +IncludePCH := +RcIncludePath := +Libs := +ArLibs := +LibPath := $(LibraryPathSwitch). + +## +## Common variables +## AR, CXX, CC, AS, CXXFLAGS and CFLAGS can be overriden using an environment variables +## +AR := C:/TDM-GCC-64/bin/ar.exe rcu +CXX := C:/TDM-GCC-64/bin/g++.exe +CC := C:/TDM-GCC-64/bin/gcc.exe +CXXFLAGS := -g $(Preprocessors) +CFLAGS := -g $(Preprocessors) +ASFLAGS := +AS := C:/TDM-GCC-64/bin/as.exe + + +## +## User defined environment variables +## +CodeLiteDir:=C:\Program Files\CodeLite +Objects0=$(IntermediateDirectory)/src_date.c$(ObjectSuffix) $(IntermediateDirectory)/src_person.c$(ObjectSuffix) $(IntermediateDirectory)/src_csv.c$(ObjectSuffix) $(IntermediateDirectory)/src_api.c$(ObjectSuffix) + + + +Objects=$(Objects0) + +## +## Main Build Targets +## +.PHONY: all clean PreBuild PrePreBuild PostBuild MakeIntermediateDirs +all: $(IntermediateDirectory) $(OutputFile) + +$(OutputFile): $(Objects) + @$(MakeDirCommand) $(@D) + @echo "" > $(IntermediateDirectory)/.d + @echo $(Objects0) > $(ObjectsFileList) + $(AR) $(ArchiveOutputSwitch)$(OutputFile) @$(ObjectsFileList) $(ArLibs) + @$(MakeDirCommand) "W:\Docencia\PP\20211\PR1\UOC20211/.build-debug" + @echo rebuilt > "W:\Docencia\PP\20211\PR1\UOC20211/.build-debug/UOCContacts" + +MakeIntermediateDirs: + @$(MakeDirCommand) "../bin/Debug" + + +../bin/Debug: + @$(MakeDirCommand) "../bin/Debug" + +PreBuild: + + +## +## Objects +## +$(IntermediateDirectory)/src_date.c$(ObjectSuffix): src/date.c $(IntermediateDirectory)/src_date.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/UOCContacts/src/date.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_date.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/src_date.c$(DependSuffix): src/date.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_date.c$(ObjectSuffix) -MF$(IntermediateDirectory)/src_date.c$(DependSuffix) -MM src/date.c + +$(IntermediateDirectory)/src_date.c$(PreprocessSuffix): src/date.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_date.c$(PreprocessSuffix) src/date.c + +$(IntermediateDirectory)/src_person.c$(ObjectSuffix): src/person.c $(IntermediateDirectory)/src_person.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/UOCContacts/src/person.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_person.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/src_person.c$(DependSuffix): src/person.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_person.c$(ObjectSuffix) -MF$(IntermediateDirectory)/src_person.c$(DependSuffix) -MM src/person.c + +$(IntermediateDirectory)/src_person.c$(PreprocessSuffix): src/person.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_person.c$(PreprocessSuffix) src/person.c + +$(IntermediateDirectory)/src_csv.c$(ObjectSuffix): src/csv.c $(IntermediateDirectory)/src_csv.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/UOCContacts/src/csv.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_csv.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/src_csv.c$(DependSuffix): src/csv.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_csv.c$(ObjectSuffix) -MF$(IntermediateDirectory)/src_csv.c$(DependSuffix) -MM src/csv.c + +$(IntermediateDirectory)/src_csv.c$(PreprocessSuffix): src/csv.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_csv.c$(PreprocessSuffix) src/csv.c + +$(IntermediateDirectory)/src_api.c$(ObjectSuffix): src/api.c $(IntermediateDirectory)/src_api.c$(DependSuffix) + $(CC) $(SourceSwitch) "W:/Docencia/PP/20211/PR1/UOC20211/UOCContacts/src/api.c" $(CFLAGS) $(ObjectSwitch)$(IntermediateDirectory)/src_api.c$(ObjectSuffix) $(IncludePath) +$(IntermediateDirectory)/src_api.c$(DependSuffix): src/api.c + @$(CC) $(CFLAGS) $(IncludePath) -MG -MP -MT$(IntermediateDirectory)/src_api.c$(ObjectSuffix) -MF$(IntermediateDirectory)/src_api.c$(DependSuffix) -MM src/api.c + +$(IntermediateDirectory)/src_api.c$(PreprocessSuffix): src/api.c + $(CC) $(CFLAGS) $(IncludePath) $(PreprocessOnlySwitch) $(OutputSwitch) $(IntermediateDirectory)/src_api.c$(PreprocessSuffix) src/api.c + + +-include $(IntermediateDirectory)/*$(DependSuffix) +## +## Clean +## +clean: + $(RM) -r ../bin/Debug/ + + diff --git a/UOCContacts/UOCContacts.project b/UOCContacts/UOCContacts.project new file mode 100644 index 0000000..6f9dd1c --- /dev/null +++ b/UOCContacts/UOCContacts.project @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + /Docencia/PP/20211/PR1/UOC20211/UOCContacts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UOCContacts/UOCContacts.txt b/UOCContacts/UOCContacts.txt new file mode 100644 index 0000000..fa50bf1 --- /dev/null +++ b/UOCContacts/UOCContacts.txt @@ -0,0 +1 @@ +../bin/Debug/src_date.c.o ../bin/Debug/src_person.c.o ../bin/Debug/src_csv.c.o ../bin/Debug/src_api.c.o diff --git a/UOCContacts/include/api.h b/UOCContacts/include/api.h new file mode 100644 index 0000000..cd452f9 --- /dev/null +++ b/UOCContacts/include/api.h @@ -0,0 +1,40 @@ +#ifndef __UOCCONTACTS_API__H +#define __UOCCONTACTS_API__H +#include +#include "error.h" +#include "csv.h" + +// Type that stores all the application data +typedef struct _ApiData { + ////////////////////////////////// + // Ex PR1 2: + ///////////////////////////////// + +} tApiData; + +// Get the API version information +const char* api_version(); + +// Load data from a CSV file. If reset is true, remove previous data +tApiError api_loadData(tApiData* data, const char* filename, bool reset); + +// Add a new entry +tApiError api_addDataEntry(tApiData* data, tCSVEntry entry); + +// Free all used memory +tApiError api_freeData(tApiData* data); + +// Initialize the data structure +tApiError api_initData(tApiData* data); + +// Get person data +tApiError api_findPerson(tApiData* data, const char* document, tCSVEntry* person); + +// Get all geolocation data for a given person +tApiError api_getPersonGeolocation(tApiData* data, const char* document, tCSVData* geodata); + +// Remove a person +tApiError api_removePerson(tApiData* data, const char* document); + + +#endif // __UOCCONTACTS_API__H \ No newline at end of file diff --git a/UOCContacts/include/csv.h b/UOCContacts/include/csv.h new file mode 100644 index 0000000..0df825c --- /dev/null +++ b/UOCContacts/include/csv.h @@ -0,0 +1,72 @@ +#ifndef __CSV_H__ +#define __CSV_H__ + +#include +#define CSV_SEPARATOR_CHAR ; + +// Store one entry from a CSV file +typedef struct _tCSVEntry { + int numFields; + char* type; + char** fields; +} tCSVEntry; + +// Store the content of a CSV file +typedef struct _tCSVData { + tCSVEntry *entries; + int count; + bool isValid; +} tCSVData; + +// Initialize the tCSVData structure +void csv_init(tCSVData* data); + +// Initialize the tCSVEntry structure +void csv_initEntry(tCSVEntry* entry); + +// Parse the contents of a CSV file +void csv_parse(tCSVData* data, const char* input, const char* type); + +// Add a new entry to the CSV Data +void csv_addStrEntry(tCSVData* data, const char* entry, const char* type); + +// Print the content of the CSV data structure +void csv_print(tCSVData data); + +// Print the content of the CSV entry structure +void csv_printEntry(tCSVEntry entry); + +// Parse the contents of a CSV line "f1;f2;f3" => field_0 = f1, field_1 = f2, field_2 = f3 +void csv_parseEntry(tCSVEntry* entry, const char* input, const char* type); + +// Get the number of entries +bool csv_isValid(tCSVData data); + +// Remove all data from structure +void csv_free(tCSVData* data); + +// Remove all data from structure +void csv_freeEntry(tCSVEntry* entry); + +// Get the number of entries +int csv_numEntries(tCSVData data); + +// Get the type of information contained in the entry +const char* csv_getType(tCSVEntry* entry); + +// Get an entry from the CSV data +tCSVEntry* csv_getEntry(tCSVData data, int position); + +// Get the number of fields for a given entry +int csv_numFields(tCSVEntry entry); + +// Get a field from the given entry as integer +int csv_getAsInteger(tCSVEntry entry, int position); + +// Get a field from the given entry as string. The value is copied to the provided buffer with provided maximum length +void csv_getAsString(tCSVEntry entry, int position, char* buffer, int length); + +// Get a field from the given entry as integer +float csv_getAsReal(tCSVEntry entry, int position); + +#endif diff --git a/UOCContacts/include/date.h b/UOCContacts/include/date.h new file mode 100644 index 0000000..6ce42b7 --- /dev/null +++ b/UOCContacts/include/date.h @@ -0,0 +1,28 @@ +#ifndef __DATE_H__ +#define __DATE_H__ +#include + +typedef struct _tDate { + int day; + int month; + int year; +} tDate; + +typedef struct _tTime { + int hour; + int minutes; +} tTime; + +typedef struct _tDateTime { + tDate date; + tTime time; +} tDateTime; + + +// Parse a tDateTime from string information +void dateTime_parse(tDateTime* dateTime, const char* date, const char* time); + +// Compare two tDateTime structures and return true if they contain the same value or false otherwise. +bool dateTime_cmp(tDateTime dateTime1, tDateTime dateTime2); + +#endif // __DATE_H__ \ No newline at end of file diff --git a/UOCContacts/include/error.h b/UOCContacts/include/error.h new file mode 100644 index 0000000..c1dea55 --- /dev/null +++ b/UOCContacts/include/error.h @@ -0,0 +1,19 @@ +#ifndef __UOCCONTACTS_ERROR__H +#define __UOCCONTACTS_ERROR__H + +// Define error codes +enum _tApiError +{ + E_SUCCESS = 0, // No error + E_NOT_IMPLEMENTED = -1, // Called method is not implemented + E_FILE_NOT_FOUND = -2, // File not found + E_PERSON_NOT_FOUND = -3, // Person not found + E_INVALID_ENTRY_TYPE = -4, // Invalid entry type + E_INVALID_ENTRY_FORMAT = -5, // Invalid entry format + E_DUPLICATED_ENTRY = -6, // Duplicated entry +}; + +// Define an error type +typedef enum _tApiError tApiError; + +#endif // __UOCCONTACTS_ERRORS__H \ No newline at end of file diff --git a/UOCContacts/include/person.h b/UOCContacts/include/person.h new file mode 100644 index 0000000..1698978 --- /dev/null +++ b/UOCContacts/include/person.h @@ -0,0 +1,48 @@ +#ifndef __PERSON_H__ +#define __PERSON_H__ +#include "csv.h" +#include "date.h" + +typedef struct _tPerson { + char* document; + char* name; + char* surname; + char* cp; + char* email; + char* address; + tDate birthday; +} tPerson; + +typedef struct _tPopulation { + tPerson* elems; + int count; +} tPopulation; + +// Initialize the population data +void population_init(tPopulation* data); + +// Remove the data from a person +void person_free(tPerson* data); + +// Remove the data from all persons +void population_free(tPopulation* data); + +// Parse input from CSVEntry +void person_parse(tPerson* data, tCSVEntry entry); + +// Add a new person +void population_add(tPopulation* data, tPerson person); + +// Remove a person +void population_del(tPopulation* data, const char *document); + +// Return the position of a person with provided document. -1 if it does not exist +int population_find(tPopulation data, const char* document); + +// Print the person data +void population_print(tPopulation data); + +// Copy the data from the source to destination +void person_cpy(tPerson* destination, tPerson source); + +#endif diff --git a/UOCContacts/src/api.c b/UOCContacts/src/api.c new file mode 100644 index 0000000..9079cb7 --- /dev/null +++ b/UOCContacts/src/api.c @@ -0,0 +1,114 @@ +#include +#include +#include "csv.h" +#include "api.h" +#include + + +#define FILE_READ_BUFFER_SIZE 2048 + +// Get the API version information +const char* api_version() { + return "UOC PP 20211"; +} + +// Load data from a CSV file. If reset is true, remove previous data +tApiError api_loadData(tApiData* data, const char* filename, bool reset) { + tApiError error; + FILE *fin; + char buffer[FILE_READ_BUFFER_SIZE]; + tCSVEntry entry; + + // Check input data + assert( data != NULL ); + assert(filename != NULL); + + // Reset current data + if (reset) { + // Remove previous information + error = api_freeData(data); + if (error != E_SUCCESS) { + return error; + } + + // Initialize the data + error = api_initData(data); + if (error != E_SUCCESS) { + return error; + } + } + + // Open the input file + fin = fopen(filename, "r"); + if (fin == NULL) { + return E_FILE_NOT_FOUND; + } + + // Read file line by line + while (fgets(buffer, FILE_READ_BUFFER_SIZE, fin)) { + // Remove new line character + buffer[strcspn(buffer, "\n\r")] = '\0'; + + csv_initEntry(&entry); + csv_parseEntry(&entry, buffer, NULL); + // Add this new entry to the api Data + error = api_addDataEntry(data, entry); + if (error != E_SUCCESS) { + return error; + } + } + + fclose(fin); + + return E_SUCCESS; +} + +// Add a new entry +tApiError api_addDataEntry(tApiData* data, tCSVEntry entry) { + + ////////////////////////////////// + // Ex PR1 2 + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Free all used memory +tApiError api_freeData(tApiData* data) { + ////////////////////////////////// + // Ex PR1 2: + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Initialize the data structure +tApiError api_initData(tApiData* data) { + + ////////////////////////////////// + // Ex PR1 2: + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get person data +tApiError api_findPerson(tApiData* data, const char* document, tCSVEntry* person) { + ////////////////////////////////// + // Ex PR1 3 + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get all geolocation data for a given person +tApiError api_getPersonGeolocation(tApiData* data, const char* document, tCSVData* geodata) { + ////////////////////////////////// + // Ex PR1 3 + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Remove a person +tApiError api_removePerson(tApiData* data, const char* document) { + ////////////////////////////////// + // Ex PR1 3 + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} diff --git a/UOCContacts/src/csv.c b/UOCContacts/src/csv.c new file mode 100644 index 0000000..f8acbb2 --- /dev/null +++ b/UOCContacts/src/csv.c @@ -0,0 +1,232 @@ +#include "csv.h" +#include +#include +#include +#include + +// Initialize the tCSVData structure +void csv_init(tCSVData* data) { + data->count = 0; + data->isValid = false; + data->entries = NULL; +} + +// Initialize the tCSVEntry structure +void csv_initEntry(tCSVEntry* entry) { + entry->numFields = 0; + entry->fields = NULL; + entry->type = NULL; +} + +// Add a new entry to the CSV Data +void csv_addStrEntry(tCSVData* data, const char* entry, const char* type) { + assert( data != NULL ); + assert( entry != NULL ); + data->count++; + if (data->count == 1) { + data->entries = (tCSVEntry*) malloc(sizeof(tCSVEntry)); + } else { + data->entries = (tCSVEntry*) realloc(data->entries, data->count * sizeof(tCSVEntry)); + } + csv_initEntry(&(data->entries[data->count-1])); + csv_parseEntry(&(data->entries[data->count-1]), entry, type); +} + +// Parse the contents of a CSV file +void csv_parse(tCSVData* data, const char* input, const char* type) { + const char *pStart, *pEnd; + char *line; + int len; + + assert(data->count == 0); + assert(data->entries == NULL); + assert(!data->isValid); + + pStart = input; + pEnd = strchr(pStart, '\n'); + while(pEnd != NULL && pEnd != pStart) { + len = pEnd - pStart + 1; + line = (char*) malloc(len * sizeof(char)); + memset(line, 0, len * sizeof(char)); + strncpy(line, pStart, pEnd - pStart); + // Add the new entry line + csv_addStrEntry(data, line, type); + free(line); + pStart = pEnd + 1; + pEnd = strchr(pStart, '\n'); + } + pEnd = strchr(pStart, '\0'); + if (pEnd != NULL && pEnd != pStart) { + len = pEnd - pStart + 1; + line = (char*) malloc(len * sizeof(char)); + memset(line, 0, len * sizeof(char)); + strncpy(line, pStart, pEnd - pStart); + data->count++; + if (data->count == 1) { + data->entries = (tCSVEntry*) malloc(sizeof(tCSVEntry)); + } else { + data->entries = (tCSVEntry*) realloc(data->entries, data->count * sizeof(tCSVEntry)); + } + csv_initEntry(&(data->entries[data->count-1])); + csv_parseEntry(&(data->entries[data->count-1]), line, type); + free(line); + } + data->isValid = true; +} + +// Print the content of the CSV data structure +void csv_print(tCSVData data) { + int i; + tCSVEntry* entry = NULL; + + for (i = 0; i < csv_numEntries(data); i++) { + entry = csv_getEntry(data, i); + printf("===============\n"); + printf("Entry %d: %s\n", i, entry->type); + printf("===============\n"); + csv_printEntry(*entry); + printf("===============\n"); + } +} + +// Print the content of the CSV entry structure +void csv_printEntry(tCSVEntry entry) { + int i; + char buffer[512]; + printf("\tNum Fields: %d\n", csv_numFields(entry)); + for (i = 0; i < csv_numFields(entry); i++) { + csv_getAsString(entry, i, buffer, 512); + printf("\tField %d: %s\n", i, buffer); + } +} + +// Parse the contents of a CSV line +void csv_parseEntry(tCSVEntry* entry, const char* input, const char* type) { + const char *pStart, *pEnd; + int len; + bool readType = true; + + assert(entry->numFields == 0); + assert(entry->fields == NULL); + + // If the type of the entry is not provided, use the first field + if(type != NULL) { + len = strlen(type) + 1; + entry->type = (char*) malloc(len * sizeof(char)); + memset(entry->type, 0, len * sizeof(char)); + strncpy(entry->type, type, len); + readType = false; + } + pStart = input; + pEnd = strchr(pStart, ';'); + while(pEnd != NULL && pEnd != pStart) { + // Get the length of the field + len = pEnd - pStart + 1; + + if(readType) { + entry->type = (char*) malloc(len * sizeof(char)); + memset(entry->type, 0, len * sizeof(char)); + strncpy(entry->type, pStart, pEnd - pStart); + readType = false; + } else { + entry->numFields++; + if (entry->numFields == 1) { + entry->fields = (char**) malloc(sizeof(char*)); + } else { + entry->fields = (char**) realloc(entry->fields, entry->numFields * sizeof(char*)); + } + entry->fields[entry->numFields - 1] = (char*) malloc(len * sizeof(char)); + memset(entry->fields[entry->numFields - 1], 0, len * sizeof(char)); + strncpy(entry->fields[entry->numFields - 1], pStart, pEnd - pStart); + } + + pStart = pEnd + 1; + pEnd = strchr(pStart, ';'); + } + pEnd = strchr(pStart, '\0'); + if (pEnd != NULL && pEnd != pStart) { + + assert(!readType); + + entry->numFields++; + if (entry->numFields == 1) { + entry->fields = (char**) malloc(sizeof(char*)); + } else { + entry->fields = (char**) realloc(entry->fields, entry->numFields * sizeof(char*)); + } + len = pEnd - pStart + 1; + entry->fields[entry->numFields - 1] = (char*) malloc(len * sizeof(char)); + memset(entry->fields[entry->numFields - 1], 0, len * sizeof(char)); + strncpy(entry->fields[entry->numFields - 1], pStart, pEnd - pStart); + } +} + +// Get the number of entries +bool csv_isValid(tCSVData data) { + return data.isValid; +} + +// Remove all data from structure +void csv_free(tCSVData* data) { + int i; + + for (i = 0; i < data->count; i++) { + csv_freeEntry(&(data->entries[i])); + } + free(data->entries); + csv_init(data); +} + +// Remove all data from structure +void csv_freeEntry(tCSVEntry* entry) { + int i; + + if(entry->fields != NULL) { + for(i = 0; i < entry->numFields; i++) { + free(entry->fields[i]); + entry->fields[i] = NULL; + } + + free(entry->fields); + } + if(entry->type != NULL) { + free(entry->type); + } + csv_initEntry(entry); +} + +// Get the number of entries +int csv_numEntries(tCSVData data) { + return data.count; +} + +// Get the type of information contained in the entry +const char* csv_getType(tCSVEntry* entry) { + return (const char*)entry->type; +} + +// Get an entry from the CSV data +tCSVEntry* csv_getEntry(tCSVData data, int position) { + return &(data.entries[position]); +} + +// Get the number of fields for a given entry +int csv_numFields(tCSVEntry entry) { + return entry.numFields; +} + +// Get a field from the given entry as integer +int csv_getAsInteger(tCSVEntry entry, int position) { + return atoi(entry.fields[position]); +} + +// Get a field from the given entry as string +void csv_getAsString(tCSVEntry entry, int position, char* buffer, int length) { + memset(buffer, 0, length); + strncpy(buffer, entry.fields[position], length - 1); +} + +// Get a field from the given entry as integer +float csv_getAsReal(tCSVEntry entry, int position) { + return atof(entry.fields[position]); +} \ No newline at end of file diff --git a/UOCContacts/src/date.c b/UOCContacts/src/date.c new file mode 100644 index 0000000..c549002 --- /dev/null +++ b/UOCContacts/src/date.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include "date.h" + +// Parse a tDateTime from string information +void dateTime_parse(tDateTime* dateTime, const char* date, const char* time) { + // Check output data + assert(dateTime != NULL); + + // Check input date + assert(date != NULL); + assert(strlen(date) == 10); + + // Check input time + assert(time != NULL); + assert(strlen(time) == 5); + + // Parse the input date + sscanf(date, "%d/%d/%d", &(dateTime->date.day), &(dateTime->date.month), &(dateTime->date.year)); + + // Parse the input time + sscanf(time, "%d:%d", &(dateTime->time.hour), &(dateTime->time.minutes)); +} + +// Compare two tDateTime structures and return true if they contain the same value or false otherwise. +bool dateTime_cmp(tDateTime dateTime1, tDateTime dateTime2) { + if (dateTime1.date.day != dateTime2.date.day || dateTime1.date.month != dateTime2.date.month || dateTime1.date.year != dateTime2.date.year) { + return false; + } + + return dateTime1.time.hour == dateTime2.time.hour && dateTime1.time.minutes == dateTime2.time.minutes; +} diff --git a/UOCContacts/src/person.c b/UOCContacts/src/person.c new file mode 100644 index 0000000..41b530b --- /dev/null +++ b/UOCContacts/src/person.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include "person.h" + + +// Initialize the population data +void population_init(tPopulation* data) { + // Check input/output data + assert(data != NULL); + + data->elems = NULL; + data->count = 0; +} + +// Remove the data from a person +void person_free(tPerson* data) { + // Check input data + assert(data != NULL); + + // Release document data + if(data->document != NULL) free(data->document); + data->document = NULL; + + // Release name data + if(data->name != NULL) free(data->name); + data->name = NULL; + + // Release surname data + if(data->surname != NULL) free(data->surname); + data->surname = NULL; + + // Release email data + if(data->email != NULL) free(data->email); + data->email = NULL; + + // Release address data + if(data->address != NULL) free(data->address); + data->address = NULL; + + // Release cp data + if(data->cp != NULL) free(data->cp); + data->cp = NULL; +} + +// Remove the data from all persons +void population_free(tPopulation* data) { + int i; + + // Check input data + assert(data != NULL); + + // Remove contents + for(i = 0; i < data->count; i++) { + person_free(&(data->elems[i])); + } + + // Release memory + if (data->count > 0) { + free(data->elems); + data->elems = NULL; + data->count = 0; + } +} + + +// Parse input from CSVEntry +void person_parse(tPerson* data, tCSVEntry entry) { + // Check input data + assert(data != NULL); + + // Check entry fields + assert(csv_numFields(entry) == 7); + + // Copy identity document data + data->document = (char*) malloc((strlen(entry.fields[0]) + 1) * sizeof(char)); + assert(data->document != NULL); + memset(data->document, 0, (strlen(entry.fields[0]) + 1) * sizeof(char)); + csv_getAsString(entry, 0, data->document, strlen(entry.fields[0]) + 1); + + // Copy name data + data->name = (char*) malloc((strlen(entry.fields[1]) + 1) * sizeof(char)); + assert(data->name != NULL); + memset(data->name, 0, (strlen(entry.fields[1]) + 1) * sizeof(char)); + csv_getAsString(entry, 1, data->name, strlen(entry.fields[1]) + 1); + + // Copy surname data + data->surname = (char*) malloc((strlen(entry.fields[2]) + 1) * sizeof(char)); + assert(data->surname != NULL); + memset(data->surname, 0, (strlen(entry.fields[2]) + 1) * sizeof(char)); + csv_getAsString(entry, 2, data->surname, strlen(entry.fields[2]) + 1); + + // Copy email data + data->email = (char*) malloc((strlen(entry.fields[3]) + 1) * sizeof(char)); + assert(data->email != NULL); + memset(data->email, 0, (strlen(entry.fields[3]) + 1) * sizeof(char)); + csv_getAsString(entry, 3, data->email, strlen(entry.fields[3]) + 1); + + // Copy address data + data->address = (char*) malloc((strlen(entry.fields[4]) + 1) * sizeof(char)); + assert(data->address != NULL); + memset(data->address, 0, (strlen(entry.fields[4]) + 1) * sizeof(char)); + csv_getAsString(entry, 4, data->address, strlen(entry.fields[4]) + 1); + + // Copy cp data + data->cp = (char*) malloc((strlen(entry.fields[5]) + 1) * sizeof(char)); + assert(data->cp != NULL); + memset(data->cp, 0, (strlen(entry.fields[5]) + 1) * sizeof(char)); + csv_getAsString(entry, 5, data->cp, strlen(entry.fields[5]) + 1); + + // Check birthday lenght + assert(strlen(entry.fields[6]) == 10); + // Parse the birthday date + sscanf(entry.fields[6], "%d/%d/%d", &(data->birthday.day), &(data->birthday.month), &(data->birthday.year)); +} + +// Add a new person +void population_add(tPopulation* data, tPerson person) { + // Check input data + assert(data != NULL); + + // If person does not exist add it + if(population_find(data[0], person.document) < 0) { + // Allocate memory for new element + if (data->count == 0) { + // Request new memory space + data->elems = (tPerson*) malloc(sizeof(tPerson)); + } else { + // Modify currently allocated memory + data->elems = (tPerson*) realloc(data->elems, (data->count + 1) * sizeof(tPerson)); + } + assert(data->elems != NULL); + + // Copy the data to the new position + person_cpy(&(data->elems[data->count]), person); + + // Increase the number of elements + data->count ++; + } +} + +// Remove a person +void population_del(tPopulation* data, const char *document) { + int i; + int pos; + + // Check input data + assert(data != NULL); + + // Find if it exists + pos = population_find(data[0], document); + + if (pos >= 0) { + // Remove current position memory + person_free(&(data->elems[pos])); + // Shift elements + for(i = pos; i < data->count-1; i++) { + // Copy address of element on position i+1 to position i + data->elems[i] = data->elems[i+1]; + } + // Update the number of elements + data->count--; + // Resize the used memory + if (data->count == 0) { + // No element remaining + free(data->elems); + data->elems = NULL; + } else { + // Still some elements are remaining + data->elems = (tPerson*)realloc(data->elems, data->count * sizeof(tPerson)); + } + } +} + +// Return the position of a person with provided document. -1 if it does not exist +int population_find(tPopulation data, const char* document) { + int i; + + for(i = 0; i < data.count; i++) { + if(strcmp(data.elems[i].document, document) == 0 ) { + return i; + } + } + + return -1; +} + +// Print the person data +void population_print(tPopulation data) { + int i; + + for(i = 0; i < data.count; i++) { + // Print position and document + printf("%d;%s;", i, data.elems[i].document); + // Print name and surname + printf("%s;%s;", data.elems[i].name, data.elems[i].surname); + // Print email + printf("%s;", data.elems[i].email); + // Print address and CP + printf("%s;%s;", data.elems[i].address, data.elems[i].cp); + // Print birthday date + printf("%02d/%02d/%04d\n", data.elems[i].birthday.day, data.elems[i].birthday.month, data.elems[i].birthday.year); + } +} + +// Copy the data from the source to destination +void person_cpy(tPerson* destination, tPerson source) { + // Copy identity document data + destination->document = (char*) malloc((strlen(source.document) + 1) * sizeof(char)); + assert(destination->document != NULL); + strcpy(destination->document, source.document); + + // Copy name data + destination->name = (char*) malloc((strlen(source.name) + 1) * sizeof(char)); + assert(destination->name != NULL); + strcpy(destination->name, source.name); + + // Copy surname data + destination->surname = (char*) malloc((strlen(source.surname) + 1) * sizeof(char)); + assert(destination->surname != NULL); + strcpy(destination->surname, source.surname); + + // Copy email data + destination->email = (char*) malloc((strlen(source.email) + 1) * sizeof(char)); + assert(destination->email != NULL); + strcpy(destination->email, source.email); + + // Copy address data + destination->address = (char*) malloc((strlen(source.address) + 1) * sizeof(char)); + assert(destination->address != NULL); + strcpy(destination->address, source.address); + + // Copy cp data + destination->cp = (char*) malloc((strlen(source.cp) + 1) * sizeof(char)); + assert(destination->cp != NULL); + strcpy(destination->cp, source.cp); + + // Copy the birthday date + destination->birthday = source.birthday; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..bd86ce3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include "test.h" +#include "api.h" + +typedef struct _AppArguments { + char* app_name; + char* out_file; + char* in_file; + bool wait_on_exit; +} tAppArguments; + +// Wait user key +void waitKey(tAppArguments parameters) { + if (parameters.wait_on_exit) { + printf("Press key to continue..."); + getchar(); + } +} + +// Display help text +void help(const char* app_name) { + printf("%s [--help] [--in ] [--out ] [--test-out ]\n", app_name); + printf("\t[%s] %s\n", "--help", "Show this help information."); + printf("\t[%s] %s\n", "--no-wait", "Do not wait user key press on exit."); + printf("\t[%s] %s\n", "--in", "Provide file with input test data in CSV format."); + printf("\t[%s] %s\n", "--out", "Write the result of tests in a file in JSON format."); +} + +bool parseArguments(tAppArguments* arguments, int argc, char **argv) { + int i; + + // Initialize the arguments + arguments->app_name = NULL; + arguments->out_file = NULL; + arguments->in_file = NULL; + arguments->wait_on_exit = true; + + // Parse input arguments + arguments->app_name = argv[0]; + for (i=1; i < argc; i += 2) { + if (strcmp(argv[i], "--help") == 0) { + help(argv[0]); + } + if (strcmp(argv[i], "--no-wait") == 0) { + arguments->wait_on_exit = false; + } + if (strcmp(argv[i], "--in") == 0) { + if (argc < i + 1) { + help(argv[0]); + return false; + } + arguments->in_file = argv[i+1]; + } + if (strcmp(argv[i], "--out") == 0) { + if (argc < i + 1) { + help(argv[0]); + return false; + } + arguments->out_file = argv[i+1]; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + tAppArguments parameters; + tTestSuite testSuite; + + // Parse input arguments + if (!parseArguments(¶meters, argc, argv)) { + printf("ERROR: Invalid input arguments"); + // Wait user to press a key to ensure error is shown + waitKey(parameters); + // Exit with error + exit(EXIT_FAILURE); + } + + // Initialize the test suite + testSuite_init(&testSuite); + + // Run all tests + testSuite_run(&testSuite, parameters.in_file); + + // Print test results + testSuite_print(&testSuite); + + // Store test results + if (parameters.out_file != NULL) { + testSuite_export(&testSuite, parameters.out_file); + printf("Restults stored in %s\n", parameters.out_file); + } + + // Remove test suite data + testSuite_free(&testSuite); + + // Wait user to press a key to ensure results are shown + waitKey(parameters); + + exit(EXIT_SUCCESS); +} diff --git a/test/include/test.h b/test/include/test.h new file mode 100644 index 0000000..3f56b2e --- /dev/null +++ b/test/include/test.h @@ -0,0 +1,11 @@ +#ifndef __TEST__H +#define __TEST__H +#include "test_suite.h" + +// Run all available tests +void testSuite_run(tTestSuite* test_suite, const char* input); + +// Check if README.txt is available and have the correct information +//void readReadme(tTestSuite* test_suite); + +#endif // __TEST__H \ No newline at end of file diff --git a/test/include/test_pr1.h b/test/include/test_pr1.h new file mode 100644 index 0000000..b87a02e --- /dev/null +++ b/test/include/test_pr1.h @@ -0,0 +1,20 @@ +#ifndef __TEST_PR1_H__ +#define __TEST_PR1_H__ + +#include +#include "test_suite.h" + +// Run all tests for PR1 +bool run_pr1(tTestSuite* test_suite, const char* input); + +// Run tests for PR1 exercice 1 +bool run_pr1_ex1(tTestSection* test_section, const char* input); + +// Run tests for PR1 exercice 2 +bool run_pr1_ex2(tTestSection* test_section, const char* input); + +// Run tests for PR1 exercice 3 +bool run_pr1_ex3(tTestSection* test_section, const char* input); + + +#endif // __TEST_PR1_H__ \ No newline at end of file diff --git a/test/include/test_suite.h b/test/include/test_suite.h new file mode 100644 index 0000000..1b6557c --- /dev/null +++ b/test/include/test_suite.h @@ -0,0 +1,151 @@ +#ifndef __TEST_SUITE__H +#define __TEST_SUITE__H +#include +#include +#include + +// #define PRINT_TEST_PROGRESS + +// Size of the buffer used to read files data +#define BUFFER_SIZE 2048 + +// Status of a test +typedef enum { + TEST_RUNNING, + TEST_NOT_IMPLEMENTED, + TEST_PASSED, + TEST_FAILED +} tTestResult; + +// A single test +typedef struct { + // Code of the test + char* code; + // Description of the test + char* description; + // Result of the test + tTestResult result; +} tTest; + +// Grup of tests +typedef struct { + // Code of the test section + char* code; + // Title of the section + char* title; + // Number of tests + int numTests; + // Array of tests + tTest* tests; +} tTestSection; + +// Test suit learner data +typedef struct { + // Email + char* email; + // Username + char* username; + // First name + char* first_name; + // Last name + char* last_name; + // Environment + char* environment; + // Whether the learner data file exists or not + bool file_exists; +} tLearnerData; + +// Test suit with multiple sections +typedef struct { + // Learner data + tLearnerData learner; + // Number of sections + int numSections; + // Array of sections + tTestSection* sections; +} tTestSuite; + +// Initialize a test Suite +void testSuite_init(tTestSuite* object); + +// Load learner data +bool testSuite_load_learner(tTestSuite* object, const char* file); + +// Remove a test Suite +void testSuite_free(tTestSuite* object); + +// Add a test Section +void testSuite_addSection(tTestSuite* object, const char* code, const char* title); + +// Add a test +void testSuite_addTest(tTestSuite* object, const char* section_code, const char* code, const char* description, tTestResult result); + +// Update a test result +void testSuite_updateTest(tTestSuite* object, const char* section_code, const char* test_code, tTestResult result); + +// Get a pointer to a section +tTestSection* testSuite_getSection(tTestSuite* object, const char* section_code); + +// Get a pointer to a test +tTest* testSuite_getTest(tTestSuite* object, const char* section_code, const char* test_code); + +// Get test statistics +void testSuite_getStats(tTestSuite* object, int* total, int* passed, int* failed, int* not_implemented); + +// Print test suite +void testSuite_print(tTestSuite* object); + +// Export a test suite +void testSuite_export(tTestSuite* object, const char* output); + + + +// Initialize a test Section +void testSection_init(tTestSection* object, const char* code, const char* title); + +// Remove a test Section +void testSection_free(tTestSection* object); + +// Add a test to the Section +void testSection_addTest(tTestSection* object, const char* code, const char* description, tTestResult result); + +// Update a test result +void testSection_updateTest(tTestSection* object, const char* test_code, tTestResult result); + +// Get a pointer to a test +tTest* testSection_getTest(tTestSection* object, const char* test_code); + +// Get test statistics +void testSection_getStats(tTestSection* object, int* total, int* passed, int* failed, int* not_implemented); + +// Print test section +void testSection_print(tTestSection* object); + +// Export a test section +void testSection_export(tTestSection* object, FILE* fout); + + + +// Initialize a test +void test_init(tTest* object, const char* code, const char* description, tTestResult result); + +// Remove a test +void test_free(tTest* object); + +// Update a test result +void test_updateTest(tTest* object, tTestResult result); + +// Print test +void test_print(tTest* object); + +// Export a test +void test_export(tTest* object, FILE* fout); + + +// Start a test +void start_test(tTestSection* section, const char* code, const char* description); + +// Finish a test +void end_test(tTestSection* section, const char* code, bool passed); + +#endif // __TEST_SUITE__H \ No newline at end of file diff --git a/test/src/test.c b/test/src/test.c new file mode 100644 index 0000000..419f3a1 --- /dev/null +++ b/test/src/test.c @@ -0,0 +1,39 @@ +#include +#include +#include "test.h" +#include "test_pr1.h" + +// Define default test data +const char* test_data_str = "PERSON;87654321K;John;Smith;john.smith@example.com;My street, 25;08001;30/12/1980\n" \ + "PERSON;98765432J;Jane;Doe;jane.doe@example.com;Her street, 5;08500;12/01/1995\n" \ + "GEOLOCATION;87654321K;15/10/2021;13:41;41.3870;2.1698\n" \ + "GEOLOCATION;87654321K;15/10/2021;13:45;41.3870;2.1695\n" \ + "GEOLOCATION;98765432J;15/10/2021;13:50;41.3871;2.1697\n" \ + "GEOLOCATION;87654321K;15/10/2021;13:50;41.3871;2.1697\n"; + +// Run all available tests +void testSuite_run(tTestSuite* test_suite, const char* input) { + const char* default_input = "default_data.csv"; + FILE *fout; + const char* filename; + + assert(test_suite != NULL); + + // Load the README.txt file + testSuite_load_learner(test_suite, "../README.txt"); + + // If no file is provided, use default data + if (input == NULL) { + filename = default_input; + fout = fopen(filename, "w"); + assert(fout != NULL); + fwrite(test_data_str, strlen(test_data_str), 1, fout); + fclose(fout); + } else { + filename = input; + } + + // Run tests for PR1 + run_pr1(test_suite, filename); +} + diff --git a/test/src/test_pr1.c b/test/src/test_pr1.c new file mode 100644 index 0000000..e186ba8 --- /dev/null +++ b/test/src/test_pr1.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include "test_pr1.h" +#include "api.h" + +// Run all tests for PR1 +bool run_pr1(tTestSuite* test_suite, const char* input) { + bool ok = true; + tTestSection* section = NULL; + + assert(test_suite != NULL); + + testSuite_addSection(test_suite, "PR1", "Tests for PR1 exercices"); + + section = testSuite_getSection(test_suite, "PR1"); + assert(section != NULL); + + ok = run_pr1_ex1(section, input); + ok = run_pr1_ex2(section, input) && ok; + ok = run_pr1_ex3(section, input) && ok; + + return ok; +} + +// Run all tests for Exercice 1 of PR1 +bool run_pr1_ex1(tTestSection* test_section, const char* input) { + bool passed = true, failed = false; + const char* version; + + ///////////////////////////// + ///// PR1 EX1 TEST 1 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX1_1", "Read version information."); + // Get the version+ + version = api_version(); + if (strcmp(version, "UOC PP 20211") != 0) { + failed = true; + passed = false; + } + end_test(test_section, "PR1_EX1_1", !failed); + + return passed; +} + +// Run all tests for Exercice 2 of PR1 +bool run_pr1_ex2(tTestSection* test_section, const char* input) { + tApiData data; + tApiError error; + tCSVEntry entry; + tCSVEntry entry2; + bool passed = true, failed = false; + bool fail_all = false; + + ///////////////////////////// + ///// PR1 EX2 TEST 1 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_1", "Initialize the API data structure"); + // Initialize the data + error = api_initData(&data); + if (error != E_SUCCESS) { + failed = true; + passed = false; + fail_all = true; + } + end_test(test_section, "PR1_EX2_1", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 2 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_2", "Load data from file"); + // Load basic data to the API + if (fail_all) { + failed = true; + } else { + error = api_loadData(&data, input, true); + if (error != E_SUCCESS) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_2", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 3 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_3", "Add an entry with invalid type"); + // Create an entry with invalid type + csv_initEntry(&entry); + csv_parseEntry(&entry, "68765432L;Jane;Doe;jane.doe2@example.com;Her street, 15;08500;12/01/1995", "INVALID_TYPE"); + if (fail_all) { + failed = true; + } else { + // Add the entry + error = api_addDataEntry(&data, entry); + if (error != E_INVALID_ENTRY_TYPE) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_3", !failed); + // Free memory + csv_freeEntry(&entry); + + ///////////////////////////// + ///// PR1 EX2 TEST 4 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_4", "Add a person entry with invalid format"); + // Create an entry with invalid number of fields + csv_initEntry(&entry); + csv_parseEntry(&entry, "PERSON;68765432L;Jane;Doe;jane.doe2@example.com;Her street, 15;08500", NULL); + if (fail_all) { + failed = true; + } else { + error = api_addDataEntry(&data, entry); + if (error != E_INVALID_ENTRY_FORMAT) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_4", !failed); + + // Free memory + csv_freeEntry(&entry); + + + ///////////////////////////// + ///// PR1 EX2 TEST 5 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_5", "Add a geoposition entry with invalid format"); + // Create an entry with invalid number of fields + csv_initEntry(&entry); + csv_parseEntry(&entry, "GEOLOCATION;87654321K;15/10/2021;13:41;41.3870;2.1698;Extra comment", NULL); + if (fail_all) { + failed = true; + } else { + error = api_addDataEntry(&data, entry); + if (error != E_INVALID_ENTRY_FORMAT) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_5", !failed); + // Free memory + csv_freeEntry(&entry); + + ///////////////////////////// + ///// PR1 EX2 TEST 6 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_6", "Add a duplicated person"); + // Create an entry + csv_initEntry(&entry); + csv_parseEntry(&entry, "PERSON;68765432L;Jane;Doe;jane.doe2@example.com;Her street, 15;08500;12/01/1995", NULL); + + if (fail_all) { + failed = true; + } else { + // Insert once to ensure it is present + api_addDataEntry(&data, entry); + // Try to insert it again + error = api_addDataEntry(&data, entry); + if (error != E_DUPLICATED_ENTRY) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_6", !failed); + // Free memory + csv_freeEntry(&entry); + + ///////////////////////////// + ///// PR1 EX2 TEST 7 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_7", "Add a duplicated geoposition"); + // Create an entry + csv_initEntry(&entry); + csv_parseEntry(&entry, "GEOLOCATION;87654321K;15/10/2021;13:41;41.3870;2.1698", NULL); + if (fail_all) { + failed = true; + } else { + // Insert once to ensure it is present + api_addDataEntry(&data, entry); + // Try to insert it again + error = api_addDataEntry(&data, entry); + if (error != E_DUPLICATED_ENTRY) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_7", !failed); + // Free memory + csv_freeEntry(&entry); + + ///////////////////////////// + ///// PR1 EX2 TEST 8 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_8", "Remove all data"); + + // Create a person entry + csv_initEntry(&entry); + csv_parseEntry(&entry, "PERSON;87654321K;Jane;Doe;jane.doe2@example.com;Her street, 15;08500;12/01/1995", NULL); + + // Create a geodata entry + csv_initEntry(&entry2); + csv_parseEntry(&entry2, "GEOLOCATION;87654321K;15/10/2021;13:41;41.3870;2.1698", NULL); + if (fail_all) { + failed = true; + } else { + // Ensure we cannot insert them + if (api_addDataEntry(&data, entry) != E_DUPLICATED_ENTRY || api_addDataEntry(&data, entry2) != E_DUPLICATED_ENTRY) { + failed = true; + passed = false; + } else { + // Remove all data + api_freeData(&data); + + // Try to insert again + if (api_addDataEntry(&data, entry) != E_SUCCESS || api_addDataEntry(&data, entry2) != E_SUCCESS) { + failed = true; + passed = false; + } + } + } + end_test(test_section, "PR1_EX2_8", !failed); + // Free memory + csv_freeEntry(&entry); + csv_freeEntry(&entry2); + + return passed; +} + +// Run all tests for Exercice 3 of PR1 +bool run_pr1_ex3(tTestSection* test_section, const char* input) { + tApiData data; + tApiError error; + tCSVEntry entry; + tCSVEntry person; + tCSVData geoData; + bool passed = true, failed = false; + bool fail_all = false; + + // Load basic data to the API + if (api_initData(&data) != E_SUCCESS) { + fail_all = true; + } + if (!fail_all && api_loadData(&data, input, true) != E_SUCCESS) { + fail_all = true; + } + + ///////////////////////////// + ///// PR1 EX3 TEST 1 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_1", "Find an exixting person"); + if (fail_all) { + failed = true; + } else { + // Create a person entry to ensure it exists + csv_initEntry(&entry); + csv_parseEntry(&entry, "PERSON;87654321K;Jane;Doe;jane.doe2@example.com;Her street, 15;08500;12/01/1995", NULL); + api_addDataEntry(&data, entry); + error = api_findPerson(&data, "87654321K", &person); + if (error != E_SUCCESS || csv_numFields(person) != 7) { + failed = true; + } + // Free memory + csv_freeEntry(&entry); + csv_freeEntry(&person); + + } + end_test(test_section, "PR1_EX3_1", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 2 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_2", "Find a non exixting person"); + if (fail_all) { + failed = true; + } else { + error = api_findPerson(&data, "00000000L", &person); + if (error != E_PERSON_NOT_FOUND) { + failed = true; + } + } + end_test(test_section, "PR1_EX3_2", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 3 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_3", "Get geoplocation from an exixting person"); + if (fail_all) { + failed = true; + } else { + error = api_getPersonGeolocation(&data, "87654321K", &geoData); + if (error != E_SUCCESS) { + failed = true; + } + // Free memory + csv_free(&geoData); + } + end_test(test_section, "PR1_EX3_3", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 4 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_4", "Get geoplocation from a non exixting person"); + if (fail_all) { + failed = true; + } else { + error = api_getPersonGeolocation(&data, "00000000L", &geoData); + if (error != E_PERSON_NOT_FOUND) { + failed = true; + } + } + end_test(test_section, "PR1_EX3_4", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 5 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_5", "Remove an exixting person"); + if (fail_all) { + failed = true; + } else { + error = api_removePerson(&data, "87654321K"); + if (error != E_SUCCESS) { + failed = true; + } else { + error = api_getPersonGeolocation(&data, "87654321K", &geoData); + if (error != E_PERSON_NOT_FOUND) { + failed = true; + } + } + } + end_test(test_section, "PR1_EX3_5", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 6 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_6", "Remove a non exixting person"); + if (fail_all) { + failed = true; + } else { + if (error != E_PERSON_NOT_FOUND) { + failed = true; + } + } + end_test(test_section, "PR1_EX3_6", !failed); + + return passed; +} \ No newline at end of file diff --git a/test/src/test_suite.c b/test/src/test_suite.c new file mode 100644 index 0000000..111ec06 --- /dev/null +++ b/test/src/test_suite.c @@ -0,0 +1,559 @@ +#include +#include +#include +#include "test_suite.h" + +// Initialize a test Suite +void testSuite_init(tTestSuite* object) { + assert(object != NULL); + object->learner.email = NULL; + object->learner.username = NULL; + object->learner.first_name = NULL; + object->learner.last_name = NULL; + object->learner.environment = NULL; + object->learner.file_exists = false; + object->numSections = 0; + object->sections = NULL; +} + +// Load learner data +bool testSuite_load_learner(tTestSuite* object, const char* file) { + char buffer[BUFFER_SIZE]; + FILE *fin=NULL; + int state; + char* pos; + + assert(object != NULL); + assert(file != NULL); + + fin = fopen(file, "r"); + if (fin == NULL) { + object->learner.email = NULL; + object->learner.username = NULL; + object->learner.first_name = NULL; + object->learner.last_name = NULL; + object->learner.environment = NULL; + object->learner.file_exists = false; + return false; + } + + state = 0; + while (fgets(buffer, BUFFER_SIZE, fin)) { + if (state == 0) { + // Read email + object->learner.email = (char*) malloc((strlen(buffer) + 1) * sizeof(char)); + memset(object->learner.email, 0, (strlen(buffer) + 1) * sizeof(char)); + strncpy(object->learner.email, buffer, strcspn(buffer, "\n\r")); + } else if(state == 1) { + // Read Surname, Name + pos = strchr(buffer, ','); + if (pos > 0) { + // Copy the surnames + object->learner.last_name = (char*) malloc((pos - buffer) * sizeof(char)); + memset(object->learner.last_name, 0, (pos - buffer) * sizeof(char)); + strncpy(object->learner.last_name, buffer, strcspn(buffer, ",") - 1); + // Skip the comma + pos++; + // Skip initial blank spaces + while (pos < &buffer[BUFFER_SIZE] && *pos==' ') pos++; + // Copy the first name + object->learner.first_name = (char*) malloc((strlen(pos) + 1) * sizeof(char)); + memset(object->learner.first_name, 0, (strlen(pos) + 1) * sizeof(char)); + strncpy(object->learner.first_name, pos, strcspn(pos, "\n\r")); + } else { + object->learner.first_name = (char*) malloc((strlen(buffer) + 1) * sizeof(char)); + memset(object->learner.first_name, 0, (strlen(buffer) + 1) * sizeof(char)); + strcpy(object->learner.first_name, buffer); + } + } else if(state == 2) { + object->learner.environment = (char*) malloc((strlen(buffer) + 1) * sizeof(char)); + strcpy(object->learner.environment, buffer); + } else if(state > 2) { + object->learner.environment = (char*) realloc(object->learner.environment, (strlen(object->learner.environment) + strlen(buffer) + 1) * sizeof(char)); + strcat(object->learner.environment, buffer); + } + state ++; + } + + object->learner.file_exists = true; + + fclose(fin); + return true; +} + +// Remove a test Suite +void testSuite_free(tTestSuite* object) { + int i; + assert(object != NULL); + // Free learner data + if(object->learner.email != NULL) { + free(object->learner.email); + object->learner.email = NULL; + } + if(object->learner.username != NULL) { + free(object->learner.username); + object->learner.username = NULL; + } + if(object->learner.first_name != NULL) { + free(object->learner.first_name); + object->learner.first_name = NULL; + } + if(object->learner.last_name != NULL) { + free(object->learner.last_name); + object->learner.last_name = NULL; + } + if(object->learner.environment != NULL) { + free(object->learner.environment); + object->learner.environment = NULL; + } + object->learner.file_exists = false; + // Free test data + if(object->sections != NULL) { + for(i = 0; i < object->numSections; i++) { + testSection_free(&(object->sections[i])); + } + free(object->sections); + } +} + +// Add a test Section +void testSuite_addSection(tTestSuite* object, const char* code, const char* title) { + assert(object != NULL); + object->numSections++; + if(object->sections == NULL) { + object->sections = (tTestSection*)malloc(object->numSections * sizeof(tTestSection)); + } else { + object->sections = (tTestSection*)realloc(object->sections, object->numSections * sizeof(tTestSection)); + } + assert(object->sections != NULL); + testSection_init(&(object->sections[object->numSections - 1]), code, title); +} + +// Add a test +void testSuite_addTest(tTestSuite* object, const char* section_code, const char* code, const char* description, tTestResult result) { + tTestSection* section = NULL; + assert(object != NULL); + assert(section_code != NULL); + assert(code != NULL); + assert(description != NULL); + + section = testSuite_getSection(object, section_code); + assert(section != NULL); + + testSection_addTest(section, code, description, result); +} + +// Update a test result +void testSuite_updateTest(tTestSuite* object, const char* section_code, const char* test_code, tTestResult result) { + tTestSection* section = NULL; + + assert(object != NULL); + assert(section_code != NULL); + assert(test_code != NULL); + + section = testSuite_getSection(object, section_code); + assert(section != NULL); + + testSection_updateTest(section, test_code, result); +} + +// Get a pointer to a section +tTestSection* testSuite_getSection(tTestSuite* object, const char* section_code) { + int i; + + assert(object != NULL); + assert(section_code != NULL); + for(i = 0; i < object->numSections; i++) { + if(strcmp(object->sections[i].code, section_code) == 0) { + return &(object->sections[i]); + } + } + return NULL; +} + +// Get a pointer to a test +tTest* testSuite_getTest(tTestSuite* object, const char* section_code, const char* test_code) { + tTestSection* section = NULL; + + assert(object != NULL); + assert(section_code != NULL); + assert(test_code != NULL); + section = testSuite_getSection(object, section_code); + assert(section != NULL); + + if(section != NULL) { + return testSection_getTest(section, test_code); + } + + return NULL; +} + +// Get test statistics +void testSuite_getStats(tTestSuite* object, int* total, int* passed, int* failed, int* not_implemented) { + int i; + int s_total, s_passed, s_failed, s_not_implemented; + assert(object != NULL); + assert(total != NULL); + assert(passed != NULL); + assert(failed != NULL); + assert(not_implemented != NULL); + + *total = 0; + *passed = 0; + *failed = 0; + *not_implemented = 0; + for(i = 0; i < object->numSections; i++) { + testSection_getStats(&(object->sections[i]), &s_total, &s_passed, &s_failed, &s_not_implemented); + *total += s_total; + *passed += s_passed; + *failed += s_failed; + *not_implemented += s_not_implemented; + } +} + +// Print test suite +void testSuite_print(tTestSuite* object) { + int i; + int total, passed, failed, not_implemented; + + assert(object != NULL); + + // Print the header + if (object->learner.file_exists) { + printf("\n=========================================================================\n"); + if (object->learner.first_name != NULL && object->learner.last_name != NULL) { + printf("\t Name: %s %s\n", object->learner.first_name, object->learner.last_name); + } else { + printf("\t Name: \n"); + } + if (object->learner.email != NULL) { + printf("\t Email: %s\n", object->learner.email); + } else { + printf("\t Email: \n"); + } + printf("=========================================================================\n"); + } else { + printf("\n=========================================================================\n"); + printf("\t NO LEARNER DATA\n"); + printf("=========================================================================\n"); + } + + testSuite_getStats(object, &total, &passed, &failed, ¬_implemented); + + printf("\n=========================================================================\n"); + printf("\t TEST RESULTS\n"); + printf("=========================================================================\n"); + if(object->numSections == 0) { + printf("NO TEST DEFINED\n"); + } else { + for(i = 0; i < object->numSections; i++) { + testSection_print(&(object->sections[i])); + } + } + printf("\n=========================================================================\n"); + if(total > 0) { + printf("Total Tests: %d\n", total); + printf("Passed Tests: %d ( %2.02f %% )\n", passed, ((float)passed / (float)total) * 100.0); + printf("Failed Tests: %d ( %2.02f %% )\n", failed, ((float)failed / (float)total) * 100.0); + //printf("Not Implemented: %d ( %2.02f %% )\n", not_implemented, ((float)not_implemented/(float)total)*100.0); + printf("=========================================================================\n"); + } +} + +void write_nullable_field(FILE* fout, const char* value) { + if (value == NULL) { + fprintf(fout, "null"); + } else { + fprintf(fout, "\"%s\"", value); + } +} + +// Export a test suite +void testSuite_export(tTestSuite* object, const char* output) { + int i; + int total, passed, failed, not_implemented; + FILE* fout = NULL; + + assert(object != NULL); + assert(output != NULL); + + fout = fopen(output, "w"); + assert(fout != NULL); + + fprintf(fout, "{ \"learner\": {\"first_name\": "); + write_nullable_field(fout, object->learner.first_name); + fprintf(fout, ", \"last_name\": "); + write_nullable_field(fout, object->learner.last_name); + fprintf(fout, ", \"email\": "); + write_nullable_field(fout, object->learner.email); + fprintf(fout, ", \"username\": "); + write_nullable_field(fout, object->learner.username); + fprintf(fout, ", \"environment\": "); + write_nullable_field(fout, object->learner.environment); + fprintf(fout, "}, "); + + testSuite_getStats(object, &total, &passed, &failed, ¬_implemented); + + fprintf(fout, "{ \"total\": %d, \"passed\": %d, \"failed\": %d, \"not_implemented\": %d, \"sections\": [", total, passed, failed, not_implemented); + + for(i = 0; i < object->numSections; i++) { + if(i > 0) { + fprintf(fout, ", "); + } + testSection_export(&(object->sections[i]), fout); + } + + fprintf(fout, "]}"); + fclose(fout); +} + + +// Initialize a test Section +void testSection_init(tTestSection* object, const char* code, const char* title) { + assert(object != NULL); + object->code = (char*) malloc((strlen(code) + 1) * sizeof(char)); + assert(object->code != NULL); + strcpy(object->code, code); + object->title = (char*) malloc((strlen(title) + 1) * sizeof(char)); + assert(object->title != NULL); + strcpy(object->title, title); + object->numTests = 0; + object->tests = NULL; +} + +// Remove a test Section +void testSection_free(tTestSection* object) { + int i; + assert(object != NULL); + assert(object->code != NULL); + + free(object->code); + free(object->title); + if(object->tests != NULL) { + for(i = 0; i < object->numTests; i++) { + test_free(&(object->tests[i])); + } + free(object->tests); + } + +} + +// Add a test to the Section +void testSection_addTest(tTestSection* object, const char* code, const char* description, tTestResult result) { + assert(object != NULL); + object->numTests++; + if(object->tests == NULL) { + object->tests = (tTest*)malloc(object->numTests * sizeof(tTest)); + } else { + object->tests = (tTest*)realloc(object->tests, object->numTests * sizeof(tTestSection)); + } + assert(object->tests != NULL); + test_init(&(object->tests[object->numTests - 1]), code, description, result); +} + +// Update a test result +void testSection_updateTest(tTestSection* object, const char* test_code, tTestResult result) { + tTest* test = NULL; + + assert(object != NULL); + test = testSection_getTest(object, test_code); + + assert(test != NULL); + test_updateTest(test, result); +} + +// Get a pointer to a test +tTest* testSection_getTest(tTestSection* object, const char* test_code) { + int i; + assert(object != NULL); + assert(test_code != NULL); + for(i = 0; i < object->numTests; i++) { + if(strcmp(object->tests[i].code, test_code) == 0) { + return &(object->tests[i]); + } + } + return NULL; +} + +// Get test statistics +void testSection_getStats(tTestSection* object, int* total, int* passed, int* failed, int* not_implemented) { + int i; + assert(object != NULL); + assert(total != NULL); + assert(passed != NULL); + assert(failed != NULL); + assert(not_implemented != NULL); + + *total = object->numTests; + *passed = 0; + *failed = 0; + *not_implemented = 0; + for(i = 0; i < object->numTests; i++) { + if(object->tests[i].result == TEST_PASSED) { + (*passed)++; + } else + if(object->tests[i].result == TEST_FAILED) { + (*failed)++; + } else + if(object->tests[i].result == TEST_FAILED) { + (*not_implemented)++; + } + } +} + +// Print test section +void testSection_print(tTestSection* object) { + int i; + int total, passed, failed, not_implemented; + + assert(object != NULL); + + testSection_getStats(object, &total, &passed, &failed, ¬_implemented); + + printf("\n\t=================================================================\n"); + printf("\t%s\n", object->title); + printf("\t=================================================================\n"); + if(object->numTests == 0) { + printf("\tNO TEST DEFINED\n"); + } else { + for(i = 0; i < object->numTests; i++) { + test_print(&(object->tests[i])); + } + } + printf("\t=================================================================\n"); + if(total > 0) { + printf("\tTotal Tests: %d\n", total); + printf("\tPassed Tests: %d ( %2.2f %% )\n", passed, ((float)passed / (float)total) * 100.0); + printf("\tFailed Tests: %d ( %2.2f %%)\n", failed, ((float)failed / (float)total) * 100.0); + //printf("\tNot Implemented: %d ( %2.2f %%)\n", not_implemented, ((float)not_implemented/(float)total)*100.0); + printf("\t=================================================================\n"); + } +} + +// Export a test section +void testSection_export(tTestSection* object, FILE* fout) { + int i; + int total, passed, failed, not_implemented; + + assert(object != NULL); + assert(fout != NULL); + + testSection_getStats(object, &total, &passed, &failed, ¬_implemented); + + fprintf(fout, "{ \"code\": \"%s\", \"title\": \"%s\", \"total\": %d, \"passed\": %d, \"failed\": %d, \"not_implemented\": %d, \"tests\": [", object->code, object->title, total, passed, failed, not_implemented); + + for(i = 0; i < object->numTests; i++) { + if(i > 0) { + fprintf(fout, ", "); + } + test_export(&(object->tests[i]), fout); + } + + fprintf(fout, "]}"); +} + + +// Initialize a test +void test_init(tTest* object, const char* code, const char* description, tTestResult result) { + assert(object != NULL); + object->code = (char*) malloc((strlen(code) + 1) * sizeof(char)); + assert(object->code != NULL); + object->description = (char*) malloc((strlen(description) + 1) * sizeof(char)); + assert(object->description != NULL); + strcpy(object->code, code); + strcpy(object->description, description); + object->result = TEST_RUNNING; +} + +// Remove a test +void test_free(tTest* object) { + assert(object != NULL); + assert(object->code != NULL); + assert(object->description != NULL); + free(object->code); + free(object->description); +} + +// Update a test result +void test_updateTest(tTest* object, tTestResult result) { + assert(object != NULL); + object->result = result; +} + +// Print test +void test_print(tTest* object) { + assert(object != NULL); + printf("\t\t"); + if(object->result == TEST_RUNNING) { + printf("[%s]", "RUNNING"); + } else + if(object->result == TEST_NOT_IMPLEMENTED) { + printf("[%s]", "NOT IMPLEMENTED"); + } else + if(object->result == TEST_PASSED) { + printf("[%s]", "OK"); + } else + if(object->result == TEST_FAILED) { + printf("[%s]", "FAIL"); + } + printf(":\t [%s] %s\n", object->code, object->description); +} + +// Export a test +void test_export(tTest* object, FILE* fout) { + assert(object != NULL); + assert(fout != NULL); + + fprintf(fout, "{ \"code\": \"%s\", \"description\": \"%s\", \"result\": ", object->code, object->description); + if(object->result == TEST_RUNNING) { + fprintf(fout, "\"%s\"}", "RUNNING"); + } else + if(object->result == TEST_NOT_IMPLEMENTED) { + fprintf(fout, "\"%s\"}", "NOT IMPLEMENTED"); + } else + if(object->result == TEST_PASSED) { + fprintf(fout, "\"%s\"}", "OK"); + } else + if(object->result == TEST_FAILED) { + fprintf(fout, "\"%s\"}", "FAIL"); + } +} + + +// Start a test +void start_test(tTestSection* section, const char* code, const char* description) { + tTest* test = NULL; + + assert(section != NULL); + assert(code != NULL); + assert(description != NULL); +#ifdef PRINT_TEST_PROGRESS + printf("\n[START] ==> Running test [%s] - %s\n", code, description); +#endif // PRINT_TEST_PROGRESS + testSection_addTest(section, code, description, TEST_RUNNING); + + test = testSection_getTest(section, code); + assert(test != NULL); +} + +// Finish a test +void end_test(tTestSection* section, const char* code, bool passed) { + tTest* test = NULL; + assert(section != NULL); + assert(code != NULL); + + test = testSection_getTest(section, code); + assert(test != NULL); + + if(passed) { +#ifdef PRINT_TEST_PROGRESS + printf("\n[OK] ==> Finished test [%s] - %s\n", test->code, test->description); +#endif // PRINT_TEST_PROGRESS + test_updateTest(test, TEST_PASSED); + } else { +#ifdef PRINT_TEST_PROGRESS + printf("\n[FAIL] ==> Finished test [%s] - %s\n", test->code, test->description); +#endif // PRINT_TEST_PROGRESS + test_updateTest(test, TEST_FAILED); + } +}