From 5dea15d7a23e239c130faf78611a2478daf9c268 Mon Sep 17 00:00:00 2001 From: Guillem Date: Fri, 1 Apr 2022 15:58:20 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 19 + UOC20212/README.txt | 4 + UOC20212/UOC20212.project | 132 +++++ UOC20212/UOC20212.workspace | 17 + UOC20212/UOCVaccine/UOCVaccine.project | 116 +++++ UOC20212/UOCVaccine/include/api.h | 63 +++ UOC20212/UOCVaccine/include/csv.h | 78 +++ UOC20212/UOCVaccine/include/date.h | 31 ++ UOC20212/UOCVaccine/include/error.h | 23 + UOC20212/UOCVaccine/include/person.h | 54 ++ UOC20212/UOCVaccine/include/vaccine.h | 106 ++++ UOC20212/UOCVaccine/src/api.c | 185 +++++++ UOC20212/UOCVaccine/src/csv.c | 265 ++++++++++ UOC20212/UOCVaccine/src/date.c | 71 +++ UOC20212/UOCVaccine/src/person.c | 272 ++++++++++ UOC20212/UOCVaccine/src/vaccine.c | 346 +++++++++++++ UOC20212/src/main.c | 48 ++ UOC20212/test/include/test.h | 14 + UOC20212/test/include/test_data.h | 14 + UOC20212/test/include/test_pr1.h | 20 + UOC20212/test/include/test_suite.h | 183 +++++++ UOC20212/test/src/test.c | 45 ++ UOC20212/test/src/test_pr1.c | 380 ++++++++++++++ UOC20212/test/src/test_suite.c | 674 +++++++++++++++++++++++++ 24 files changed, 3160 insertions(+) create mode 100644 .gitignore create mode 100644 UOC20212/README.txt create mode 100644 UOC20212/UOC20212.project create mode 100644 UOC20212/UOC20212.workspace create mode 100644 UOC20212/UOCVaccine/UOCVaccine.project create mode 100644 UOC20212/UOCVaccine/include/api.h create mode 100644 UOC20212/UOCVaccine/include/csv.h create mode 100644 UOC20212/UOCVaccine/include/date.h create mode 100644 UOC20212/UOCVaccine/include/error.h create mode 100644 UOC20212/UOCVaccine/include/person.h create mode 100644 UOC20212/UOCVaccine/include/vaccine.h create mode 100644 UOC20212/UOCVaccine/src/api.c create mode 100644 UOC20212/UOCVaccine/src/csv.c create mode 100644 UOC20212/UOCVaccine/src/date.c create mode 100644 UOC20212/UOCVaccine/src/person.c create mode 100644 UOC20212/UOCVaccine/src/vaccine.c create mode 100644 UOC20212/src/main.c create mode 100644 UOC20212/test/include/test.h create mode 100644 UOC20212/test/include/test_data.h create mode 100644 UOC20212/test/include/test_pr1.h create mode 100644 UOC20212/test/include/test_suite.h create mode 100644 UOC20212/test/src/test.c create mode 100644 UOC20212/test/src/test_pr1.c create mode 100644 UOC20212/test/src/test_suite.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f4ef0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# CodeLite IDE +*.mk +*.txt +#*.project +#*.workspace +compile_commands.json +Makefile +.codelite/ +*.session +*.tags +tags + +# Builds +Debug/ +Release/ +.build-debug/ +.build-release/ +bin/ +lib/ diff --git a/UOC20212/README.txt b/UOC20212/README.txt new file mode 100644 index 0000000..c93092b --- /dev/null +++ b/UOC20212/README.txt @@ -0,0 +1,4 @@ +gsboeck@uoc.edu +SolĂ  i Boeck , Guillem +Fedora release 35 (Thirty Five) x86_64 +gcc version 11.2.1 20220127 (Red Hat 11.2.1-9) (GCC) diff --git a/UOC20212/UOC20212.project b/UOC20212/UOC20212.project new file mode 100644 index 0000000..dc844cf --- /dev/null +++ b/UOC20212/UOC20212.project @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + diff --git a/UOC20212/UOC20212.workspace b/UOC20212/UOC20212.workspace new file mode 100644 index 0000000..3cb0630 --- /dev/null +++ b/UOC20212/UOC20212.workspace @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/UOC20212/UOCVaccine/UOCVaccine.project b/UOC20212/UOCVaccine/UOCVaccine.project new file mode 100644 index 0000000..3417a88 --- /dev/null +++ b/UOC20212/UOCVaccine/UOCVaccine.project @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UOC20212/UOCVaccine/include/api.h b/UOC20212/UOCVaccine/include/api.h new file mode 100644 index 0000000..8953225 --- /dev/null +++ b/UOC20212/UOCVaccine/include/api.h @@ -0,0 +1,63 @@ +#ifndef __UOCVACCINE_API__H +#define __UOCVACCINE_API__H +#include +#include "error.h" +#include "csv.h" + +#include "person.h" +#include "vaccine.h" + + +// Type that stores all the application data +typedef struct _ApiData { + //////////////////////////////// + // PR1 EX2a + //////////////////////////////// + + tPopulation population; + tVaccineList vaccines; + tVaccineLotData vaccineLots; + + +} 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); + +// Add a new vaccines lot +tApiError api_addVaccineLot(tApiData* data, tCSVEntry entry); + +// Get the number of persons registered on the application +int api_populationCount(tApiData data); + +// Get the number of vaccines registered on the application +int api_vaccineCount(tApiData data); + +// Get the number of vaccine lots registered on the application +int api_vaccineLotsCount(tApiData data); + +// Get vaccine data +tApiError api_getVaccine(tApiData data, const char *name, tCSVEntry *entry); + +// Get vaccine lot data +tApiError api_getVaccineLot(tApiData data, const char* cp, const char* vaccine, tDateTime timestamp, tCSVEntry *entry); + +// Get registered vaccines +tApiError api_getVaccines(tApiData data, tCSVData *vaccines); + +// Get vaccine lots +tApiError api_getVaccineLots(tApiData data, tCSVData *lots); + +#endif // __UOCVACCINE_API__H diff --git a/UOC20212/UOCVaccine/include/csv.h b/UOC20212/UOCVaccine/include/csv.h new file mode 100644 index 0000000..dec4efd --- /dev/null +++ b/UOC20212/UOCVaccine/include/csv.h @@ -0,0 +1,78 @@ +#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); + +// Compare if two entries are the same +bool csv_equalsEntry(tCSVEntry entry1, tCSVEntry entry2); + +// Compare if two data objects are the same +bool csv_equals(tCSVData data1, tCSVData data2); + +#endif diff --git a/UOC20212/UOCVaccine/include/date.h b/UOC20212/UOCVaccine/include/date.h new file mode 100644 index 0000000..c7b9aa2 --- /dev/null +++ b/UOC20212/UOCVaccine/include/date.h @@ -0,0 +1,31 @@ +#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 -1 if dateTime1dateTime2. +int dateTime_cmp(tDateTime dateTime1, tDateTime dateTime2); + +// Compare two tDateTime structures and return true if they contain the same value or false otherwise. +bool dateTime_equals(tDateTime dateTime1, tDateTime dateTime2); + +#endif // __DATE_H__ \ No newline at end of file diff --git a/UOC20212/UOCVaccine/include/error.h b/UOC20212/UOCVaccine/include/error.h new file mode 100644 index 0000000..6f42226 --- /dev/null +++ b/UOC20212/UOCVaccine/include/error.h @@ -0,0 +1,23 @@ +#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_PERSON = -6, // Duplicated person + E_MEMORY_ERROR = -7, // Memory error + E_VACCINE_NOT_FOUND = -8, // Vaccine not found + E_HEALTH_CENTER_NOT_FOUND = -9, // Health Center not found + E_LOT_NOT_FOUND = -10, // Vaccine lot not found +}; + +// Define an error type +typedef enum _tApiError tApiError; + +#endif // __UOCCONTACTS_ERRORS__H \ No newline at end of file diff --git a/UOC20212/UOCVaccine/include/person.h b/UOC20212/UOCVaccine/include/person.h new file mode 100644 index 0000000..f05f6f9 --- /dev/null +++ b/UOC20212/UOCVaccine/include/person.h @@ -0,0 +1,54 @@ +#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); + +// Initialize a person structure +void person_init(tPerson* 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); + +// Return population lenght +int population_len(tPopulation data); + +#endif diff --git a/UOC20212/UOCVaccine/include/vaccine.h b/UOC20212/UOCVaccine/include/vaccine.h new file mode 100644 index 0000000..33835aa --- /dev/null +++ b/UOC20212/UOCVaccine/include/vaccine.h @@ -0,0 +1,106 @@ +#ifndef __VACCINE__H +#define __VACCINE__H + +#include "csv.h" +#include "date.h" + +// Vaccine data +typedef struct _tVaccine { + char *name; + int required; + int days; +} tVaccine; + +// Node of a list of vaccines +typedef struct _tVaccineNode { + tVaccine vaccine; + struct _tVaccineNode *next; +} tVaccineNode; + +// List of vaccines +typedef struct _tVaccineList { + tVaccineNode* first; + int count; +} tVaccineList; + +// Vaccine lot data +typedef struct _tVaccineLot { + tVaccine* vaccine; + tDateTime timestamp; + char *cp; + int doses; +} tVaccineLot; + +// Table of lots +typedef struct _tVaccineLotData { + tVaccineLot* elems; + int count; +} tVaccineLotData; + + + +// Initialize vaccine structure +void vaccine_init(tVaccine* vaccine, const char* name, int required, int days); + +// Release vaccine data +void vaccine_free(tVaccine* vaccine); + +// Copy the data of a vaccine from the source to destination +void vaccine_cpy(tVaccine* destination, tVaccine source); + + + +// Release vaccine lot data +void vaccineLot_init(tVaccineLot* lot, tVaccine* vaccine, const char* cp, tDateTime timestamp, int doses); + +// Release vaccine lot data +void vaccineLot_free(tVaccineLot* lot); + +// Copy the data of a vaccine lot from the source to destination +void vaccineLot_cpy(tVaccineLot* destination, tVaccineLot source); + +// Parse input from CSVEntry +void vaccineLot_parse(tVaccine* vaccine, tVaccineLot* lot, tCSVEntry entry); + + + +// Initialize the vaccine's list +void vaccineList_init(tVaccineList* list); + +// Remove all elements +void vaccineList_free(tVaccineList* list); + +// Get the number of vaccines +int vaccineList_len(tVaccineList list); + +// Find a vaccine in the list of vaccines +tVaccine* vaccineList_find(tVaccineList list, const char* name); + +// Add a new vaccine +void vaccineList_insert(tVaccineList* list, tVaccine vaccine); + +// Remove a vaccine +void vaccineList_del(tVaccineList* list, const char* vaccine); + + + +// Initialize the vaccine lots data +void vaccineLotData_init(tVaccineLotData* data); + +// Remove all elements +void vaccineLotData_free(tVaccineLotData* data); + +// Get the number of lots +int vaccineLotData_len(tVaccineLotData data); + +// Add a new vaccine lot +void vaccineLotData_add(tVaccineLotData* data, tVaccineLot lot); + +// Remove vaccines from a lot +void vaccineLotData_del(tVaccineLotData* data, const char* cp, const char* vaccine, tDateTime timestamp, int doses); + +// Return the position of a vaccine lot entry with provided information. -1 if it does not exist +int vaccineLotData_find(tVaccineLotData data, const char* cp, const char* vaccine, tDateTime timestamp); + + +#endif // __VACCINE__H diff --git a/UOC20212/UOCVaccine/src/api.c b/UOC20212/UOCVaccine/src/api.c new file mode 100644 index 0000000..8dd9d94 --- /dev/null +++ b/UOC20212/UOCVaccine/src/api.c @@ -0,0 +1,185 @@ +#include +#include +#include "csv.h" +#include "api.h" + +#include +#include "person.h" +#include "vaccine.h" + + +#define FILE_READ_BUFFER_SIZE 2048 +#define VACCINE_LOT_TYPE_NAME "VACCINE_LOT" +#define VACCINE_LOT_NUM_FIELDS 7 + +// Get the API version information +const char* api_version() { + return "UOC PP 20212"; +} + +// 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; + } + csv_freeEntry(&entry); + } + + fclose(fin); + + return E_SUCCESS; +} + +// Initialize the data structure +tApiError api_initData(tApiData* data) { + ////////////////////////////////// + // Ex PR1 2b + ///////////////////////////////// + + population_init(&(data->population)); + vaccineList_init(&(data->vaccines)); + vaccineLotData_init(&(data->vaccineLots)); + + return E_SUCCESS; +} + +// Add a new vaccines lot +tApiError api_addVaccineLot(tApiData* data, tCSVEntry entry) { + ////////////////////////////////// + // Ex PR1 2c + ///////////////////////////////// + + tApiError ret = E_NOT_IMPLEMENTED; + + if (csv_numFields(entry) == VACCINE_LOT_NUM_FIELDS) { + if (csv_getType(&entry) == VACCINE_LOT_TYPE_NAME) { + // OK + + + + ret = E_SUCCESS; + + } else { + // numFields other than valid one + ret = E_INVALID_ENTRY_TYPE; + } + } else { + // csv record type other than valid one + ret = E_INVALID_ENTRY_FORMAT; + } + + return ret; +} + +// Get the number of persons registered on the application +int api_populationCount(tApiData data) { + ////////////////////////////////// + // Ex PR1 2d + ///////////////////////////////// + return -1; +} + +// Get the number of vaccines registered on the application +int api_vaccineCount(tApiData data) { + ////////////////////////////////// + // Ex PR1 2d + ///////////////////////////////// + return -1; +} + +// Get the number of vaccine lots registered on the application +int api_vaccineLotsCount(tApiData data) { + ////////////////////////////////// + // Ex PR1 2d + ///////////////////////////////// + return -1; +} + + +// Free all used memory +tApiError api_freeData(tApiData* data) { + ////////////////////////////////// + // Ex PR1 2e + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + + +// Add a new entry +tApiError api_addDataEntry(tApiData* data, tCSVEntry entry) { + ////////////////////////////////// + // Ex PR1 2f + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get vaccine data +tApiError api_getVaccine(tApiData data, const char *name, tCSVEntry *entry) { + ////////////////////////////////// + // Ex PR1 3a + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get vaccine lot data +tApiError api_getVaccineLot(tApiData data, const char* cp, const char* vaccine, tDateTime timestamp, tCSVEntry *entry) { + ////////////////////////////////// + // Ex PR1 3b + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get registered vaccines +tApiError api_getVaccines(tApiData data, tCSVData *vaccines) { + ////////////////////////////////// + // Ex PR1 3c + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} + +// Get vaccine lots +tApiError api_getVaccineLots(tApiData data, tCSVData *lots) { + ////////////////////////////////// + // Ex PR1 3d + ///////////////////////////////// + return E_NOT_IMPLEMENTED; +} diff --git a/UOC20212/UOCVaccine/src/csv.c b/UOC20212/UOCVaccine/src/csv.c new file mode 100644 index 0000000..b51d055 --- /dev/null +++ b/UOC20212/UOCVaccine/src/csv.c @@ -0,0 +1,265 @@ +#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]); +} + +// Compare if two entries are the same +bool csv_equalsEntry(tCSVEntry entry1, tCSVEntry entry2) { + int i; + if (entry1.numFields != entry2.numFields) { + return false; + } + if (strcmp(entry1.type, entry2.type) != 0) { + return false; + } + for (i = 0; i < entry1.numFields ; i++) { + if (strcmp(entry1.fields[i], entry2.fields[i]) != 0) { + return false; + } + } + + return true; +} + +// Compare if two data objects are the same +bool csv_equals(tCSVData data1, tCSVData data2) { + int i; + if (data1.count != data2.count) { + return false; + } + for (i = 0; i < data1.count ; i++) { + if (!csv_equalsEntry(data1.entries[i], data2.entries[i])) { + return false; + } + } + + return true; +} diff --git a/UOC20212/UOCVaccine/src/date.c b/UOC20212/UOCVaccine/src/date.c new file mode 100644 index 0000000..45abc34 --- /dev/null +++ b/UOC20212/UOCVaccine/src/date.c @@ -0,0 +1,71 @@ +#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 -1 if dateTime1dateTime2. +int dateTime_cmp(tDateTime dateTime1, tDateTime dateTime2) { + // Checkl year + if (dateTime1.date.year < dateTime2.date.year) { + return -1; + } + if (dateTime1.date.year > dateTime2.date.year) { + return 1; + } + // Check month + if (dateTime1.date.month < dateTime2.date.month) { + return -1; + } + if (dateTime1.date.month > dateTime2.date.month) { + return 1; + } + // Check day + if (dateTime1.date.day < dateTime2.date.day) { + return -1; + } + if (dateTime1.date.day > dateTime2.date.day) { + return 1; + } + // Check hour + if (dateTime1.time.hour < dateTime2.time.hour) { + return -1; + } + if (dateTime1.time.hour > dateTime2.time.hour) { + return 1; + } + // Check minutes + if (dateTime1.time.minutes < dateTime2.time.minutes) { + return -1; + } + if (dateTime1.time.minutes > dateTime2.time.minutes) { + return 1; + } + + return 0; +} + +// Compare two tDateTime structures and return true if they contain the same value or false otherwise. +bool dateTime_equals(tDateTime dateTime1, tDateTime dateTime2) { + return dateTime_cmp(dateTime1, dateTime2) == 0; +} diff --git a/UOC20212/UOCVaccine/src/person.c b/UOC20212/UOCVaccine/src/person.c new file mode 100644 index 0000000..9b5998d --- /dev/null +++ b/UOC20212/UOCVaccine/src/person.c @@ -0,0 +1,272 @@ +#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; +} + +// Initialize a person structure +void person_init(tPerson* data) { + // Check input data + assert(data != NULL); + + data->document = NULL; + data->name = NULL; + data->surname = NULL; + data->email = NULL; + data->address = NULL; + data->cp = NULL; + data->birthday.day=-1; + data->birthday.month=-1; + data->birthday.year=-1; +} + +// 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); + + // Remove old data + person_free(data); + + // 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); + + // Initialize the new element + person_init(&(data->elems[data->count])); + + // 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) { + + // Remove old data + person_free(destination); + + // 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; +} + +// Return population lenght +int population_len(tPopulation data) { + return data.count; +} \ No newline at end of file diff --git a/UOC20212/UOCVaccine/src/vaccine.c b/UOC20212/UOCVaccine/src/vaccine.c new file mode 100644 index 0000000..4ff2a94 --- /dev/null +++ b/UOC20212/UOCVaccine/src/vaccine.c @@ -0,0 +1,346 @@ +#include +#include +#include +#include +#include "vaccine.h" + +// Initialize vaccine structure +void vaccine_init(tVaccine* vaccine, const char* name, int required, int days) { + assert(vaccine != NULL); + assert(name != NULL); + + // Allocate memory for the name + vaccine->name = (char*) malloc(strlen(name) + 1); + assert(vaccine->name != NULL); + + // Set the data + strcpy(vaccine->name, name); + vaccine->required = required; + vaccine->days = days; +} + +// Release vaccine data +void vaccine_free(tVaccine* vaccine) { + assert(vaccine != NULL); + + // Release used memory + if (vaccine->name != NULL) { + free(vaccine->name); + vaccine->name = NULL; + } +} + +// Copy the data of a vaccine from the source to destination +void vaccine_cpy(tVaccine* destination, tVaccine source) { + assert(destination != NULL); + + // Set the data + vaccine_init(destination, source.name, source.required, source.days); +} + + +// Release vaccine lot data +void vaccineLot_init(tVaccineLot* lot, tVaccine* vaccine, const char* cp, tDateTime timestamp, int doses) { + assert(lot != NULL); + assert(cp != NULL); + + // Allocate memory for the cp + lot->cp = (char*) malloc(strlen(cp) + 1); + assert(lot->cp != NULL); + + // Set the data + strcpy(lot->cp, cp); + lot->vaccine = vaccine; + lot->timestamp = timestamp; + lot->doses = doses; +} + +// Release vaccine lot data +void vaccineLot_free(tVaccineLot* lot) { + assert(lot != NULL); + + // Release used memory + if (lot->cp != NULL) { + free(lot->cp); + lot->cp = NULL; + } +} + +// Copy the data of a vaccine lot from the source to destination +void vaccineLot_cpy(tVaccineLot* destination, tVaccineLot source) { + assert(destination != NULL); + + // Set the data + vaccineLot_init(destination, source.vaccine, source.cp, source.timestamp, source.doses); +} + +// Parse input from CSVEntry +void vaccineLot_parse(tVaccine* vaccine, tVaccineLot* lot, tCSVEntry entry) { + char date[11]; + char time[6]; + char buffer[512]; + tDateTime timestamp; + int doses; + int required; + int days; + + // Check input data + assert(vaccine != NULL); + assert(lot != NULL); + assert(csv_numFields(entry) == 7); + + // Get Lot data + csv_getAsString(entry, 0, date, 11); + csv_getAsString(entry, 1, time, 6); + dateTime_parse(×tamp, date, time); + csv_getAsString(entry, 2, buffer, 511); + doses = csv_getAsInteger(entry, 6); + + // Initialize the lot structure + vaccineLot_init(lot, NULL, buffer, timestamp, doses); + + // Get vaccine data + csv_getAsString(entry, 3, buffer, 511); + required = csv_getAsInteger(entry, 4); + days = csv_getAsInteger(entry, 5); + + // Initialize the vaccine data + vaccine_init(vaccine, buffer, required, days); +} + + +// Initialize the vaccine's list +void vaccineList_init(tVaccineList* list) { + assert(list != NULL); + + list->first = NULL; + list->count = 0; +} + +// Remove all elements +void vaccineList_free(tVaccineList* list) { + tVaccineNode *pNode = NULL; + tVaccineNode *pAux = NULL; + + assert(list != NULL); + + pNode = list->first; + while(pNode != NULL) { + // Store the position of the current node + pAux = pNode; + + // Move to the next node in the list + pNode = pNode->next; + + // Remove previous node + vaccine_free(&(pAux->vaccine)); + free(pAux); + } + + // Initialize to an empty list + vaccineList_init(list); +} + +// Get the number of vaccines +int vaccineList_len(tVaccineList list) { + return list.count; +} + +// Find a vaccine in the list of vaccines +tVaccine* vaccineList_find(tVaccineList list, const char* name) { + tVaccineNode *pNode = NULL; + tVaccineNode *pVaccine = NULL; + + // Point the first element + pNode = list.first; + + while(pNode != NULL && pVaccine == NULL) { + // Compare current node with given name + if (strcmp(pNode->vaccine.name, name) == 0) { + pVaccine = pNode; + } + pNode = pNode->next; + } + + return &(pVaccine->vaccine); +} + +// Add a new vaccine +void vaccineList_insert(tVaccineList* list, tVaccine vaccine) { + tVaccineNode *pNode = NULL; + tVaccineNode *pPrev = NULL; + + assert(list != NULL); + + // If the list is empty add the node as first position + if (list->count == 0) { + list->first = (tVaccineNode*) malloc(sizeof(tVaccineNode)); + list->first->next = NULL; + vaccine_cpy(&(list->first->vaccine), vaccine); + } else { + // Point the first element + pNode = list->first; + pPrev = pNode; + + // Advance in the list up to the insertion point or the end of the list + while(pNode != NULL && strcmp(pNode->vaccine.name, vaccine.name) < 0) { + pPrev = pNode; + pNode = pNode->next; + } + + if (pNode == pPrev) { + // Insert as first element + list->first = (tVaccineNode*) malloc(sizeof(tVaccineNode)); + list->first->next = pNode; + vaccine_cpy(&(list->first->vaccine), vaccine); + } else { + // Insert after pPrev + pPrev->next = (tVaccineNode*) malloc(sizeof(tVaccineNode)); + vaccine_cpy(&(pPrev->next->vaccine), vaccine); + pPrev->next->next = pNode; + } + } + list->count ++; +} + +// Remove a vaccine +void vaccineList_del(tVaccineList* list, const char* vaccine) { + tVaccineNode *pNode = NULL; + tVaccineNode *pPrev = NULL; + + assert(list != NULL); + + // Check if the list has elements + if (list->count > 0) { + pNode = list->first; + + // Check if we are removing the first position + if (strcmp(pNode->vaccine.name, vaccine) == 0) { + list->first = pNode->next; + } else { + // Search in the list + pPrev = pNode; + while (pNode != NULL) { + if (strcmp(pNode->vaccine.name, vaccine) == 0) { + // Link previous and next nodes + pPrev->next = pNode->next; + // Remove node + vaccine_free(&(pNode->vaccine)); + free(pNode); + list->count ++; + pNode = NULL; + } else { + pPrev = pNode; + pNode = pNode->next; + } + } + } + } +} + + +// Initialize the vaccine lots data +void vaccineLotData_init(tVaccineLotData* data) { + assert(data != NULL); + + // Set the initial number of elements to zero. + data->count = 0; + data->elems = NULL; +} + +// Remove all elements +void vaccineLotData_free(tVaccineLotData* data) { + int i; + if (data->elems != NULL) { + for(i=0; i < data->count; i++) { + vaccineLot_free(&(data->elems[i])); + } + free(data->elems); + } + vaccineLotData_init(data); +} + +// Get the number of lots +int vaccineLotData_len(tVaccineLotData data) { + // Return the number of lots + return data.count; +} + +// Add a new vaccine lot +void vaccineLotData_add(tVaccineLotData* data, tVaccineLot lot) { + int idx = -1; + + // Check input data (Pre-conditions) + assert(data != NULL); + + // Check if an entry with this data already exists + idx = vaccineLotData_find(*data, lot.cp, lot.vaccine->name, lot.timestamp); + + // If it does not exist, create a new entry, otherwise add the number of doses + if (idx < 0) { + if (data->elems == NULL) { + data->elems = (tVaccineLot*) malloc(sizeof(tVaccineLot)); + } else { + data->elems = (tVaccineLot*) realloc(data->elems, (data->count + 1) * sizeof(tVaccineLot)); + } + assert(data->elems != NULL); + vaccineLot_cpy(&(data->elems[data->count]), lot); + data->count ++; + } else { + data->elems[idx].doses += lot.doses; + } +} + +// Remove vaccines from a lot +void vaccineLotData_del(tVaccineLotData* data, const char* cp, const char* vaccine, tDateTime timestamp, int doses) { + int idx; + int i; + + assert(cp != NULL); + assert(vaccine != NULL); + + // Check if an entry with this data already exists + idx = vaccineLotData_find(*data, cp, vaccine, timestamp); + + if (idx >= 0) { + // Reduce the number of doses + data->elems[idx].doses -= doses; + // Shift elements to remove selected + if (data->elems[idx].doses <= 0) { + for(i = idx; i < data->count-1; i++) { + // Remove the data from this position + vaccineLot_free(&(data->elems[i])); + // Copy element on position i+1 to position i + vaccineLot_cpy(&(data->elems[i]), data->elems[i+1]); + } + // Update the number of elements + data->count--; + // Free last position + vaccineLot_free(&(data->elems[data->count])); + } + if (data->count > 0) { + data->elems = (tVaccineLot*) realloc(data->elems, data->count * sizeof(tVaccineLot)); + assert(data->elems != NULL); + } else { + free(data->elems); + data->elems = NULL; + } + } +} + +// Return the position of a vaccine lot entry with provided information. -1 if it does not exist +int vaccineLotData_find(tVaccineLotData data, const char* cp, const char* vaccine, tDateTime timestamp) { + int i; + + assert(cp != NULL); + assert(vaccine != NULL); + + for(i = 0; i < data.count; i++) { + if(strcmp(data.elems[i].cp, cp) == 0 && strcmp(data.elems[i].vaccine->name, vaccine) == 0 && dateTime_equals(data.elems[i].timestamp, timestamp)) { + return i; + } + } + + return -1; +} + diff --git a/UOC20212/src/main.c b/UOC20212/src/main.c new file mode 100644 index 0000000..9c9d6c1 --- /dev/null +++ b/UOC20212/src/main.c @@ -0,0 +1,48 @@ +#include +#include +#include "test_suite.h" +#include "test.h" + +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); + + // Set the progress file + testSuite_set_progress_file(&testSuite, parameters.progress_file); + + // Run all tests + testSuite_run(&testSuite, parameters.in_file, parameters.readme_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); + } + if (parameters.progress_file != NULL) { + printf("Progress stored in %s\n", parameters.progress_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/UOC20212/test/include/test.h b/UOC20212/test/include/test.h new file mode 100644 index 0000000..13e2c52 --- /dev/null +++ b/UOC20212/test/include/test.h @@ -0,0 +1,14 @@ +#ifndef __TEST__H +#define __TEST__H +#include "test_suite.h" + +// Write data to file +void save_data(const char* filename, const char* data); + +// Run all available tests +void testSuite_run(tTestSuite* test_suite, const char* input, const char* readme); + +// 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/UOC20212/test/include/test_data.h b/UOC20212/test/include/test_data.h new file mode 100644 index 0000000..3f969d8 --- /dev/null +++ b/UOC20212/test/include/test_data.h @@ -0,0 +1,14 @@ +#ifndef __TEST_DATA__H +#define __TEST_DATA__H + +// Define test data for PR1 +const char* test_data_pr1_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" \ + "VACCINE_LOT;01/01/2022;13:45;08001;PFIZER;2;21;300\n" \ + "VACCINE_LOT;01/01/2022;13:45;08500;PFIZER;2;21;300\n" \ + "VACCINE_LOT;01/01/2022;13:45;08001;PFIZER;2;21;50\n" \ + "VACCINE_LOT;02/01/2022;18:00;08500;MODERNA;1;0;100\n" \ + "VACCINE_LOT;02/01/2022;15:45;08001;MODERNA;1;0;100\n" \ + "VACCINE_LOT;03/01/2022;13:45;08500;PFIZER;2;21;70\n"; + +#endif // TEST_DATA__H \ No newline at end of file diff --git a/UOC20212/test/include/test_pr1.h b/UOC20212/test/include/test_pr1.h new file mode 100644 index 0000000..b87a02e --- /dev/null +++ b/UOC20212/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/UOC20212/test/include/test_suite.h b/UOC20212/test/include/test_suite.h new file mode 100644 index 0000000..3a9e704 --- /dev/null +++ b/UOC20212/test/include/test_suite.h @@ -0,0 +1,183 @@ +#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 + +// Arguments for a test program +typedef struct _AppArguments { + char* app_name; + char* out_file; + char* in_file; + char* readme_file; + char* progress_file; + bool wait_on_exit; +} tAppArguments; + +// 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; + // Progress file + char* progress_file; +} 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; + // Progress file + char* progress_file; +} tTestSuite; + + +// Wait user key +void waitKey(tAppArguments parameters); + +// Display help text +void help(const char* app_name); + +// Parse application arguments +bool parseArguments(tAppArguments* arguments, int argc, char **argv); + + +// Initialize a test Suite +void testSuite_init(tTestSuite* object); + +// Set output progress file +void testSuite_set_progress_file(tTestSuite* object, char* progress_file); + +// 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); + +// Set output progress file +void _save_progress(tTestSection* section, const char* test_code, const char* test_result); + + +#endif // __TEST_SUITE__H \ No newline at end of file diff --git a/UOC20212/test/src/test.c b/UOC20212/test/src/test.c new file mode 100644 index 0000000..5e9c2d4 --- /dev/null +++ b/UOC20212/test/src/test.c @@ -0,0 +1,45 @@ +#include +#include +#include "test_data.h" +#include "test.h" +#include "test_pr1.h" + + +// Write data to file +void save_data(const char* filename, const char* data) { + FILE *fout; + fout = fopen(filename, "w"); + assert(fout != NULL); + fwrite(data, strlen(data), 1, fout); + fclose(fout); +} + + +// Run all available tests +void testSuite_run(tTestSuite* test_suite, const char* input, const char* readme) { + const char* default_readme = "../README.txt"; + const char* filename; + + assert(test_suite != NULL); + + // Load the README.txt file + if (readme == NULL) { + testSuite_load_learner(test_suite, default_readme); + } else { + testSuite_load_learner(test_suite, readme); + } + + ////////////////////// + // Run tests for PR1 + ////////////////////// + + // If no file is provided, use default data for PR1 + if (input == NULL) { + filename = "test_data_pr1.csv"; + save_data(filename, test_data_pr1_str); + } else { + filename = input; + } + // Run tests + run_pr1(test_suite, filename); +} diff --git a/UOC20212/test/src/test_pr1.c b/UOC20212/test/src/test_pr1.c new file mode 100644 index 0000000..cf0f6fb --- /dev/null +++ b/UOC20212/test/src/test_pr1.c @@ -0,0 +1,380 @@ +#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 20212") != 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; + int nLots; + int nVaccines; + int nPeople; + bool passed = true; + bool 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", "Add a valid lot"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + csv_parseEntry(&entry, "01/01/2022;13:45;08001;PFIZER;2;21;300", "VACCINE_LOT"); + error = api_addVaccineLot(&data, entry); + if (error != E_SUCCESS) { + failed = true; + passed = false; + fail_all = true; + } + csv_freeEntry(&entry); + } + end_test(test_section, "PR1_EX2_2", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 3 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_3", "Check the number of vaccines"); + if (fail_all) { + failed = true; + } else { + nVaccines = api_vaccineCount(data); + if (nVaccines != 1) { + failed = true; + passed = false; + fail_all = true; + } + } + end_test(test_section, "PR1_EX2_3", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 4 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_4", "Check the number of vaccine lots"); + if (fail_all) { + failed = true; + } else { + nLots = api_vaccineLotsCount(data); + if (nLots != 1) { + failed = true; + passed = false; + fail_all = true; + } + } + end_test(test_section, "PR1_EX2_4", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 5 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_5", "Add a lot with invalid type"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + csv_parseEntry(&entry, "01/01/2022;13:45;08001;PFIZER;2;21;300", "VACCINE_LOT_DATA"); + error = api_addVaccineLot(&data, entry); + if (error != E_INVALID_ENTRY_TYPE) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + } + end_test(test_section, "PR1_EX2_5", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 6 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_6", "Add a lot with invalid fields"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + csv_parseEntry(&entry, "01/01/2022;13:45;08001;PFIZER;2;21;300;extra_field", "VACCINE_LOT"); + error = api_addVaccineLot(&data, entry); + if (error != E_INVALID_ENTRY_FORMAT) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + } + end_test(test_section, "PR1_EX2_6", !failed); + + ///////////////////////////// + ///// PR1 EX2 TEST 7 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_7", "Free API data"); + if (fail_all) { + failed = true; + } else { + error = api_freeData(&data); + nVaccines = api_vaccineCount(data); + nLots = api_vaccineLotsCount(data); + nPeople = api_populationCount(data); + if (error != E_SUCCESS || nVaccines != 0 || nLots != 0 || nPeople != 0) { + failed = true; + passed = false; + fail_all = true; + } + } + end_test(test_section, "PR1_EX2_7", !failed); + + + ///////////////////////////// + ///// PR1 EX2 TEST 8 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX2_8", "Load data from file"); + // Load basic data to the API + if (fail_all) { + failed = true; + } else { + error = api_loadData(&data, input, true); + nVaccines = api_vaccineCount(data); + nLots = api_vaccineLotsCount(data); + nPeople = api_populationCount(data); + if (error != E_SUCCESS || nVaccines != 2 || nLots != 5 || nPeople != 2) { + failed = true; + passed = false; + } + } + end_test(test_section, "PR1_EX2_8", !failed); + + // Release all data + api_freeData(&data); + + 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 refEntry; + tCSVData report; + tCSVData refReport; + tDateTime timestamp; + int nLots; + int nVaccines; + int nPeople; + bool passed = true; + bool failed = false; + bool fail_all = false; + + + // Initialize the data + error = api_initData(&data); + if (error != E_SUCCESS) { + passed = false; + fail_all = true; + } + + if (!fail_all) { + error = api_loadData(&data, input, true); + nVaccines = api_vaccineCount(data); + nLots = api_vaccineLotsCount(data); + nPeople = api_populationCount(data); + if (error != E_SUCCESS || nVaccines != 2 || nLots != 5 || nPeople != 2) { + passed = false; + fail_all = true; + } + } + + + ///////////////////////////// + ///// PR1 EX3 TEST 1 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_1", "Request a valid vaccine"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + csv_initEntry(&refEntry); + csv_parseEntry(&refEntry, "PFIZER;2;21", "VACCINE"); + error = api_getVaccine(data, "PFIZER", &entry); + if (error != E_SUCCESS || !csv_equalsEntry(entry, refEntry)) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + csv_freeEntry(&refEntry); + } + end_test(test_section, "PR1_EX3_1", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 2 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_2", "Request a missing vaccine"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + error = api_getVaccine(data, "NO_VACC", &entry); + if (error != E_VACCINE_NOT_FOUND) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + } + end_test(test_section, "PR1_EX3_2", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 3 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_3", "Request a valid vaccine lot"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + csv_initEntry(&refEntry); + csv_parseEntry(&refEntry, "01/01/2022;13:45;08001;PFIZER;2;21;350", "VACCINE_LOT"); + dateTime_parse(×tamp, "01/01/2022", "13:45"); + error = api_getVaccineLot(data, "08001", "PFIZER", timestamp, &entry); + if (error != E_SUCCESS || !csv_equalsEntry(entry, refEntry)) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + csv_freeEntry(&refEntry); + } + end_test(test_section, "PR1_EX3_3", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 4 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_4", "Request a missing vaccine lot"); + if (fail_all) { + failed = true; + } else { + csv_initEntry(&entry); + dateTime_parse(×tamp, "01/01/2022", "13:45"); + error = api_getVaccineLot(data, "10001", "NO_VACC", timestamp, &entry); + if (error != E_LOT_NOT_FOUND) { + failed = true; + passed = false; + } + csv_freeEntry(&entry); + } + end_test(test_section, "PR1_EX3_4", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 5 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_5", "Get registered vaccines"); + if (fail_all) { + failed = true; + } else { + csv_init(&report); + csv_init(&refReport); + csv_addStrEntry(&refReport, "MODERNA;1;0", "VACCINE"); + csv_addStrEntry(&refReport, "PFIZER;2;21", "VACCINE"); + error = api_getVaccines(data, &report); + if (error != E_SUCCESS || !csv_equals(report, refReport)) { + failed = true; + passed = false; + } + csv_free(&report); + csv_free(&refReport); + } + end_test(test_section, "PR1_EX3_5", !failed); + + ///////////////////////////// + ///// PR1 EX3 TEST 6 ////// + ///////////////////////////// + failed = false; + start_test(test_section, "PR1_EX3_6", "Get registered vaccine lots"); + if (fail_all) { + failed = true; + } else { + csv_init(&report); + csv_init(&refReport); + csv_addStrEntry(&refReport, "01/01/2022;13:45;08001;PFIZER;2;21;350", "VACCINE_LOT"); + csv_addStrEntry(&refReport, "01/01/2022;13:45;08500;PFIZER;2;21;300", "VACCINE_LOT"); + csv_addStrEntry(&refReport, "02/01/2022;18:00;08500;MODERNA;1;0;100", "VACCINE_LOT"); + csv_addStrEntry(&refReport, "02/01/2022;15:45;08001;MODERNA;1;0;100", "VACCINE_LOT"); + csv_addStrEntry(&refReport, "03/01/2022;13:45;08500;PFIZER;2;21;70", "VACCINE_LOT"); + error = api_getVaccineLots(data, &report); + if (error != E_SUCCESS || !csv_equals(report, refReport)) { + failed = true; + passed = false; + } + csv_free(&report); + csv_free(&refReport); + } + end_test(test_section, "PR1_EX3_6", !failed); + + // Release all data + api_freeData(&data); + + return passed; +} diff --git a/UOC20212/test/src/test_suite.c b/UOC20212/test/src/test_suite.c new file mode 100644 index 0000000..0442cfd --- /dev/null +++ b/UOC20212/test/src/test_suite.c @@ -0,0 +1,674 @@ +#include +#include +#include +#include "test_suite.h" + + +// 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 ] [--progress ] [--readme ]\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."); + printf("\t[%s] %s\n", "--progress", "Write test progress in an output file."); + printf("\t[%s] %s\n", "--readme", "Path to README.txt file."); +} + +// Parse application arguments +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->readme_file = NULL; + arguments->progress_file = NULL; + arguments->wait_on_exit = true; + + // Parse input arguments + arguments->app_name = argv[0]; + for (i=1; i < argc; i ++) { + 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]; + i++; + } + if (strcmp(argv[i], "--out") == 0) { + if (argc < i + 1) { + help(argv[0]); + return false; + } + arguments->out_file = argv[i+1]; + i++; + } + if (strcmp(argv[i], "--progress") == 0) { + if (argc < i + 1) { + help(argv[0]); + return false; + } + arguments->progress_file = argv[i+1]; + i++; + } + if (strcmp(argv[i], "--readme") == 0) { + if (argc < i + 1) { + help(argv[0]); + return false; + } + arguments->readme_file = argv[i+1]; + i++; + } + } + + return true; +} + +// 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; + object->progress_file = NULL; +} + +// Set output progress file +void testSuite_set_progress_file(tTestSuite* object, char* progress_file) { + object->progress_file = progress_file; +} + +// 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); + } + object->progress_file=NULL; +} + +// 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); + if (object->progress_file != NULL) { + object->sections[object->numSections - 1].progress_file = object->progress_file; + } +} + +// 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; + object->progress_file = 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); + + _save_progress(section, code, 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 + _save_progress(section, test->code, "OK"); + 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 + _save_progress(section, test->code, "FAIL"); + test_updateTest(test, TEST_FAILED); + } +} + +// Set output progress file +void _save_progress(tTestSection* section, const char* test_code, const char* test_result) { + FILE *f_progress=NULL; + if (section->progress_file == NULL) { + return; + } + // Open the file + f_progress = fopen(section->progress_file, "a"); + if (f_progress == NULL) { + return; + } + + if (test_result == NULL) { + fprintf(f_progress, "TEST:START:{\"section_code\": \"%s\", \"test_code\": \"%s\"}\n", section->code, test_code); + } else { + fprintf(f_progress, "TEST:END:{\"section_code\": \"%s\", \"test_code\": \"%s\", \"result\": \"%s\"}\n", section->code, test_code, test_result); + } + + // Close the file + fclose(f_progress); +}