lib

morphux C utility library
Log | Files | Refs | Submodules | README | LICENSE | git clone https://git.ne02ptzero.me/git/lib

commit 2d2e1af74c0424cabb0a0e37ddd1e397eb50d24a
parent 4c01a1326aa542405919603c8034e77f5f42cafc
Author: Ne02ptzero <louis@ne02ptzero.me>
Date:   Mon, 24 Apr 2017 18:33:05 +0200

qMerge branch 'enerdhil-flush_on_m_info_and_typos' into unstable

Diffstat:
A.gitmodules | 3+++
ACONTRIBUTING.md | 1+
MMakefile | 12+++++++-----
Mdocs/doxyfile | 9+++++----
Adocs/doxygen-theme | 1+
Minc/fail_test.h | 6++++++
Minc/m_args.h | 78++++++++++++++++++++++++++++++++++++++++++------------------------------------
Ainc/m_file.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minc/m_infos.h | 41+++++++++++++++++++++++++++++++++++------
Minc/m_list.h | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Minc/m_print.h | 58++++++++++++++++++++++++++++++++++++++++++++++++++--------
Minc/m_test.h | 134++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Minc/m_types.h | 16++++++++--------
Minc/morphux.h | 3++-
Msrc/m_args.c | 400++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Asrc/m_file.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/m_infos.c | 114+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/m_list.c | 506+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/m_print.c | 101++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/m_test.c | 368++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/test.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtests/Makefile | 3+++
Mtests/main.c | 25+++++++++++++------------
Mtests/test.h | 25+++++++++++++------------
Mtests/test_args.c | 801++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Atests/test_files.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/test_print.c | 6+++++-
Mtests/test_tests.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28 files changed, 2161 insertions(+), 1132 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docs/doxygen-theme"] + path = docs/doxygen-theme + url = https://github.com/Ne02ptzero/doxygen-theme diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md @@ -0,0 +1 @@ +See https://dev-docs.morphux.org/Contributing/ diff --git a/Makefile b/Makefile @@ -17,16 +17,16 @@ NAME = libmorphux.a CC = gcc LIB = ar -CFLAGS = -Wall -Wextra -Werror -Wno-unused-result -I inc/ -std=c99 -g -O3 +CFLAGS = -Wall -Wextra -Werror -Wno-unused-result -I inc/ -std=gnu99 -g -O3 LFLAGS = -cq SRCS = $(wildcard src/*.c) OBJS = $(SRCS:%.c=%.o) OSTYPE = $(shell uname) ifeq ($(OSTYPE), Linux) -COVFLAGS = "-Wall -Wextra -Wno-unused-result -I inc/ -std=c99 -g -O0 -coverage -lgcov -DCOMPILE_WITH_TEST" +COVFLAGS = "-Wall -Wextra -Wno-unused-result -I inc/ -std=gnu99 -g -O0 -coverage -lgcov -DCOMPILE_WITH_TEST" else ifeq ($(OSTYPE), Darwin) -COVFLAGS = "-Wall -Wextra -Wno-unused-result -I inc/ -std=c99 -g -O0 -coverage -DCOMPILE_WITH_TEST" +COVFLAGS = "-Wall -Wextra -Wno-unused-result -I inc/ -std=gnu99 -g -O0 -coverage -DCOMPILE_WITH_TEST" endif all: $(NAME) @@ -35,7 +35,7 @@ $(NAME): $(OBJS) $(LIB) $(LFLAGS) $(NAME) $(OBJS) check: all - $(MAKE) fclean all CFLAGS="$(CFLAGS) -DCOMPILE_WITH_TEST" + $(MAKE) fclean all CFLAGS="$(CFLAGS) -Wno-error -DCOMPILE_WITH_TEST" make -C tests re check doc: @@ -44,7 +44,6 @@ doc: coverage: $(MAKE) fclean all CFLAGS=$(COVFLAGS) make -C tests coverage check - rm src/test.gc* gcov -o src/ $(SRCS) clean: @@ -56,6 +55,9 @@ clean: fclean: clean rm -f $(NAME) +test: + $(MAKE) fclean all CFLAGS="$(CFLAGS) -DCOMPILE_WITH_TEST -Wno-error" + re: fclean all .PHONY: fclean clean all diff --git a/docs/doxyfile b/docs/doxyfile @@ -7,7 +7,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Morphux/lib PROJECT_NUMBER = PROJECT_BRIEF = -PROJECT_LOGO = +PROJECT_LOGO = /morphux/graphic/logo/single_penguin.png OUTPUT_DIRECTORY = docs CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO @@ -93,7 +93,7 @@ SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = -LAYOUT_FILE = +LAYOUT_FILE = ./docs/doxygen-theme/DoxygenLayout.xml CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -108,9 +108,10 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = src +INPUT = inc INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ + *.h \ *.cc \ *.cxx \ *.cpp \ @@ -195,7 +196,7 @@ HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = ./docs/doxygen-theme/customdoxygen.css HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 diff --git a/docs/doxygen-theme b/docs/doxygen-theme @@ -0,0 +1 @@ +Subproject commit e55ed43dc48f614179d702adcc13347bb3d47490 diff --git a/inc/fail_test.h b/inc/fail_test.h @@ -22,16 +22,22 @@ # define write(fd, ptr, len) fl_write(fd, ptr, len) # define read(fd, ptr, len) fl_read(fd, ptr, len) # define close(fd) fl_close(fd) +# define strdup(str) fl_strdup(str) +# define fstat(fd, buf) fl_fstat(fd, buf) void *fl_malloc(size_t alloc); ssize_t fl_write(int fd, const void *ptr, size_t len); ssize_t fl_read(int fd, void *ptr, size_t len); int fl_close(int fd); +char *fl_strdup(const char *str); +int fl_fstat(int fd, struct stat *buf); void set_malloc_fail(int val); void set_write_fail(int val); void set_read_fail(int val); void set_close_fail(int val); +void set_strdup_fail(int val); +void set_fstat_fail(int val); # endif /* M_FAIL_TEST_H */ diff --git a/inc/m_args.h b/inc/m_args.h @@ -19,48 +19,54 @@ # include <stdbool.h> # include <stdlib.h> -# include <m_types.h> -# include <m_print.h> -# include <m_infos.h> +# include <errno.h> # include <morphux.h> -typedef struct s_args { - /** - * Single letter option - * Example: -f, -s - */ - char opt; +typedef struct opts_s { + char opt; /*!< Single letter option. '\0' for no option */ + char *s_opt; /*!< Word option. NULL for no option */ + char *desc; /*!< Description of the option. Used for the help. */ + char *usage; /*!< Usage example */ + bool take_arg; /*!< Describe if the option must take an argument */ + bool required; /*!< Describe if the option is required or not */ + bool (*callback)(const char *); /*!< Callback function */ +} mopts_t; - /** - * Full string option - * Example: --force, --skip - */ - char *s_opt; - - /** - * Description of the option. - * Used for the help - */ - char *desc; - - /** - * Boolean that describe if the option must take an argument - */ - bool take_arg; +#define ARGS_EOL {.opt = 0, .s_opt = NULL, .desc = NULL, .take_arg = false, .callback = NULL} +#define IS_EOL(lst) (lst.opt == 0 && lst.s_opt == NULL && lst.desc == NULL && \ + lst.take_arg == false && lst.callback == NULL) - /** - * Callback of the option - */ - void (*callback)(const char *); -} margs_t; +/*! + * \brief Read the options given by the program + * \param[in] ac Number of argument in av + * \param[in] av Array of string, containing the arguments + * \param[in] args Array of margs_t, containing the preset options. Must end with + * an empty structure. + * + * The read_opt function reads a given list of arguments, and parse the options + * in it. The options are read from the args array. + * If an option is not known, the function calls the help and quit. + * If the option -h | --help is passed, the function call the help and quit. + * If the option -v | --version is passed, the function call the version and quit. + * + * \note Only the arguments beginning with - are parsed. + * \return Number of options read + */ +u32_t read_opt(const int ac, char **av, const mopts_t *opts, mlist_t **args); -#define ARGS_EOL {0, NULL, NULL, false, NULL} -#define IS_EOL(lst) (lst.opt == 0 && lst.s_opt == NULL && lst.desc == NULL && \ - lst.take_arg == false && lst.callback == NULL) +/*! + * \brief Print helps with a list of argument, and exit + * \param[in] args List of arguments to print + * \param[in] ret Return code of the exit + */ +void opt_help(const mopts_t *opts, u8_t ret); +/*! + * \brief Print the program name, the version and the maintainer, then exit + * \param[in] ret Return code of the exit + */ +void p_version(u8_t ret); -u32_t read_opt(const int ac, char **av, const margs_t *args); -void opt_help(const margs_t *args, u8_t ret); -void p_version(u8_t ret); +void usage(const mopts_t *opts); #endif /* M_ARGS_H */ diff --git a/inc/m_file.h b/inc/m_file.h @@ -0,0 +1,67 @@ +/*********************************** LICENSE **********************************\ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ + +#ifndef M_FILE_H +# define M_FILE_H + +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +# include <stdlib.h> +# include <fcntl.h> + +/*! + * \brief Read an entire file from an fd + * + * \param[in] fd File descriptor + * + * Read an entire from a fd, in two syscalls + * + * \return The content of the file, NULL on failure + */ +char *mpm_read_file_from_fd(int fd); + +/*! + * \brief Read an entire file from a filename + * + * \param[in] fn Filename + * + * Read an entire file from a fd, in 4 syscalls + * + * \return The content of the file, NULL on failure + */ +char *mpm_read_file_from_fn(const char *fn); + +/*! + * \brief Get a size from an fd, in bytes + * + * \param[in] fd File descriptor + * + * \return The size of file, 0 on failure + */ +off_t mpm_get_file_size_from_fd(int fd); + +/*! + * \brief Get a size from a filename, in bytes + * + * \param[in] fn Filename + * + * \return The size of the file, 0 on failure + */ +off_t mpm_get_file_size_from_fn(const char *fn); + + +#endif /* M_FILE_H */ diff --git a/inc/m_infos.h b/inc/m_infos.h @@ -23,11 +23,40 @@ # define INFOS_G_LEN_MAX 150 -void set_program_name(const char *str); -void set_version(const char *str); -void set_maintainer(const char *str); -const char *get_program_name(void); -const char *get_version(void); -const char *get_maintainer(void); +/*! + * \brief Set the program name to a string + * \param[in] str String to set to + */ +void set_program_name(const char *str); + +/*! + * \brief Set the version name to a string + * \param[in] str String to set to + */ +void set_version(const char *str); + +/*! + * \brief Set the maintainer name to a string + * \param[in] str String to set to + */ +void set_maintainer(const char *str); + +/*! + * \brief Get the program name + * \note If the program is not set, return NULL + */ +const char *get_program_name(void); + +/*! + * Get the version + * \note If the version is not set, return NULL + */ +const char *get_version(void); + +/*! + * Get the maintainer + * \note If the maintainer is not set, return NULL + */ +const char *get_maintainer(void); #endif /* M_INFOS_H */ diff --git a/inc/m_list.h b/inc/m_list.h @@ -21,28 +21,28 @@ # include <stdlib.h> /* malloc */ # include <assert.h> /* assert */ # include <string.h> /* mem{cpy,move,cmp} */ -# include <morphux.h> +# include <m_types.h> /* Linked list */ -typedef struct s_list { - void *member; /* Actual member */ - u32_t size; /* Size of the member */ - struct s_list *next; /* Next in list */ - struct s_list *prev; /* Prev in list */ - struct s_list *head; /* Head of the list */ -} mlist_t; +typedef struct list_s { + void *member; /*!< Actual member */ + u32_t size; /*!< Size of the member */ + struct list_s *next; /*!< Next in list */ + struct list_s *prev; /*!< Prev in list */ + struct list_s *head; /*!< Head of the list */ +} mlist_t; /* Defines */ # define list_add(ptr, member, sizeZ) ptr = list_add_member(ptr, member, sizeZ); # define list_for_each(org_list, temp, p_tr)\ - for (temp = org_list, p_tr = temp->member;\ - temp && (p_tr = temp->member); temp = temp->next) + for (temp = org_list, p_tr = temp->member;\ + temp && (p_tr = temp->member); temp = temp->next) # define list_for_each_rev(org_list, temp, p_tr)\ - for (temp = list_get_last(org_list), p_tr = temp->member;\ - temp != temp->head && (p_tr = temp->member); temp = temp->prev) + for (temp = list_get_last(org_list), p_tr = temp->member;\ + temp != temp->head && (p_tr = temp->member); temp = temp->prev) # define list_tail(org_list) list_get_last(org_list); @@ -53,15 +53,103 @@ typedef struct s_list { # define list_del(org_list, p_tr1, sizeZ, fn) org_list = list_remove(org_list, p_tr1, sizeZ, fn); -/* Functions */ -mlist_t *list_add_member(mlist_t *list, void *member, u32_t size); -mlist_t *list_get_last(mlist_t *list); -mlist_t *list_insert_after(mlist_t *org, mlist_t *ptr, void *member, u32_t size); -mlist_t *list_insert_before(mlist_t *org, mlist_t *ptr, void *member, u32_t size); -u32_t list_size(mlist_t *list); -mlist_t *list_free(mlist_t *list, int (*free_fn)(void *member)); -void *list_get(mlist_t *list, void *member, size_t size); -mlist_t *list_remove(mlist_t *list, void *member, size_t size, - int (*free_fn)(void *member)); +/*! + * \brief Add a new member to a linked list + * \param list Head of a linked list + * \param member New member to add + * \param size Size of the new member + * + * This function will add the member at the end of the list. If the list + * pointer is null, a new list head is returned. + * Member is copied with a memcpy, in a pre-allocated pointer of size. + * + * \note If member is NULL, NULL is returned + * \note If size is equal to 0, NULL is returned + * \note Member is _not freed_ by this function + */ +mlist_t *list_add_member(mlist_t *list, void *member, u32_t size); + +/*! + * \brief Get list last member + * \param list Head of the list + * \return The last member of the list + * \note If the list head is NULL, NULL is returned + */ +mlist_t *list_get_last(mlist_t *list); + +/*! + * \brief Insert a new member after a given existing member in a list + * \param org List head + * \param ptr Pointer used to add member after + * \param member New member to add + * \param size Size of the new member + * + * This function will try to add a new member after a given ptr. + * If list head is NULL, a new list is returned + * If the given pointer is not in the list, the new member is added at the end + */ +mlist_t *list_insert_after(mlist_t *org, mlist_t *ptr, void *member, u32_t size); + +/*! + * \brief Insert a new member before a given existing member in a list + * \param org List head + * \param ptr Pointer used to add member after + * \param member New member to add + * \param size Size of the new member + * + * This function will try to add a new member before a given ptr + * If list head is NULL, a new list is returned + * If the given ptr is the current head, the head is updated with the new + * member. + * If the given ptr is not in the list, the member is added at the end + */ +mlist_t *list_insert_before(mlist_t *org, mlist_t *ptr, void *member, u32_t size); + +/*! + * \brief Return the size of a list + * \param list List head + * \note If the list head is NULL, this function will return 0 + */ +u32_t list_size(mlist_t *list); + +/*! + * \brief Free a list + * \param list List head + * \param free_fn Function pointer to free the member + * + * This function will try to free a linked list. + * free_fn function pointer is used to free the members. + * This function must return something besides 0 in order to this function to + * carry on. + * If the free_fn function return 0, this function will stop, and return the + * current not-freed pointer. + * + * \return If the list has been entirely freed, this function will return NULL + */ +mlist_t *list_free(mlist_t *list, int (*free_fn)(void *member)); + +/*! + * \brief Search a member in a list + * \param list List head + * \param member Member to search + * \param size Size of the member + * + * Search member in list. + * If member is found, return a pointer to it + * If not, NULL is returned + */ +void *list_get(mlist_t *list, void *member, size_t size); + +/*! + * \brief Remove a member from the list + * \param list List head + * \param member Member to remove + * \param size Size of the member (Used for memcmp) + * \param free_fn Function use to free the member + * + * Remove a member in a list. + */ +mlist_t *list_remove(mlist_t *list, void *member, size_t size, + int (*free_fn)(void *member)); #endif diff --git a/inc/m_print.h b/inc/m_print.h @@ -17,7 +17,10 @@ #ifndef M_PRINT_H # define M_PRINT_H -# define _XOPEN_SOURCE 700 +# define _XOPEN_SOURCE 700 +# if defined(__APPLE__) +# define _DARWIN_C_SOURCE +# endif # include <stdarg.h> # include <stdio.h> @@ -33,12 +36,51 @@ # define M_LOG_FORCE (1 << 2) # define M_LOG_TRUNC (1 << 3) -void m_panic(const char *str, ...); -void m_error(const char *str, ...); -void m_warning(const char *str, ...); -void m_info(const char *str, ...); -bool m_log(const char *str, ...); -bool m_init_log(const char *str, u8_t flags); -bool m_clean_log(void); +/*! + * \brief Print a string in an error fomat, then call exit with 1 + * \note Support printf format + */ +void m_panic(const char *str, ...); + +/*! + * \brief Print a string with an error format + * \note Support printf format + */ +void m_error(const char *str, ...); + +/*! + * \brief Print a string with a warning format + * \note Support printf format + */ +void m_warning(const char *str, ...); + +/*! + * \brief Print a string with an information format + * \note Support printf format + */ +void m_info(const char *str, ...); + +/*! + * \brief Write a string in an already opened log file + * \return true on success, false on failure + * \note Support printf format + */ +bool m_log(const char *str, ...); + +/*! + * \brief Initialize the logging for the program + * \param[in] str File path + * \param[in] flags Flag (see defines M_LOG_* in m_print.h) + * + * Open a file with O_APPEND flag, and keep it open. + * If the M_LOG_FORCE flag is true, all the calls to m_{panic,error,warning,info} + * will be written in the log file. + */ +bool m_init_log(const char *str, u8_t flags); + +/*! + * \brief Close a log file descriptor and reset the flags + */ +bool m_clean_log(void); #endif /* M_PRINT_H */ diff --git a/inc/m_test.h b/inc/m_test.h @@ -23,43 +23,119 @@ # include <m_list.h> # include <m_print.h> -typedef struct s_test { - char *(*fn_test)(void); - char *group; - char *name; -} mtest_t; +typedef struct test_s { + char *(*fn_test)(void); + char *group; + char *name; +} mtest_t; -typedef struct s_test_results { - char *group_name; - u32_t success; - u32_t failed; - u32_t total; -} mtest_results_t; +typedef struct test_results_s { + char *group_name; + u32_t success; + u32_t failed; + u32_t total; +} mtest_results_t; /* Defines */ -# define TEST_SUCCESS 0x0 -# define LINE_SIZE 80 -# define TITLE_LEN LINE_SIZE +# define TEST_SUCCESS 0x0 +# define LINE_SIZE 80 +# define TITLE_LEN LINE_SIZE -# define TEST(name) char *test_##name(void) +# define TEST(name) char *test_##name(void) # define reg_test(group, name) register_test(group, &test_##name, #name); # define TEST_ASSERT(condition, error_name) {\ - if (!(condition)) {\ - char *ret = NULL; \ - asprintf(&ret, "\t%s: Test: '%s', File %s:%d", error_name, #condition, __FILE__, __LINE__);\ - return ret;\ - }\ + if (!(condition)) {\ + char *__ret = NULL; \ + asprintf(&__ret, "\n%s\tFunction: %s\n%s\tError: %s\n%s\tTest: '%s'\n%s\tFile: %s:%d\n%s", \ + "\033[0;31m> \033[0m", \ + __FUNCTION__, \ + "\033[0;31m> \033[0m", \ + error_name, \ + "\033[0;31m> \033[0m", \ + #condition, \ + "\033[0;31m> \033[0m", \ + __FILE__, \ + __LINE__, \ + "\033[0;31m> \033[0m" \ + );\ + return __ret;\ + }\ } +# define TEST_ASSERT_FMT(condition, error_name, ...) {\ + if (!(condition)) {\ + char *__ret = NULL, *tmp = NULL; \ + asprintf(&tmp, error_name, __VA_ARGS__); \ + asprintf(&__ret, "\n%s\tFunction: %s\n%s\tError: %s\n%s\tTest: '%s'\n%s\tFile: %s:%d\n%s", \ + "\033[0;31m> \033[0m", \ + __FUNCTION__, \ + "\033[0;31m> \033[0m", \ + tmp, \ + "\033[0;31m> \033[0m", \ + #condition, \ + "\033[0;31m> \033[0m", \ + __FILE__, \ + __LINE__, \ + "\033[0;31m> \033[0m" \ + );\ + free(tmp); \ + return __ret;\ + }\ +} + + + +#ifdef COMPILE_WITH_TEST +# define MPX_STATIC +#else +# define MPX_STATIC static +#endif + +/*! + * \brief Print a title + * \param s Title name + */ +void title(char *s); + +/*! + * \brief Register a test + * \param group Group name + * \param fn_test Test function + * \param name Test name + * + * \note Do not call this function directly, use test_reg macro instead. + */ +void register_test(char *group, char *(*fn_test)(void), char *name); + +/*! + * \brief Test an entire group of tests + * \param group Group name + */ +mtest_results_t test_group(char *group); + +/*! + * \brief Test all registered tests + * \return Numbers of tests failed + */ +u32_t test_all(void); + +/*! + * \brief Free all the test + */ +void test_free(void); + +/*! + * \brief Free a single test + * \note Used in test_free, as a list_free callback + */ +int single_test_free(void *ptr); + +/*! + * \brief Free a mtest_results_t + * \note Used in test_all, as a list_free callback + */ +int single_result_free(void *ptr); -/* Functions */ -void title(char *s); -void register_test(char *group, char *(*fn_test)(void), char *name); -mtest_results_t test_group(char *group); -u32_t test_all(void); -void test_free(void); -int single_test_free(void *ptr); -int single_result_free(void *ptr); -void print_result(const char *title, u32_t success, u32_t failed); +void print_result(const char *title, u32_t success, u32_t failed); #endif /* M_TEST_H */ diff --git a/inc/m_types.h b/inc/m_types.h @@ -19,14 +19,14 @@ /* Generic types */ -typedef signed char s8_t; -typedef signed short s16_t; -typedef signed int s32_t; -typedef signed long long s64_t; +typedef signed char s8_t; +typedef signed short s16_t; +typedef signed int s32_t; +typedef signed long long s64_t; -typedef unsigned char u8_t; -typedef unsigned short u16_t; -typedef unsigned int u32_t; -typedef unsigned long long u64_t; +typedef unsigned char u8_t; +typedef unsigned short u16_t; +typedef unsigned int u32_t; +typedef unsigned long long u64_t; #endif /* M_TYPES_H */ diff --git a/inc/morphux.h b/inc/morphux.h @@ -21,10 +21,11 @@ # include <m_types.h> # include <m_print.h> -# include <m_args.h> # include <m_infos.h> # include <m_list.h> +# include <m_args.h> # include <m_test.h> +# include <m_file.h> # include <fail_test.h> diff --git a/src/m_args.c b/src/m_args.c @@ -1,179 +1,247 @@ /*********************************** LICENSE **********************************\ -* Copyright 2017 Morphux * -* * -* Licensed under the Apache License, Version 2.0 (the "License"); * -* you may not use this file except in compliance with the License. * -* You may obtain a copy of the License at * -* * -* http://www.apache.org/licenses/LICENSE-2.0 * -* * -* Unless required by applicable law or agreed to in writing, software * -* distributed under the License is distributed on an "AS IS" BASIS, * -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * -* See the License for the specific language governing permissions and * -* limitations under the License. * -\******************************************************************************/ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ #include <m_args.h> +#include <m_list.h> + #define LIB_OPT_TOKEN_HELP 'h' #define LIB_OPT_STRING_HELP "help" #define LIB_OPT_TOKEN_VERSION 'V' #define LIB_OPT_STRING_VERSION "version" -/*! - * \brief Read the options given by the program - * \param ac Number of argument in av - * \param av Array of string, containing the arguments - * \param args Array of margs_t, containing the preset options. Must end with - * an empty structure. - * \return Number of options read - * - * The read_opt function reads a given list of arguments, and parse the options - * in it. The options are read from the args array. - * If an option is not known, the function calls the help and quit. - * If the option -h | --help is passed, the function call the help and quit. - * If the option -v | --version is passed, the function call the version and quit. - * - * \note Only the arguments beginning with - are parsed. - */ -u32_t read_opt(const int ac, char **av, const margs_t *args) { - u32_t ret = 0, it, k; - u8_t n_dash; - - if (ac == 0 || av == NULL || args == NULL) - return ret; - - for (u32_t i = 1, j = 0; i < (u32_t)ac; i++) { - - if (av[i] == NULL || strlen(av[i]) == 0) - continue ; - - n_dash = 0; - for (j = 0; av[i][j] != '\0' && av[i][j] == '-'; j++) - n_dash++; - - /* Argument have more than two '-' */ - if (n_dash > 2) { - m_error("Malformed option: %s\n", av[i]); - continue ; - } - - /* Single letter option */ - if (n_dash == 1) { - - if (strlen(av[i]) < 2) { - m_error("Dash without option. Ignoring...\n"); - continue ; - } - - /* Builtins options */ - if (av[i][1] == LIB_OPT_TOKEN_HELP) - opt_help(args, 0); - else if (av[i][1] == LIB_OPT_TOKEN_VERSION) - p_version(0); - - /* Search the option in the args array */ - for (u32_t z = 1; av[i][z] != '\0'; z++) { - for (it = 0; !IS_EOL(args[it]) && args[it].opt != av[i][z]; it++) - ; - - /* Can't find the option */ - if (IS_EOL(args[it])) { - m_error("Unknow option -%s\n", &(av[i][z])); - opt_help(args, 1); - } else { - if (args[it].take_arg) { - if (i + 1 < (u32_t)ac) { - args[it].callback(av[++i]); - ret++; - break ; - } else { - m_error("Option -%c must take an argument\n", - args[it].opt); - opt_help(args, 1); - } - } else { - args[it].callback(NULL); - ret++; - } - } - } - - /* Word option */ - } else if (n_dash == 2) { - bool got_arg = false; - - if (strlen(av[i]) < 3) { - m_error("Double dash without option. Ignoring...\n"); - continue ; - } - - /* Builtins options */ - if (strcmp(&(av[i][2]), LIB_OPT_STRING_HELP) == 0) - opt_help(args, 0); - else if (strcmp(&(av[i][2]), LIB_OPT_STRING_VERSION) == 0) - p_version(0); - - /* Look for an argument */ - for (k = 2; av[i][k] != '\0' && av[i][k] != '='; k++) - ; - - if (av[i][k] != '\0') { - got_arg = true; - k -= 2; - } else { - k = strlen(av[i]) - 2; - } - - /* Search the option in the args array */ - for (it = 0; !IS_EOL(args[it]) && - (strncmp(args[it].s_opt, &(av[i][2]), k) != 0); it++) - ; - - /* Can't find the option */ - if (IS_EOL(args[it])) { - m_error("Unknown option %s\n", av[i]); - opt_help(args, 1); - } else { - if (args[it].take_arg && !got_arg) { - m_error("Option %s must take an argument", args[it].s_opt); - opt_help(args, 1); - } - if (got_arg) - args[it].callback(&(av[i][k + 3])); - else - args[it].callback(NULL); - ret++; - } - } - } - return ret; +u32_t read_opt(const int ac, char **av, const mopts_t *opts, mlist_t **args) { + u32_t ret = 0, i = 1, it, k; + u8_t n_dash; + + if (ac == 0 || av == NULL || opts == NULL || args == NULL) + return ret; + + for (/* Using u32_t i */ u32_t j = 0; i < (u32_t)ac; i++) + { + + if (av[i] == NULL || strlen(av[i]) == 0) + continue ; + + n_dash = 0; + for (j = 0; av[i][j] != '\0' && av[i][j] == '-'; j++) + n_dash++; + + /* Single letter option */ + if (n_dash == 1) + { + + /* If single dash alone, its a parameter */ + if (strlen(av[i]) < 2) + { + list_add(*args, av[i], strlen(av[i]) + 1); + continue ; + } + + /* Builtins options */ + if (av[i][1] == LIB_OPT_TOKEN_HELP) + opt_help(opts, 0); + else if (av[i][1] == LIB_OPT_TOKEN_VERSION) + p_version(0); + + /* Search the option in the opts array */ + for (u32_t z = 1; av[i][z] != '\0'; z++) + { + for (it = 0; !IS_EOL(opts[it]) && opts[it].opt != av[i][z]; it++) + ; + + /* Can't find the option */ + if (IS_EOL(opts[it])) + { + m_error("Unknow option -%s\n", &(av[i][z])); + opt_help(opts, 1); + } + else + { + if (opts[it].take_arg) + { + if (i + 1 < (u32_t)ac) + { + if (!opts[it].callback(av[++i])) + { + m_error("Wrong value \"%s\" for argument -%c\n", + av[i], opts[it].opt); + usage(opts); + exit(1); + } + ret++; + break ; + } + else + { + m_error("Option -%c must take an argument\n", + opts[it].opt); + opt_help(opts, 1); + } + } + else + { + opts[it].callback(NULL); + ret++; + } + } + } + /* Word option */ + } + else if (n_dash == 2) + { + bool got_arg = false; + + /* If double dash alone, skip it and then stop reading options */ + if (strlen(av[i]) < 3) + { + i++; + break ; + } + + /* Builtins options */ + if (strcmp(&(av[i][2]), LIB_OPT_STRING_HELP) == 0) + opt_help(opts, 0); + else if (strcmp(&(av[i][2]), LIB_OPT_STRING_VERSION) == 0) + p_version(0); + + /* Look for an argument */ + for (k = 2; av[i][k] != '\0' && av[i][k] != '='; k++) + ; + + if (av[i][k] != '\0') + { + got_arg = true; + k -= 2; + } + else + { + k = strlen(av[i]) - 2; + } + + /* Search the option in the opts array */ + for (it = 0; !IS_EOL(opts[it]) && + (strncmp((opts[it].s_opt != NULL ? opts[it].s_opt : ""), &(av[i][2]), k) != 0); it++) + ; + + /* Can't find the option */ + if (IS_EOL(opts[it])) + { + m_error("Unknown option %s\n", av[i]); + opt_help(opts, 1); + } + else + { + if (opts[it].take_arg && !got_arg) + { + m_error("Option %s must take an argument", opts[it].s_opt); + opt_help(opts, 1); + } + char *arg = NULL; + + if (got_arg) + arg = &(av[i][k + 3]); + if (!opts[it].callback(arg)) + { + m_error("Wrong value \"%s\" for argument --%s\n", + arg != NULL ? arg : "", opts[it].s_opt); + usage(opts); + exit(1); + } + ret++; + } + /* Not beginning with a dash */ + } + else + { + /* Stop reading options */ + list_add(*args, av[i], strlen(av[i]) + 1); + } + } + /* If reading of flags is stopped by '--' get the rest of the parameters */ + for ( /* Using u32_t i */ ; i < (u32_t)ac; i++) + { + if (av[i] && strlen(av[i])) + list_add(*args, av[i], strlen(av[i]) + 1); + } + return ret; +} + +void opt_help(const mopts_t *opts, u8_t ret) { + size_t pad = 0; + + usage(opts); + m_info("Help:\n"); + for (u32_t i = 0; opts[i].opt != 0; i++) + { + if (opts[i].s_opt != NULL && (strlen(opts[i].s_opt) + 2) > pad) + pad = strlen(opts[i].s_opt) + 6; + } + for (u32_t i = 0; opts[i].opt != 0; i++) + { + if (opts[i].s_opt != NULL) + m_info("\t-%c, --%s%*s%s\n", opts[i].opt, opts[i].s_opt, pad - (strlen(opts[i].s_opt)), "", opts[i].desc); + else + m_info("\t-%c%*s%s\n", opts[i].opt, pad + 4, "", opts[i].desc); + } + m_info("\t-%c, --%s%*s%s\n", LIB_OPT_TOKEN_HELP, LIB_OPT_STRING_HELP, + pad - (strlen(LIB_OPT_STRING_HELP)), "", "Show this help"); + m_info("\t-%c, --%s%*s%s\n", LIB_OPT_TOKEN_VERSION, LIB_OPT_STRING_VERSION, + pad - (strlen(LIB_OPT_STRING_VERSION)), "", "Print the current version"); + exit(ret); } -/*! - * \brief Print helps with a list of argument, and exit - * \param args List of arguments to print - * \param ret Return code of the exit - */ -void opt_help(const margs_t *args, u8_t ret) { - m_info("Help:\n"); - for (u32_t i = 0; args[i].opt != 0; i++) { - m_info("\t-%c | --%s : %s\n", args[i].opt, args[i].s_opt, args[i].desc); - } - write(1, "\n", 1); - m_info("If an argument requires a value, you can set it two ways:\n"); - m_info("\t-o value\n"); - m_info("\t--option=value\n"); - exit(ret); +void p_version(u8_t ret) { + m_info("Program: %s\n", get_program_name()); + m_info("Version: %s\n", get_version()); + m_info("%s\n", get_maintainer()); + exit(ret); } -/*! - * \brief Print the program name, the version and the maintainer, then exit - * \param ret Return code of the exit - */ -void p_version(u8_t ret) { - m_info("Program: %s\n", get_program_name()); - m_info("Version: %s\n", get_version()); - m_info("%s\n", get_maintainer()); - exit(ret); +void usage(const mopts_t *opts) { + u8_t i; + + m_info("Usage: %s [-%c%c", get_program_name(), LIB_OPT_TOKEN_HELP, + LIB_OPT_TOKEN_VERSION); + fflush(stdout); + for (i = 0; !IS_EOL(opts[i]); i++) + { + if (opts[i].take_arg == false && opts[i].opt != '\0') + { + write(1, &opts[i].opt, 1); + } + } + fprintf(stdout, "] "); + + for (i = 0; !IS_EOL(opts[i]); i++) + { + if (opts[i].take_arg == true && opts[i].usage != NULL) + { + if (opts[i].opt != '\0') + fprintf(stdout, "[-%c %s] ", opts[i].opt, opts[i].usage); + else if (opts[i].s_opt) + fprintf(stdout, "[--%s=%s] ", opts[i].s_opt, opts[i].usage); + } + } + + for (i = 0; !IS_EOL(opts[i]); i++) + { + if (opts[i].take_arg == false && opts[i].opt == '\0') + { + fprintf(stdout, "[--%s] ", opts[i].s_opt); + } + } + fprintf(stdout, "\n"); } diff --git a/src/m_file.c b/src/m_file.c @@ -0,0 +1,85 @@ +/*********************************** LICENSE **********************************\ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ + +#include <morphux.h> + +off_t mpm_get_file_size_from_fd(int fd) { + struct stat buf; + if (fd == -1) + return 0; + + if (fstat(fd, &buf) == -1) + return 0; + return buf.st_size; +} + +off_t mpm_get_file_size_from_fn(const char *fn) { + int fd; + off_t ret; + + if (fn == NULL) + return 0; + + fd = open(fn, O_RDONLY); + if (fd == -1) + return 0; + ret = mpm_get_file_size_from_fd(fd); + close(fd); + return ret; +} + +char *mpm_read_file_from_fd(int fd) { + char *ret = NULL; + off_t size; + + if (fd == -1) + return NULL; + + size = mpm_get_file_size_from_fd(fd); + if (size == 0) + return NULL; + + ret = malloc(size + 1); + if (ret == NULL) + return NULL; + + if (read(fd, ret, size) == -1) + goto cleanup; + + ret[size] = 0; + + return ret; + +cleanup: + free(ret); + return NULL; +} + +char *mpm_read_file_from_fn(const char *fn) { + int fd; + char *ret = NULL; + + if (fn == NULL) + return NULL; + + fd = open(fn, O_RDONLY); + if (fd == -1) + return NULL; + + ret = mpm_read_file_from_fd(fd); + close(fd); + return ret; +} diff --git a/src/m_infos.c b/src/m_infos.c @@ -1,86 +1,62 @@ /*********************************** LICENSE **********************************\ -* Copyright 2017 Morphux * -* * -* Licensed under the Apache License, Version 2.0 (the "License"); * -* you may not use this file except in compliance with the License. * -* You may obtain a copy of the License at * -* * -* http://www.apache.org/licenses/LICENSE-2.0 * -* * -* Unless required by applicable law or agreed to in writing, software * -* distributed under the License is distributed on an "AS IS" BASIS, * -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * -* See the License for the specific language governing permissions and * -* limitations under the License. * -\******************************************************************************/ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ #include <m_infos.h> /* Globals */ -static char program[INFOS_G_LEN_MAX] = ""; -static char version[INFOS_G_LEN_MAX] = ""; -static char maintainer[INFOS_G_LEN_MAX] = ""; - -/*! - * Set the program name to a string - */ -void set_program_name(const char *str) { - if (str == NULL) { - strcpy(program, ""); - } else if (strlen(str) < INFOS_G_LEN_MAX) { - strcpy(program, str); - } +static char program[INFOS_G_LEN_MAX] = ""; +static char version[INFOS_G_LEN_MAX] = ""; +static char maintainer[INFOS_G_LEN_MAX] = ""; + +void set_program_name(const char *str) { + if (str == NULL) + strcpy(program, ""); + else if (strlen(str) < INFOS_G_LEN_MAX) + strcpy(program, str); } -/*! - * Set the version name to a string - */ -void set_version(const char *str) { - if (str == NULL) { - strcpy(version, ""); - } else if (strlen(str) < INFOS_G_LEN_MAX) { - strcpy(version, str); - } +void set_version(const char *str) { + if (str == NULL) + strcpy(version, ""); + else if (strlen(str) < INFOS_G_LEN_MAX) + strcpy(version, str); } -/*! - * Set the maintainer name to a string - */ -void set_maintainer(const char *str) { - if (str == NULL) { - strcpy(maintainer, ""); - } else if (strlen(str) < INFOS_G_LEN_MAX) { - strcpy(maintainer, str); - } +void set_maintainer(const char *str) { + if (str == NULL) + strcpy(maintainer, ""); + else if (strlen(str) < INFOS_G_LEN_MAX) + strcpy(maintainer, str); } -/*! - * Get the program name - * \note If the program is not set, return NULL - */ -const char *get_program_name(void) { - if (strlen(program) != 0) - return program; - return NULL; +const char *get_program_name(void) { + if (strlen(program) != 0) + return program; + return NULL; } -/*! - * Get the version - * \note If the version is not set, return NULL - */ -const char *get_version(void) { - if (strlen(version) != 0) - return version; - return NULL; +const char *get_version(void) { + if (strlen(version) != 0) + return version; + return NULL; } -/*! - * Get the maintainer - * \note If the maintainer is not set, return NULL - */ -const char *get_maintainer(void) { - if (strlen(maintainer) != 0) - return maintainer; - return NULL; +const char *get_maintainer(void) { + if (strlen(maintainer) != 0) + return maintainer; + return NULL; } diff --git a/src/m_list.c b/src/m_list.c @@ -1,303 +1,241 @@ /*********************************** LICENSE **********************************\ -* Copyright 2017 Morphux * -* * -* Licensed under the Apache License, Version 2.0 (the "License"); * -* you may not use this file except in compliance with the License. * -* You may obtain a copy of the License at * -* * -* http://www.apache.org/licenses/LICENSE-2.0 * -* * -* Unless required by applicable law or agreed to in writing, software * -* distributed under the License is distributed on an "AS IS" BASIS, * -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * -* See the License for the specific language governing permissions and * -* limitations under the License. * -\******************************************************************************/ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ #include <m_list.h> -/*! - * \brief Add a new member to a linked list - * \param list Head of a linked list - * \param member New member to add - * \param size Size of the new member - * - * This function will add the member at the end of the list. If the list - * pointer is null, a new list head is returned. - * Member is copied with a memcpy, in a pre-allocated pointer of size. - * - * \note If member is NULL, NULL is returned - * \note If size is equal to 0, NULL is returned - * \note Member is _not freed_ by this function - */ -mlist_t *list_add_member(mlist_t *list, void *member, u32_t size) { - mlist_t *n_member, *tmp; - - if (member == NULL || size <= 0) - return NULL; - - /* Allocate the list container and the new member */ - n_member = malloc(sizeof(mlist_t)); - n_member->member = malloc(size); - assert(n_member && n_member->member); - - /* Copy the new member */ - memcpy(n_member->member, member, size); - n_member->size = size; - n_member->next = n_member->prev = NULL; - - /* If the list head is NULL, we return the new */ - if (list == NULL) { - n_member->head = n_member; - return n_member; - } - - /* Else, we go the end of the list */ - for (tmp = list; tmp->next != NULL; tmp = tmp->next) - ; - tmp->next = n_member; - n_member->prev = tmp; - n_member->head = list; - return list; +mlist_t *list_add_member(mlist_t *list, void *member, u32_t size) { + mlist_t *n_member, *tmp; + + if (member == NULL || size <= 0) + return NULL; + + /* Allocate the list container and the new member */ + n_member = malloc(sizeof(mlist_t)); + n_member->member = malloc(size); + assert(n_member && n_member->member); + + /* Copy the new member */ + memcpy(n_member->member, member, size); + n_member->size = size; + n_member->next = n_member->prev = NULL; + + /* If the list head is NULL, we return the new */ + if (list == NULL) + { + n_member->head = n_member; + return n_member; + } + + /* Else, we go the end of the list */ + for (tmp = list; tmp->next != NULL; tmp = tmp->next) + ; + tmp->next = n_member; + n_member->prev = tmp; + n_member->head = list; + return list; } -/*! - * \brief Get list last member - * \param list Head of the list - * \return The last member of the list - * \note If the list head is NULL, NULL is returned - */ -mlist_t *list_get_last(mlist_t *list) { - mlist_t *tmp; - - if (list == NULL) - return NULL; - for (tmp = list; tmp->next; tmp = tmp->next) - ; - return tmp; +mlist_t *list_get_last(mlist_t *list) +{ + mlist_t *tmp; + + if (list == NULL) + return NULL; + for (tmp = list; tmp->next; tmp = tmp->next) + ; + return tmp; } -/*! - * \brief Insert a new member after a given existing member in a list - * \param org List head - * \param ptr Pointer used to add member after - * \param member New member to add - * \param size Size of the new member - * - * This function will try to add a new member after a given ptr. - * If list head is NULL, a new list is returned - * If the given pointer is not in the list, the new member is added at the end - */ -mlist_t *list_insert_after(mlist_t *org, mlist_t *ptr, void *member, u32_t size) { - mlist_t *n_member, *tmp, *tmp2; - - /* Allocate the new member */ - n_member = malloc(sizeof(mlist_t)); - n_member->member = malloc(size); - assert(n_member && n_member->member); - - /* Copy the new member */ - memcpy(n_member->member, member, size); - n_member->size = size; - n_member->next = n_member->prev = NULL; - - /* If the list head is NULL, we return the new member */ - if (org == NULL) { - n_member->head = n_member; - return n_member; - } - - /* Search for the given ptr */ - for (tmp = org; tmp->next && tmp != ptr; tmp = tmp->next) - ; - - /* If the ptr is not in the list, add the new member at the end of head */ - if (tmp->next == NULL) { - tmp->next = n_member; - n_member->prev = tmp; - } else { - tmp2 = tmp->next; - tmp->next = n_member; - n_member->prev = tmp; - n_member->next = tmp2; - tmp2->prev = n_member; - } - n_member->head = org; - return org; +mlist_t *list_insert_after(mlist_t *org, mlist_t *ptr, void *member, u32_t size) { + mlist_t *n_member, *tmp, *tmp2; + + /* Allocate the new member */ + n_member = malloc(sizeof(mlist_t)); + n_member->member = malloc(size); + assert(n_member && n_member->member); + + /* Copy the new member */ + memcpy(n_member->member, member, size); + n_member->size = size; + n_member->next = n_member->prev = NULL; + + /* If the list head is NULL, we return the new member */ + if (org == NULL) + { + n_member->head = n_member; + return n_member; + } + + /* Search for the given ptr */ + for (tmp = org; tmp->next && tmp != ptr; tmp = tmp->next) + ; + + /* If the ptr is not in the list, add the new member at the end of head */ + if (tmp->next == NULL) + { + tmp->next = n_member; + n_member->prev = tmp; + } + else + { + tmp2 = tmp->next; + tmp->next = n_member; + n_member->prev = tmp; + n_member->next = tmp2; + tmp2->prev = n_member; + } + n_member->head = org; + return org; } -/*! - * \brief Insert a new member before a given existing member in a list - * \param org List head - * \param ptr Pointer used to add member after - * \param member New member to add - * \param size Size of the new member - * - * This function will try to add a new member before a given ptr - * If list head is NULL, a new list is returned - * If the given ptr is the current head, the head is updated with the new - * member. - * If the given ptr is not in the list, the member is added at the end - */ -mlist_t *list_insert_before(mlist_t *org, mlist_t *ptr, void *member, u32_t size) { - mlist_t *n_member, *tmp, *tmp2; - - /* Allocate the new member */ - n_member = malloc(sizeof(mlist_t)); - n_member->member = malloc(size); - assert(n_member && n_member->member); - - /* Copy the new member */ - memcpy(n_member->member, member, size); - n_member->size = size; - n_member->next = n_member->prev = NULL; - - /* If the list is NULL, we return the new member */ - if (org == NULL) { - n_member->head = n_member; - return n_member; - } - - /* Search for the given ptr */ - for (tmp = org; tmp->next != NULL && tmp != ptr; tmp = tmp->next) - ; - - /* If the ptr is not in the list, add the new member at the end of head */ - if (tmp->next == NULL) { - tmp->next = n_member; - n_member->prev = tmp; - - /* If the given ptr is the head, replace the head by the new member */ - } else if (ptr == org) { - n_member->next = org; - n_member->prev = NULL; - org->prev = n_member; - org = n_member; - } else { - tmp2 = tmp->prev; - n_member->next = tmp; - tmp->prev = n_member; - tmp2->next = n_member; - } - n_member->head = org; - return org; +mlist_t *list_insert_before(mlist_t *org, mlist_t *ptr, void *member, u32_t size) { + mlist_t *n_member, *tmp, *tmp2; + + /* Allocate the new member */ + n_member = malloc(sizeof(mlist_t)); + n_member->member = malloc(size); + assert(n_member && n_member->member); + + /* Copy the new member */ + memcpy(n_member->member, member, size); + n_member->size = size; + n_member->next = n_member->prev = NULL; + + /* If the list is NULL, we return the new member */ + if (org == NULL) + { + n_member->head = n_member; + return n_member; + } + + /* Search for the given ptr */ + for (tmp = org; tmp->next != NULL && tmp != ptr; tmp = tmp->next) + ; + + /* If the ptr is not in the list, add the new member at the end of head */ + if (tmp->next == NULL) + { + tmp->next = n_member; + n_member->prev = tmp; + } + /* If the given ptr is the head, replace the head by the new member */ + else if (ptr == org) + { + n_member->next = org; + n_member->prev = NULL; + org->prev = n_member; + org = n_member; + } + else + { + tmp2 = tmp->prev; + n_member->next = tmp; + tmp->prev = n_member; + tmp2->next = n_member; + } + n_member->head = org; + return org; } -/*! - * \brief Return the size of a list - * \param list List head - * \note If the list head is NULL, this function will return 0 - */ -u32_t list_size(mlist_t *list) { - u32_t i; - mlist_t *tmp; - - if (list == NULL) - return 0; - - for (tmp = list, i = 0; tmp != NULL; tmp = tmp->next, i++) - ; - return i; +u32_t list_size(mlist_t *list) { + u32_t i; + mlist_t *tmp; + + if (list == NULL) + return 0; + + for (tmp = list, i = 0; tmp != NULL; tmp = tmp->next, i++) + ; + return i; } -/*! - * \brief Free a list - * \param list List head - * \param free_fn Function pointer to free the member - * - * This function will try to free a linked list. - * free_fn function pointer is used to free the members. - * This function must return something besides 0 in order to this function to - * carry on. - * If the free_fn function return 0, this function will stop, and return the - * current not-freed pointer. - * - * \return If the list has been entirely freed, this function will return NULL - */ -mlist_t *list_free(mlist_t *list, int (*free_fn)(void *member)) { - mlist_t *tmp = list, *tmp2; - - while (tmp != NULL) { - tmp2 = tmp->next; - if (free_fn != NULL && free_fn(tmp->member) == 0) { - return tmp; - } else { - free(tmp->member); - free(tmp); - } - tmp = tmp2; - } - return NULL; +mlist_t *list_free(mlist_t *list, int (*free_fn)(void *member)) { + mlist_t *tmp = list, *tmp2; + + while (tmp != NULL) + { + tmp2 = tmp->next; + if (free_fn != NULL && free_fn(tmp->member) == 0) + { + return tmp; + } + else + { + free(tmp->member); + free(tmp); + } + tmp = tmp2; + } + return NULL; } -/*! - * \brief Search a member in a list - * \param list List head - * \param member Member to search - * \param size Size of the member - * - * Search member in list. - * If member is found, return a pointer to it - * If not, NULL is returned - */ -void *list_get(mlist_t *list, void *member, size_t size) { - mlist_t *tmp; - void *ptr; - - if (list == NULL) - return NULL; - list_for_each(list, tmp, ptr) { - if (memcmp(ptr, member, size) == 0 && (size == tmp->size)) - return ptr; - } - return NULL; +void *list_get(mlist_t *list, void *member, size_t size) { + mlist_t *tmp; + void *ptr; + + if (list == NULL) + return NULL; + list_for_each(list, tmp, ptr) + { + if (memcmp(ptr, member, size) == 0 && (size == tmp->size)) + return ptr; + } + return NULL; } -/*! - * \brief Remove a member from the list - * \param list List head - * \param member Member to remove - * \param size Size of the member (Used for memcmp) - * \param free_fn Function use to free the member - * - * Remove a member in a list. - */ -mlist_t *list_remove(mlist_t *list, void *member, size_t size, - int (*free_fn)(void *member)) { - mlist_t *tmp, *tmp2; - void *ptr; - - if (list == NULL) - return NULL; - - /* Search for the member */ - list_for_each(list, tmp2, ptr) { - if (memcmp(member, ptr, size) == 0) - break ; - } - - /* We can't find the member */ - if (tmp2 == NULL) { - return list; - } - - if (tmp2 == list) { - /* Replace the head */ - list = tmp2->next; - - /* Update the head in all other members */ - list_for_each(list, tmp, ptr) { - tmp->head = list; - } - } else { - tmp2->prev->next = tmp2->next; - } - - if (free_fn != NULL) { - free_fn(tmp2->member); - } else { - free(tmp2->member); - } - free(tmp2); - return list; +mlist_t *list_remove(mlist_t *list, void *member, size_t size, + int (*free_fn)(void *member)) { + mlist_t *tmp, *tmp2; + void *ptr; + + if (list == NULL) + return NULL; + + /* Search for the member */ + list_for_each(list, tmp2, ptr) + { + if (memcmp(member, ptr, size) == 0) + break ; + } + + /* We can't find the member */ + if (tmp2 == NULL) + return list; + + if (tmp2 == list) + { + /* Replace the head */ + list = tmp2->next; + + /* Update the head in all other members */ + list_for_each(list, tmp, ptr) + { + tmp->head = list; + } + } + else + { + tmp2->prev->next = tmp2->next; + } + + if (free_fn != NULL) + free_fn(tmp2->member); + else + free(tmp2->member); + + free(tmp2); + return list; } diff --git a/src/m_print.c b/src/m_print.c @@ -19,26 +19,18 @@ static int g_log_fd = 0; static u8_t g_log_flags = M_LOG_NONE; -/*! - * \brief Initialize the logging for the program - * \param file File path - * \param force Flag (see defines M_LOG_* in m_print.h) - * - * Open a file with O_APPEN flag, and keep it open. - * If the M_LOG_FORCE flag is true, all the calls to m_{panic,error,warning,info} - * will be written in the log file. - */ -bool m_init_log(const char *file, u8_t flags) { +bool m_init_log(const char *file, u8_t flags) { u32_t open_flags = O_CREAT | O_WRONLY | O_ASYNC; - int fd; + int fd; - if (flags & M_LOG_TRUNC) { + if (flags & M_LOG_TRUNC) open_flags |= O_TRUNC; - } else { + else open_flags |= O_APPEND; - } + fd = open(file, open_flags, 0662); - if (fd != -1) { + if (fd != -1) + { if (flags & M_LOG_FORCE) g_log_flags |= M_LOG_FORCE; g_log_fd = fd; @@ -47,11 +39,10 @@ bool m_init_log(const char *file, u8_t flags) { return false; } -/*! - * \brief Close a log file descriptor and reset the flags - */ -bool m_clean_log(void) { - if (g_log_fd != 0) { + +bool m_clean_log(void) { + if (g_log_fd != 0) + { if (close(g_log_fd) != 0) return false; g_log_fd = 0; @@ -67,17 +58,17 @@ static bool m_log_v(const char *str, va_list ap) { return true; } -/*! - * \brief Print a string in an error fomat, then call exit with 1 - * \note Support printf format - */ -void m_panic(const char *str, ...) { - va_list ap; + +void m_panic(const char *str, ...) { + va_list ap; va_start(ap, str); - if (g_log_flags & M_LOG_FORCE) { + if (g_log_flags & M_LOG_FORCE) + { m_log_v(str, ap); - } else { + } + else + { write(2, "\033[0;31m> \033[0m", 13); vfprintf(stderr, str, ap); if (str[strlen(str) - 1] != '\n') @@ -87,17 +78,17 @@ void m_panic(const char *str, ...) { exit(1); } -/*! - * \brief Print a string with an error format - * \note Support printf format - */ -void m_error(const char *str, ...) { - va_list ap; + +void m_error(const char *str, ...) { + va_list ap; va_start(ap, str); - if (g_log_flags & M_LOG_FORCE) { + if (g_log_flags & M_LOG_FORCE) + { m_log_v(str, ap); - } else { + } + else + { write(2, "\033[0;31m> \033[0m", 13); vfprintf(stderr, str, ap); if (str[strlen(str) - 1] != '\n') @@ -106,45 +97,41 @@ void m_error(const char *str, ...) { va_end(ap); } -/*! - * \brief Print a string with a warning format - * \note Support printf format - */ -void m_warning(const char *str, ...) { - va_list ap; + +void m_warning(const char *str, ...) { + va_list ap; va_start(ap, str); - if (g_log_flags & M_LOG_FORCE) { + if (g_log_flags & M_LOG_FORCE) + { m_log_v(str, ap); - } else { + } + else + { write(2, "\033[0;31m> \033[0m", 13); vfprintf(stderr, str, ap); } va_end(ap); } -/*! - * \brief Print a string with an information format - * \note Support printf format - */ -void m_info(const char *str, ...) { - va_list ap; +void m_info(const char *str, ...) { + va_list ap; va_start(ap, str); - if (g_log_flags & M_LOG_FORCE) { + if (g_log_flags & M_LOG_FORCE) + { m_log_v(str, ap); - } else { + } + else + { write(1, "\033[0;34m> \033[0m", 13); vprintf(str, ap); + fflush(stdout); } va_end(ap); } -/*! - * \brief Write a string in an already opened log file - * \return true on success, false on failure - */ -bool m_log(const char *str, ...) { +bool m_log(const char *str, ...) { va_list ap; bool ret; diff --git a/src/m_test.c b/src/m_test.c @@ -1,212 +1,194 @@ /*********************************** LICENSE **********************************\ -* Copyright 2017 Morphux * -* * -* Licensed under the Apache License, Version 2.0 (the "License"); * -* you may not use this file except in compliance with the License. * -* You may obtain a copy of the License at * -* * -* http://www.apache.org/licenses/LICENSE-2.0 * -* * -* Unless required by applicable law or agreed to in writing, software * -* distributed under the License is distributed on an "AS IS" BASIS, * -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * -* See the License for the specific language governing permissions and * -* limitations under the License. * -\******************************************************************************/ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ #include <m_test.h> -static mlist_t *tests = NULL; - -/*! - * \brief Print a title - * \param s Title name - */ -void title(char *s) { - u8_t len = TITLE_LEN; - int i; - - len -= strlen(s); - for (i = 0; i < len / 2; i++, write(1, "=", 1)) - ; - write(1, " ", 1); - write(1, s, strlen(s)); - write(1, " ", 1); - for (; i < len; i++, write(1, "=", 1)); - write(1, "\n", 1); +static mlist_t *tests = NULL; + +void title(char *s) { + u8_t len = TITLE_LEN; + int i; + + len -= strlen(s); + for (i = 0; i < len / 2; i++, write(1, "━", 3)) + ; + write(1, " ", 1); + write(1, s, strlen(s)); + write(1, " ", 1); + for (; i < len; i++, write(1, "━", 3)); + write(1, "\n", 1); } -void print_result(const char *title, u32_t success, u32_t failed) { - u32_t total = success + failed; - u32_t percent = success * 100 / total; - - m_info("%s:", title); - fflush(stdout); - for (u32_t i = LINE_SIZE - strlen(title) + 1 - 14; i > 0; i--) - write(1, " ", 1); - fprintf(stdout, "%02d/%02d [", success, total); - fflush(stdout); - if (percent == 100) { - fprintf(stdout, "\033[1;32m"); - } else if (percent >= 90) { - fprintf(stdout, "\033[1;33m"); - } else { - fprintf(stdout, "\033[1;31m"); - } - fflush(stdout); - fprintf(stdout, "%03d%%\033[0;m]\n", percent); - fflush(stdout); +void print_result(const char *title, u32_t success, u32_t failed) { + u32_t total = success + failed; + u32_t percent = success * 100 / total; + u8_t padding = 14; + + if (success >= 100) + padding++; + if (total >= 100) + padding++; + m_info("%s:", title); + fflush(stdout); + for (u32_t i = LINE_SIZE - strlen(title) + 1 - padding; i > 0; i--) + write(1, " ", 1); + if (percent == 100) + fprintf(stdout, "\033[1;32m"); + else if (percent >= 90) + fprintf(stdout, "\033[1;33m"); + else + fprintf(stdout, "\033[1;31m"); + + fprintf(stdout, " %02d/%02d ", success, total); + fflush(stdout); + fprintf(stdout, "\033[0;m\n"); + fflush(stdout); } -/*! - * \brief Register a test - * \param group Group name - * \param fn_test Test function - * \param name Test name - * - * \note Do not call this function directly, use test_reg macro instead. - */ -void register_test(char *group, char *(*fn_test)(void), char *name) { - mtest_t *ptr; - - /* Allocate test */ - ptr = malloc(sizeof(mtest_t)); - assert(ptr); - ptr->group = malloc(strlen(group) + 1); - ptr->name = malloc(strlen(name) + 1); - assert(ptr->group && ptr->name); - - /* Copy name and group */ - memcpy(ptr->group, group, strlen(group)); - memcpy(ptr->name, name, strlen(name)); - ptr->name[strlen(name)] = 0; - ptr->group[strlen(group)] = 0; - assert(ptr->group && ptr->name); - ptr->fn_test = fn_test; - - /* Add to the list of tests */ - list_add(tests, ptr, sizeof(mtest_t)); - free(ptr); +void register_test(char *group, char *(*fn_test)(void), char *name) { + mtest_t *ptr; + + /* Allocate test */ + ptr = malloc(sizeof(mtest_t)); + assert(ptr); + ptr->group = malloc(strlen(group) + 1); + ptr->name = malloc(strlen(name) + 1); + assert(ptr->group && ptr->name); + + /* Copy name and group */ + memcpy(ptr->group, group, strlen(group)); + memcpy(ptr->name, name, strlen(name)); + ptr->name[strlen(name)] = 0; + ptr->group[strlen(group)] = 0; + assert(ptr->group && ptr->name); + ptr->fn_test = fn_test; + + /* Add to the list of tests */ + list_add(tests, ptr, sizeof(mtest_t)); + free(ptr); } -/*! - * \brief Test an entire group of tests - * \param group Group name - */ -mtest_results_t test_group(char *group) { - mlist_t *tmp; - mtest_t *ptr; - mtest_results_t res; - u32_t tab; - char *s_tmp; - - res.total = res.success = res.failed = 0; - res.group_name = NULL; - title(group); - - if (tests == NULL) - { - m_warning("Could not find any registered tests in %s group.\n", group); - return res; - } - - /* Iterate over each test */ - list_for_each(tests, tmp, ptr) { - if (strcmp(ptr->group, group) == 0) { - res.total++; - m_info("Testing %s ...", ptr->name); - - for (tab = strlen(ptr->name); tab < TITLE_LEN - 18; - tab++, printf(" ")) - ; - - if ((s_tmp = ptr->fn_test()) != TEST_SUCCESS) { - printf("[ \033[1;31mKO\033[0m ]\n"); - m_warning("\033[0;37m%s\033[0m\n", s_tmp); - free(s_tmp); - res.failed++; - } else { - printf("[ \033[1;32mOK\033[0m ]\n"); - res.success++; - } - } - } - - print_result("Group Results", res.success, res.failed); - return res; +mtest_results_t test_group(char *group) { + mlist_t *tmp; + mtest_t *ptr; + mtest_results_t res; + u32_t tab; + char *s_tmp; + + res.total = res.success = res.failed = 0; + res.group_name = NULL; + title(group); + + if (tests == NULL) + { + m_warning("Could not find any registered tests in %s group.\n", group); + return res; + } + + /* Iterate over each test */ + list_for_each(tests, tmp, ptr) + { + if (strcmp(ptr->group, group) == 0) + { + res.total++; + m_info("Testing %s ...", ptr->name); + for (tab = strlen(ptr->name); tab < TITLE_LEN - 18; + tab++, printf(" ")) + ; + + if ((s_tmp = ptr->fn_test()) != TEST_SUCCESS) + { + printf(" [ \033[1;31m✕\033[0m ]\n"); + m_warning("\033[0;37m%s\033[0m\n", s_tmp); + free(s_tmp); + res.failed++; + } + else + { + printf(" [ \033[1;32m✓\033[0m ]\n"); + res.success++; + } + } + } + + print_result("Group Results", res.success, res.failed); + return res; } -/*! - * \brief Test all registered tests - * \return Numbers of tests failed - */ -u32_t test_all(void) { - mlist_t *tmp, *groups = NULL, *tests_results = NULL; - mtest_t *ptr; - mtest_results_t res, *ptr2; - u32_t total = 0, success = 0, failed = 0; - - if (tests == NULL) - { - m_warning("No tests registered, skipping.\n"); - return 0; - } - list_for_each(tests, tmp, ptr) { - if (list_get(groups, ptr->group, strlen(ptr->group)) == NULL) { - res = test_group(ptr->group); - - res.group_name = malloc(sizeof(char) * strlen(ptr->group) + 1); - strcpy(res.group_name, ptr->group); - total += res.total; - success += res.success; - failed += res.failed; - list_add(groups, ptr->group, strlen(ptr->group)); - list_add(tests_results, &res, sizeof(res)); - } - } - - title("Results"); - list_for_each(tests_results, tmp, ptr2) { - print_result(ptr2->group_name, ptr2->success, ptr2->failed); - } - print_result("Total", success, failed); - - list_free(groups, NULL); - list_free(tests_results, &single_result_free); - - return failed; +u32_t test_all(void) { + mlist_t *tmp, *groups = NULL, *tests_results = NULL; + mtest_t *ptr; + mtest_results_t res, *ptr2; + u32_t total = 0, success = 0, failed = 0; + + if (tests == NULL) + { + m_warning("No tests registered, skipping.\n"); + return 0; + } + list_for_each(tests, tmp, ptr) + { + if (list_get(groups, ptr->group, strlen(ptr->group)) == NULL) + { + res = test_group(ptr->group); + + res.group_name = malloc(sizeof(char) * strlen(ptr->group) + 1); + strcpy(res.group_name, ptr->group); + total += res.total; + success += res.success; + failed += res.failed; + list_add(groups, ptr->group, strlen(ptr->group)); + list_add(tests_results, &res, sizeof(res)); + } + } + + title("Results"); + list_for_each(tests_results, tmp, ptr2) + { + print_result(ptr2->group_name, ptr2->success, ptr2->failed); + } + print_result("Total", success, failed); + + list_free(groups, NULL); + list_free(tests_results, &single_result_free); + + return failed; } -/*! - * \brief Free all the test - */ -void test_free(void) { - list_free(tests, &single_test_free); - tests = NULL; +void test_free(void) { + list_free(tests, &single_test_free); + tests = NULL; } -/*! - * \brief Free a single test - * \note Used in test_free, as a list_free callback - */ -int single_test_free(void *ptr) { - mtest_t *tmp = ptr; - - if (ptr) { - free(tmp->group); - free(tmp->name); - } - return 1; +int single_test_free(void *ptr) { + mtest_t *tmp = ptr; + + if (ptr) + { + free(tmp->group); + free(tmp->name); + } + return 1; } -/*! - * \brief Free a mtest_results_t - * \note Used in test_all, as a list_free callback - */ -int single_result_free(void *ptr) { - mtest_results_t *tmp = ptr; - if (ptr) { - free(tmp->group_name); - } - return 1; +int single_result_free(void *ptr) { + mtest_results_t *tmp = ptr; + if (ptr) + { + free(tmp->group_name); + } + return 1; } diff --git a/src/test.c b/src/test.c @@ -1,30 +1,34 @@ /*********************************** LICENSE **********************************\ -* Copyright 2017 Morphux * -* * -* Licensed under the Apache License, Version 2.0 (the "License"); * -* you may not use this file except in compliance with the License. * -* You may obtain a copy of the License at * -* * -* http://www.apache.org/licenses/LICENSE-2.0 * -* * -* Unless required by applicable law or agreed to in writing, software * -* distributed under the License is distributed on an "AS IS" BASIS, * -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * -* See the License for the specific language governing permissions and * -* limitations under the License. * -\******************************************************************************/ + * Copyright 2017 Morphux * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + \******************************************************************************/ #ifdef COMPILE_WITH_TEST -# include <stdlib.h> -# include <sys/types.h> -# include <fcntl.h> -# include <unistd.h> +# include <string.h> +# include <stdlib.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> /* Real functions */ static void *(*real_malloc)(size_t) = &malloc; static ssize_t (*real_write)(int, const void *, size_t) = &write; static ssize_t (*real_read)(int, void *, size_t) = &read; static int (*real_close)(int) = &close; +static char *(*real_strdup)(const char *) = &strdup; +static int (*real_fstat)(int, struct stat *) = &fstat; # include <fail_test.h> @@ -32,8 +36,11 @@ static int g_malloc_fail = -1; static int g_write_fail = -1; static int g_read_fail = -1; static int g_close_fail = -1; +static int g_strdup_fail = -1; +static int g_fstat_fail = -1; void *fl_malloc(size_t alloc) { +static char *(*real_strdup)(const char *) = &(strdup); if (g_malloc_fail == -1) return real_malloc(alloc); if (g_malloc_fail == 0) { @@ -45,7 +52,7 @@ void *fl_malloc(size_t alloc) { } ssize_t fl_write(int fd, const void *ptr, size_t len) { - if (g_write_fail == -1) + if (g_write_fail == -1) return real_write(fd, ptr, len); if (g_write_fail == 0) { g_write_fail = -1; @@ -56,7 +63,7 @@ ssize_t fl_write(int fd, const void *ptr, size_t len) { } ssize_t fl_read(int fd, void *ptr, size_t len) { - if (g_read_fail == -1) + if (g_read_fail == -1) return real_read(fd, ptr, len); if (g_read_fail == 0) { g_read_fail = -1; @@ -67,7 +74,7 @@ ssize_t fl_read(int fd, void *ptr, size_t len) { } int fl_close(int fd) { - if (g_close_fail == -1) + if (g_close_fail == -1) return real_close(fd); if (g_close_fail == 0) { g_close_fail = -1; @@ -77,24 +84,57 @@ int fl_close(int fd) { return real_close(fd); } -void set_malloc_fail(int val) { +char *fl_strdup(const char *str) { + if (g_strdup_fail == -1) + return real_strdup(str); + if (g_strdup_fail == 0) { + g_strdup_fail = -1; + return NULL; + } + g_strdup_fail--; + return real_strdup(str); +} + +int fl_fstat(int fd, struct stat *buf) { + if (g_fstat_fail == -1) + return real_fstat(fd, buf); + if (g_fstat_fail == 0) + { + g_fstat_fail = -1; + return -1; + } + g_fstat_fail--; + return real_fstat(fd, buf); +} + +void set_malloc_fail(int val) { if (g_malloc_fail == -1) g_malloc_fail = val; } -void set_write_fail(int val) { +void set_write_fail(int val) { if (g_write_fail == -1) g_write_fail = val; } -void set_read_fail(int val) { +void set_read_fail(int val) { if (g_read_fail == -1) g_read_fail = val; } -void set_close_fail(int val) { +void set_close_fail(int val) { if (g_close_fail == -1) g_close_fail = val; } +void set_strdup_fail(int val) { + if (g_strdup_fail == -1) + g_strdup_fail = val; +} + +void set_fstat_fail(int val) { + if (g_fstat_fail == -1) + g_fstat_fail = val; +} + #endif /* COMPILE_WITH_TEST */ diff --git a/tests/Makefile b/tests/Makefile @@ -31,5 +31,8 @@ clean: fclean: clean rm -f $(NAME) + rm -f *.gcov + rm -f *.gcno + rm -f *.gcda re: fclean all diff --git a/tests/main.c b/tests/main.c @@ -1,18 +1,19 @@ #include "test.h" -int main(void) { - u32_t ret; +int main(void) { + u32_t ret; - /* MUST keep test_tests called first */ - register_tests_tests(); - register_infos_tests(); - register_args_tests(); - register_list_tests(); - register_print_tests(); + /* MUST keep test_tests called first */ + register_tests_tests(); + register_infos_tests(); + register_args_tests(); + register_list_tests(); + register_print_tests(); + register_files_tests(); - m_info("Beginning tests...\n"); - ret = test_all(); - test_free(); + m_info("Beginning tests...\n"); + ret = test_all(); + test_free(); - return ret; + return ret; } diff --git a/tests/test.h b/tests/test.h @@ -12,12 +12,12 @@ # define WAIT_AND_CLOSE(pid, status, fd) pid = waitpid(pid, &status, 0); close(fd[1]); # define OPT_STR_SIZE 150 # define OPT_DEF(val) { \ - {'q', "qwerty", "qwerty", val, &callback_q}, \ - {'w', "wertyu", "wertyu", val, &callback_w}, \ - {'e', "ertyui", "rtyuio", val, &callback_e}, \ - {'r', "rtyuio", "tyuiop", val, &callback_r}, \ - {'t', "tyuiop", "tyuiop", val, &callback_t}, \ - {'y', "yuiop[", "yuiop[", val, &callback_y}, \ + {.opt = 'q', .s_opt = "qwerty", .desc = "qwerty", .take_arg = val, .callback = &callback_q}, \ + {.opt = 'w', .s_opt = "wertyu", .desc = "wertyu", .take_arg = val, .callback = &callback_w}, \ + {.opt = 'e', .s_opt = "ertyui", .desc = "rtyuio", .take_arg = val, .callback = &callback_e}, \ + {.opt = 'r', .s_opt = "rtyuio", .desc = "tyuiop", .take_arg = val, .callback = &callback_r}, \ + {.opt = 't', .s_opt = "tyuiop", .desc = "tyuiop", .take_arg = val, .callback = &callback_t}, \ + {.opt = 'y', .s_opt = "yuiop[", .desc = "yuiop[", .take_arg = val, .callback = &callback_y}, \ ARGS_EOL \ } @@ -26,12 +26,13 @@ void register_args_tests(void); void register_list_tests(void); void register_tests_tests(void); void register_print_tests(void); -void callback_q(const char *s); -void callback_w(const char *s); -void callback_e(const char *s); -void callback_r(const char *s); -void callback_t(const char *s); -void callback_y(const char *s); +void register_files_tests(void); +bool callback_q(const char *s); +bool callback_w(const char *s); +bool callback_e(const char *s); +bool callback_r(const char *s); +bool callback_t(const char *s); +bool callback_y(const char *s); void reset_args(void); diff --git a/tests/test_args.c b/tests/test_args.c @@ -2,159 +2,207 @@ static struct margs_tests args; -TEST(args_NULL) { - TEST_ASSERT(read_opt(0, NULL, NULL) == 0, "Not handling null arguments"); - return TEST_SUCCESS; -} - -TEST(args_NULL_1) { - TEST_ASSERT(read_opt(10, NULL, NULL) == 0, "Not handling null arguments"); - return TEST_SUCCESS; -} - -TEST(args_NULL_2) { - char *tab[] = {"test", "test2"}; - TEST_ASSERT(read_opt(0, tab, NULL) == 0, "Not handling null arguments"); - return TEST_SUCCESS; -} - -TEST(args_NULL_3) { - char *tab[] = {"test", "test2"}; - TEST_ASSERT(read_opt(10, tab, NULL) == 0, "Not handling null arguments"); - return TEST_SUCCESS; -} - -TEST(args_NULL_4) { +/* Testing all possibilities with a NULL args somewhere */ +TEST(opts_NULL) { char *tab[] = {"test", "test2"}; - margs_t t; - - TEST_ASSERT(read_opt(0, tab, &t) == 0, "Not handling null arguments"); - return TEST_SUCCESS; -} - -TEST(args_NULL_5) { - margs_t t; + mopts_t t; + mlist_t *lst = NULL; + + /* 0 0 0 0 */ + TEST_ASSERT(read_opt(0, NULL, NULL, NULL) == 0, + "Not handling null arguments"); + /* 0 0 0 1 */ + TEST_ASSERT(read_opt(0, NULL, NULL, &lst) == 0, + "Not handling null arguments"); + /* 0 0 1 0 */ + TEST_ASSERT(read_opt(0, NULL, &t, NULL) == 0, + "Not handling null arguments"); + /* 0 0 1 1 */ + TEST_ASSERT(read_opt(0, NULL, &t, &lst) == 0, + "Not handling null arguments"); + /* 0 1 0 0 */ + TEST_ASSERT(read_opt(0, tab, NULL, NULL) == 0, + "Not handling null arguments"); + /* 0 1 0 1 */ + TEST_ASSERT(read_opt(0, tab, NULL, &lst) == 0, + "Not handling null arguments"); + /* 0 1 1 0 */ + TEST_ASSERT(read_opt(0, tab, &t, NULL) == 0, + "Not handling null arguments"); + /* 0 1 1 1 */ + TEST_ASSERT(read_opt(0, tab, &t, &lst) == 0, + "Not handling null arguments"); + /* 1 0 0 0 */ + TEST_ASSERT(read_opt(10, NULL, NULL, NULL) == 0, + "Not handling null arguments"); + /* 1 0 0 1 */ + TEST_ASSERT(read_opt(10, NULL, NULL, &lst) == 0, + "Not handling null arguments"); + /* 1 0 1 0 */ + TEST_ASSERT(read_opt(10, NULL, &t, NULL) == 0, + "Not handling null arguments"); + /* 1 0 1 1 */ + TEST_ASSERT(read_opt(10, NULL, &t, &lst) == 0, + "Not handling null arguments"); + /* 1 1 0 0 */ + TEST_ASSERT(read_opt(10, tab, NULL, NULL) == 0, + "Not handling null arguments"); + /* 1 1 0 1 */ + TEST_ASSERT(read_opt(10, tab, NULL, &lst) == 0, + "Not handling null arguments"); + /* 1 1 1 0 */ + TEST_ASSERT(read_opt(10, tab, &t, NULL) == 0, + "Not handling null arguments"); + + return TEST_SUCCESS; +} + +/* Testing with av being an array full of empty strings */ +TEST(opts_empty_1) { + mopts_t t; + char *tab[] = {"", "", ""}; + mlist_t *lst = NULL;; - TEST_ASSERT(read_opt(10, NULL, &t) == 0, "Not handling null arguments"); + TEST_ASSERT(read_opt(3, tab, &t, &lst) == 0, "Not handling null arguments"); return TEST_SUCCESS; } -TEST(args_empty_1) { - margs_t t; - char *tab[] = {"", "", ""}; +/* Testing with av being an array full of NULLs */ +TEST(opts_empty_2) { + mopts_t t; + char *tab[] = {NULL, NULL, NULL}; + mlist_t *lst = NULL;; - TEST_ASSERT(read_opt(3, tab, &t) == 0, "Not handling null arguments"); + TEST_ASSERT(read_opt(3, tab, &t, &lst) == 0, "Not handling null arguments"); return TEST_SUCCESS; } -TEST(args_empty_2) { - margs_t t; - char *tab[] = {NULL, NULL, NULL}; +/* Testing single dash alone */ +TEST(dashes_alone_1) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, + ARGS_EOL + }; + char *av[] = {"./test", "-"}; + mlist_t *lst = NULL; - TEST_ASSERT(read_opt(3, tab, &t) == 0, "Not handling null arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling unknown option"); + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(!strcmp(lst->member, "-"), "Parameter not read"); + TEST_ASSERT(lst->size == strlen(av[1]) + 1, "Wrong size allocated"); + list_free(lst, NULL); return TEST_SUCCESS; } - -TEST(args_unhandled_1) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing double dash alone */ +TEST(dashes_alone_2) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./tests", "oui", "--allow"}; - pid_t pid; - int st, fd[2]; - - pipe(fd); - fflush(stdout); - if ((pid = fork()) == 0) { + char *av[] = {"./test", "--"}; + mlist_t *lst = NULL; - DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av), av, opt) == 0, "Not handling properly unknown arguments"); - } else { - WAIT_AND_CLOSE(pid, st, fd); - TEST_ASSERT((WEXITSTATUS(st) == 1), "Wrong return"); - } + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling unknown option"); + TEST_ASSERT(!lst, "Unwanted list created"); return TEST_SUCCESS; } -TEST(args_unhandled_2) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, + +/* Testing with unknown single dash option */ +TEST(opts_unhandled_1) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "---wrong-option"}; + char *av[] = {"./test", "-q"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling triple '-' arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling unknown option"); exit(0); } else { WAIT_AND_CLOSE(pid, st, fd); - TEST_ASSERT((WEXITSTATUS(st) == 0), "Wrong return"); + TEST_ASSERT((WEXITSTATUS(st) == 1), "Wrong return"); } return TEST_SUCCESS; } -TEST(args_unhandled_3) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing with unknown double dash option */ +TEST(opts_unhandled_2) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "-", "<-", "Single dash"}; - int st, fd[2]; + char *av[] = {"./tests", "--oui"}; pid_t pid; + int st, fd[2]; + mlist_t *lst = NULL;; pipe(fd); + fflush(stdout); if ((pid = fork()) == 0) { + DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling alone '-' in arguments"); - exit(0); + TEST_ASSERT(read_opt(sizeof(av), av, opt, &lst) == 0, + "Not handling properly unknown arguments"); } else { WAIT_AND_CLOSE(pid, st, fd); - TEST_ASSERT((WEXITSTATUS(st) == 0), "Wrong return"); + TEST_ASSERT((WEXITSTATUS(st) == 1), "Wrong return"); } return TEST_SUCCESS; } -TEST(args_unhandled_4) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing with triple dash */ +TEST(opts_unhandled_3) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "-q"}; + char *av[] = {"./test", "---wrong-option"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL;; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling unknown option"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling triple '-' arguments"); exit(0); } else { WAIT_AND_CLOSE(pid, st, fd); - TEST_ASSERT((WEXITSTATUS(st) == 1), "Wrong return"); + TEST_ASSERT((WEXITSTATUS(st) == 0), "Wrong return"); } return TEST_SUCCESS; } -TEST(args_unhandled_5) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing a call to -h builtin option */ +TEST(opts_help_1) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, + {.opt = 'w', .desc = "No idea.", .take_arg = false, .callback = NULL}, + {.s_opt = "we", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "--"}; + char *av[] = {"./test", "-h"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling double dash without option"); - exit(0); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling -h arguments"); + exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); TEST_ASSERT((WEXITSTATUS(st) == 0), "Wrong return"); @@ -162,40 +210,46 @@ TEST(args_unhandled_5) { return TEST_SUCCESS; } -TEST(args_unhandled_6) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing a call to --help builtin option */ +TEST(opts_help_2) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "-"}; + char *av[] = {"./test", "--help"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling single dash without option"); - exit(1); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling --help arguments"); + exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); - TEST_ASSERT((WEXITSTATUS(st) == 1), "Wrong return"); + TEST_ASSERT((WEXITSTATUS(st) == 0), "Wrong return"); } return TEST_SUCCESS; } -TEST(args_help_1) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing that --help properly exits instantly */ +TEST(opts_help_3) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "-h"}; + char *av[] = {"./test", "--help", "--zoiberg"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling -h arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling --help arguments"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -204,19 +258,22 @@ TEST(args_help_1) { return TEST_SUCCESS; } -TEST(args_help_2) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing a call to -V builtin option */ +TEST(opts_version_1) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "--help"}; + char *av[] = {"./test", "-V"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling --help arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling --version arguments"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -225,19 +282,22 @@ TEST(args_help_2) { return TEST_SUCCESS; } -TEST(args_version_1) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing a call to --version builtin option */ +TEST(opts_version_2) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "-V"}; + char *av[] = {"./test", "--version"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling --version arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling -v arguments"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -246,19 +306,22 @@ TEST(args_version_1) { return TEST_SUCCESS; } -TEST(args_version_2) { - margs_t opt[] = { - {'z', "zoiberg", "No idea.", false, NULL}, +/* Testing that --version properly exit instantly */ +TEST(opts_version_3) { + mopts_t opt[] = { + {.opt = 'z', .s_opt = "zoiberg", .desc = "No idea.", .take_arg = false, .callback = NULL}, ARGS_EOL }; - char *av[] = {"./test", "--version"}; + char *av[] = {"./test", "--version", "--zoiberg"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling -v arguments"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling -v arguments"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -267,71 +330,89 @@ TEST(args_version_2) { return TEST_SUCCESS; } -TEST(args_base_1) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading of 1 option without parameters */ +TEST(opts_base_1) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "-q"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 1), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w != true, "Argument not read"); return TEST_SUCCESS; } -TEST(args_base_2) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading of 2 packed options without parameters */ +TEST(opts_base_2) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "-qw"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e != true, "Argument not read"); return TEST_SUCCESS; } -TEST(args_base_3) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading of 2 separated options without parameters */ +TEST(opts_base_3) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "-q", "-w"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e != true, "Argument not read"); return TEST_SUCCESS; } -TEST(args_base_4) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading of 1 long and 1 short options without parameters */ +TEST(opts_base_4) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "--qwerty", "-w"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e != true, "Argument not read"); return TEST_SUCCESS; } -TEST(args_base_5) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading of 2 long options without parameters */ +TEST(opts_base_5) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "--qwerty", "--wertyu"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e != true, "Argument not read"); return TEST_SUCCESS; } -TEST(args_base_6) { - margs_t opt[] = OPT_DEF(false); +/* Testing reading 6 packed options that makes the name of a long option */ +TEST(opts_base_6) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "-qwerty"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 6), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 6), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e == true, "Argument not read"); @@ -341,12 +422,21 @@ TEST(args_base_6) { return TEST_SUCCESS; } -TEST(args_base_7) { - margs_t opt[] = OPT_DEF(false); - char *av[] = {"./test", "--qwerty", "--wertyu", "--ertyui", "--rtyuio", "--tyuiop", "--yuiop["}; +/* Testing reading of 6 long options without parameters */ +TEST(opts_base_7) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", + "--qwerty", + "--wertyu", + "--ertyui", + "--rtyuio", + "--tyuiop", + "--yuiop["}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 6), "Wrong return"); + TEST_ASSERT((read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 6), + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); TEST_ASSERT(args.opt_e == true, "Argument not read"); @@ -356,17 +446,20 @@ TEST(args_base_7) { return TEST_SUCCESS; } -TEST(args_missing_value_1) { - margs_t opt[] = OPT_DEF(true); +/* Testing missing argument in short options-argument pair */ +TEST(opts_missing_value_1) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "-q"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; reset_args(); pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling missing argument"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling missing argument"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -375,17 +468,20 @@ TEST(args_missing_value_1) { return TEST_SUCCESS; } -TEST(args_missing_value_2) { - margs_t opt[] = OPT_DEF(true); +/* Testing missing argument in long options-argument pair */ +TEST(opts_missing_value_2) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "--qwerty"}; int st, fd[2]; pid_t pid; + mlist_t *lst = NULL; reset_args(); pipe(fd); if ((pid = fork()) == 0) { DUP_ALL_OUTPUTS(fd); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 0, "Not handling missing argument"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Not handling missing argument"); exit(5); } else { WAIT_AND_CLOSE(pid, st, fd); @@ -394,34 +490,43 @@ TEST(args_missing_value_2) { return TEST_SUCCESS; } -TEST(args_value_1) { - margs_t opt[] = OPT_DEF(true); +/* Testing good reading of short options-argument pair */ +TEST(opts_value_1) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "-q", "toto"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 1, "Wrong return"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(strcmp(args.str_q, "toto") == 0, "Value not read"); return TEST_SUCCESS; } -TEST(args_value_2) { - margs_t opt[] = OPT_DEF(true); +/* Testing good reading of long options-argument pair */ +TEST(opts_value_2) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "--qwerty=toto"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 1, "Wrong return"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(strcmp(args.str_q, "toto") == 0, "Value not read"); return TEST_SUCCESS; } -TEST(args_value_3) { - margs_t opt[] = OPT_DEF(true); +/* Mixing both tests above */ +TEST(opts_value_3) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "--qwerty=toto", "-w", "tata"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2, "Wrong return"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2, + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(strcmp(args.str_q, "toto") == 0, "Value not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); @@ -429,12 +534,15 @@ TEST(args_value_3) { return TEST_SUCCESS; } -TEST(args_value_4) { - margs_t opt[] = OPT_DEF(true); +/* Testing good reading of multiple long options-argument pair */ +TEST(opts_value_4) { + mopts_t opt[] = OPT_DEF(true); char *av[] = {"./test", "--qwerty=toto", "--wertyu=tata"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 2, "Wrong return"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 2, + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(strcmp(args.str_q, "toto") == 0, "Value not read"); TEST_ASSERT(args.opt_w == true, "Argument not read"); @@ -442,67 +550,329 @@ TEST(args_value_4) { return TEST_SUCCESS; } -TEST(args_word_only_1) { - margs_t opt[] = { - {0, "qwerty", "qwerty", false, &callback_q}, - ARGS_EOL - }; +/* Testing existence of long-only options */ +TEST(opts_long_only_1) { + mopts_t opt[] = OPT_DEF(false); char *av[] = {"./test", "--qwerty"}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); + TEST_ASSERT(args.opt_q == true, "Argument not read"); + TEST_ASSERT(args.opt_w == false, "Wrong argument"); + return TEST_SUCCESS; +} + +/* Testing existence of short-only options */ +TEST(opts_short_only_1) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", "-q"}; + mlist_t *lst = NULL; reset_args(); - TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt) == 1, "Wrong return"); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); TEST_ASSERT(args.opt_q == true, "Argument not read"); TEST_ASSERT(args.opt_w == false, "Wrong argument"); return TEST_SUCCESS; } +/* Testing reading of a params alone */ +TEST(params_reading_1) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", "hey"}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Wrong return"); + TEST_ASSERT(args.opt_q == false, "Wrong argument"); + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(lst->size == strlen(av[1]) + 1, "Parameter not read"); + TEST_ASSERT(!strcmp(lst->member, "hey"), "Parameter not read"); + list_free(lst, NULL); + return TEST_SUCCESS; +} + +/* Testing reading of a params then a short option */ +TEST(params_reading_2) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", "hey", "-q"}; + mlist_t *lst = NULL; -void callback_q(const char *s) { + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); + TEST_ASSERT(args.opt_q == true, "Didn't parsed option"); + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(!strcmp(lst->member, "hey"), "Parameter not read"); + TEST_ASSERT(lst->size == strlen(av[1]) + 1, "Wrong size allocated"); + TEST_ASSERT(!lst->next, "Unwanted node created"); + list_free(lst, NULL); + return TEST_SUCCESS; +} + +/* Testing reading of a params then a long option */ +TEST(params_reading_3) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", "hey", "--qwerty"}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 1, + "Wrong return"); + TEST_ASSERT(args.opt_q == true, "Didn't parsed option"); + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(!strcmp(lst->member, "hey"), "Parameter not read"); + TEST_ASSERT(lst->size == strlen(av[1]) + 1, "Wrong size allocated"); + TEST_ASSERT(!lst->next, "Unwanted node created"); + list_free(lst, NULL); + return TEST_SUCCESS; +} + +/* Testing with empty param */ +TEST(empty_param_1) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", ""}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Wrong return"); + TEST_ASSERT(args.opt_q == false, "Wrong argument"); + TEST_ASSERT(!lst, "List created with empty param"); + return TEST_SUCCESS; +} + +/* Testing with null param */ +TEST(empty_param_2) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", NULL}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Wrong return"); + TEST_ASSERT(args.opt_q == false, "Wrong argument"); + TEST_ASSERT(!lst, "List created with null param"); + return TEST_SUCCESS; +} + +/* Testing with empty param followed by a param */ +TEST(empty_param_3) { + mopts_t opt[] = { + {.opt = 'q', .s_opt = NULL, .desc = "qwerty", .take_arg = false, .callback = &callback_q}, + ARGS_EOL + }; + char *av[] = {"./test", "", "hey"}; + mlist_t *lst = NULL; + + reset_args(); + TEST_ASSERT(read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst) == 0, + "Wrong return"); + TEST_ASSERT(args.opt_q == false, "Wrong argument"); + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(!strcmp(lst->member, "hey"), "Parameter not read"); + TEST_ASSERT(lst->size == strlen(av[2]) + 1, "Wrong size allocated"); + list_free(lst, NULL); + return TEST_SUCCESS; +} + +/* Testing reading of parameters, options, and empty cases in av */ +TEST(mixed_1) { + mopts_t opt[] = OPT_DEF(false); + char *av[] = {"./test", "hey", "--qwerty", "-q", "-w", + "ho", "ah", "--ertyui", "", "-", "--", "-r"}; + mlist_t *lst = NULL; + int i = 0; + + reset_args(); + TEST_ASSERT((i = read_opt(sizeof(av) / sizeof(av[0]), av, opt, &lst)) == 4, + "Wrong return"); + TEST_ASSERT(args.opt_q == true, "Didn't parsed option"); + TEST_ASSERT(args.opt_w == true, "Didn't parsed option"); + TEST_ASSERT(args.opt_e == true, "Didn't parsed option"); + TEST_ASSERT(args.opt_r != true, "Didn't parsed option"); + /* First param : "hey" */ + TEST_ASSERT(lst, "List not created"); + TEST_ASSERT(!strcmp(lst->member, "hey"), "Parameter not read"); + TEST_ASSERT(lst->size == strlen(av[1]) + 1, "Wrong size allocated"); + /* Second param : "ho" */ + TEST_ASSERT(lst->next, "Node not created"); + TEST_ASSERT(!strcmp(lst->next->member, "ho"), "Parameter not read"); + TEST_ASSERT(lst->next->size == strlen(av[5]) + 1, "Wrong size allocated"); + /* Third param : "ah" */ + TEST_ASSERT(lst->next->next, "Node not created"); + TEST_ASSERT(!strcmp(lst->next->next->member, "ah"), + "Parameter not read"); + TEST_ASSERT(lst->next->next->size == strlen(av[6]) + 1, + "Wrong size allocated"); + /* Fourth param : "-" */ + TEST_ASSERT(lst->next->next->next, "Node not created"); + TEST_ASSERT(!strcmp(lst->next->next->next->member, "-"), + "Parameter not read"); + TEST_ASSERT(lst->next->next->next->size == strlen(av[9]) + 1, + "Wrong size allocated"); + /* Fifth param : "-r" */ + TEST_ASSERT(lst->next->next->next->next, "Node not created"); + TEST_ASSERT(!strcmp(lst->next->next->next->next->member, "-r"), + "Parameter not read"); + TEST_ASSERT(lst->next->next->next->next->size == strlen(av[11]) + 1, + "Wrong size allocated"); + TEST_ASSERT(!lst->next->next->next->next->next, "Unwanted node created"); + list_free(lst, NULL); + return TEST_SUCCESS; +} + +TEST(usage) { + mopts_t opts[] = { + { + .opt = 'q' + }, + { + .s_opt = "test" + }, + { + .opt = 'w', + .usage = "TEST", + .take_arg = true + }, + { + .s_opt = "rerer", + .usage = "wefwef", + .take_arg = true + }, + ARGS_EOL + }; + int st, fd[2]; + pid_t pid; + + set_program_name("test"); + reset_args(); + pipe(fd); + if ((pid = fork()) == 0) { + DUP_ALL_OUTPUTS(fd); + usage(opts); + } else { + WAIT_AND_CLOSE(pid, st, fd); + TEST_ASSERT(WEXITSTATUS(st) == 0, "Wrong return"); + } + return TEST_SUCCESS; +} + +static bool args_callback_wrong_value(const char *s) { + (void)s; + return false; +} + +TEST(wrong_value_1) { + mopts_t opts[] = { + { + .opt = 'q', + .take_arg = true, + .callback = &args_callback_wrong_value + }, + ARGS_EOL + }; + char *av[] = {"./test", "-q", "nocare"}; + int st, fd[2]; + pid_t pid; + mlist_t *lst = NULL; + + reset_args(); + pipe(fd); + if ((pid = fork()) == 0) { + DUP_ALL_OUTPUTS(fd); + read_opt(sizeof(av) / sizeof(av[0]), av, opts, &lst); + exit(0); + } else { + WAIT_AND_CLOSE(pid, st, fd); + TEST_ASSERT(WEXITSTATUS(st) == 1, "Wrong return"); + } + return TEST_SUCCESS; +} + +TEST(wrong_value_2) { + mopts_t opts[] = { + { + .s_opt = "qwert", + .take_arg = true, + .callback = &args_callback_wrong_value + }, + ARGS_EOL + }; + char *av[] = {"./test", "--qwert=nocare"}; + int st, fd[2]; + pid_t pid; + mlist_t *lst = NULL; + + reset_args(); + pipe(fd); + if ((pid = fork()) == 0) { + DUP_ALL_OUTPUTS(fd); + read_opt(sizeof(av) / sizeof(av[0]), av, opts, &lst); + exit(0); + } else { + WAIT_AND_CLOSE(pid, st, fd); + TEST_ASSERT(WEXITSTATUS(st) == 1, "Wrong return"); + } + return TEST_SUCCESS; +} + + +bool callback_q(const char *s) { args.opt_q = true; if (s == NULL) strcpy(args.str_q, ""); else strcpy(args.str_q, s); + return true; } -void callback_w(const char *s) { +bool callback_w(const char *s) { args.opt_w = true; if (s == NULL) strcpy(args.str_w, ""); else strcpy(args.str_w, s); + return true; } -void callback_e(const char *s) { +bool callback_e(const char *s) { args.opt_e = true; if (s == NULL) strcpy(args.str_e, ""); else strcpy(args.str_e, s); + return true; } -void callback_r(const char *s) { +bool callback_r(const char *s) { args.opt_r = true; if (s == NULL) strcpy(args.str_r, ""); else strcpy(args.str_r, s); + return true; } -void callback_t(const char *s) { +bool callback_t(const char *s) { args.opt_t = true; if (s == NULL) strcpy(args.str_t, ""); else strcpy(args.str_t, s); + return true; } -void callback_y(const char *s) { +bool callback_y(const char *s) { args.opt_y = true; if (s == NULL) strcpy(args.str_y, ""); else strcpy(args.str_y, s); + return true; } void reset_args(void) { @@ -521,36 +891,79 @@ void reset_args(void) { } void register_args_tests(void) { - reg_test("m_args", args_NULL); - reg_test("m_args", args_NULL_1); - reg_test("m_args", args_NULL_2); - reg_test("m_args", args_NULL_3); - reg_test("m_args", args_NULL_4); - reg_test("m_args", args_NULL_5); - reg_test("m_args", args_empty_1); - reg_test("m_args", args_empty_2); - reg_test("m_args", args_unhandled_1); - reg_test("m_args", args_unhandled_2); - reg_test("m_args", args_unhandled_3); - reg_test("m_args", args_unhandled_4); - reg_test("m_args", args_unhandled_5); - reg_test("m_args", args_unhandled_6); - reg_test("m_args", args_help_1); - reg_test("m_args", args_help_2); - reg_test("m_args", args_version_1); - reg_test("m_args", args_version_2); - reg_test("m_args", args_base_1); - reg_test("m_args", args_base_2); - reg_test("m_args", args_base_3); - reg_test("m_args", args_base_4); - reg_test("m_args", args_base_5); - reg_test("m_args", args_base_6); - reg_test("m_args", args_base_7); - reg_test("m_args", args_missing_value_1); - reg_test("m_args", args_missing_value_2); - reg_test("m_args", args_value_1); - reg_test("m_args", args_value_2); - reg_test("m_args", args_value_3); - reg_test("m_args", args_value_4); - reg_test("m_args", args_word_only_1); +/* Testing all possibilities with a NULL args somewhere */ + reg_test("m_args", opts_NULL); +/* Testing with av being an array full of empty strings */ + reg_test("m_args", opts_empty_1); +/* Testing with av being an array full of NULLs */ + reg_test("m_args", opts_empty_2); +/* Testing double dash alone */ + reg_test("m_args", dashes_alone_1); +/* Testing double dash alone */ + reg_test("m_args", dashes_alone_1); +/* Testing with unknown single dash option */ + reg_test("m_args", opts_unhandled_1); +/* Testing with unknown double dash option */ + reg_test("m_args", opts_unhandled_2); +/* Testing with triple dash */ + reg_test("m_args", opts_unhandled_3); +/* Testing a call to -h builtin option */ + reg_test("m_args", opts_help_1); +/* Testing a call to --help builtin option */ + reg_test("m_args", opts_help_2); +/* Testing that --help properly exits instantly */ + reg_test("m_args", opts_help_3); +/* Testing a call to -V builtin option */ + reg_test("m_args", opts_version_1); +/* Testing a call to --version builtin option */ + reg_test("m_args", opts_version_2); +/* Testing that --version properly exit instantly */ + reg_test("m_args", opts_version_3); +/* Testing reading of 1 option without parameters */ + reg_test("m_args", opts_base_1); +/* Testing reading of 2 packed options without parameters */ + reg_test("m_args", opts_base_2); +/* Testing reading of 2 separated options without parameters */ + reg_test("m_args", opts_base_3); +/* Testing reading of 1 long and 1 short options without parameters */ + reg_test("m_args", opts_base_4); +/* Testing reading of 2 long options without parameters */ + reg_test("m_args", opts_base_5); +/* Testing reading 6 packed options that makes the name of a long option */ + reg_test("m_args", opts_base_6); +/* Testing reading of 6 long options without parameters */ + reg_test("m_args", opts_base_7); +/* Testing missing argument in short options-argument pair */ + reg_test("m_args", opts_missing_value_1); +/* Testing missing argument in long options-argument pair */ + reg_test("m_args", opts_missing_value_2); +/* Testing good reading of short options-argument pair */ + reg_test("m_args", opts_value_1); +/* Testing good reading of long options-argument pair */ + reg_test("m_args", opts_value_2); +/* Mixing both tests above */ + reg_test("m_args", opts_value_3); +/* Testing good reading of multiple long options-argument pair */ + reg_test("m_args", opts_value_4); +/* Testing existence of long-only options */ + reg_test("m_args", opts_long_only_1); +/* Testing existence of short-only options */ + reg_test("m_args", opts_short_only_1); +/* Testing reading of a params alone */ + reg_test("m_args", params_reading_1); +/* Testing reading of a params then a short option */ + reg_test("m_args", params_reading_2); +/* Testing reading of a params then a long option */ + reg_test("m_args", params_reading_3); +/* Testing with empty param */ + reg_test("m_args", empty_param_1); +/* Testing with null param */ + reg_test("m_args", empty_param_2); +/* Testing with empty param followed by a param */ + reg_test("m_args", empty_param_3); +/* Testing reading of parameters, options, and empty cases in av */ + reg_test("m_args", mixed_1); + reg_test("m_args", usage); + reg_test("m_args", wrong_value_1); + reg_test("m_args", wrong_value_2); } diff --git a/tests/test_files.c b/tests/test_files.c @@ -0,0 +1,114 @@ +#include "test.h" + +TEST(file_get_file_size_from_fd_1) { + TEST_ASSERT(mpm_get_file_size_from_fd(-1) == 0, "Did not return 0"); + return TEST_SUCCESS; +} + +TEST(file_get_file_size_from_fd_2) { + int fd = open("./Makefile", O_RDONLY); + + set_fstat_fail(0); + TEST_ASSERT(mpm_get_file_size_from_fd(fd) == 0, "Wrong return"); + set_fstat_fail(-1); + close(fd); + return TEST_SUCCESS; +} + +TEST(file_get_file_size_from_fd_3) { + int fd = open("./Makefile", O_RDONLY); + TEST_ASSERT(mpm_get_file_size_from_fd(fd) != 0, "Wrong return"); + close(fd); + return TEST_SUCCESS; +} + +TEST(file_get_file_size_from_fn_1) { + TEST_ASSERT(mpm_get_file_size_from_fn(NULL) == 0, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_get_file_size_from_fn_2) { + TEST_ASSERT(mpm_get_file_size_from_fn("/non/sense/path") == 0, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_get_file_size_from_fn_3) { + TEST_ASSERT(mpm_get_file_size_from_fn("./Makefile") != 0, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fd_1) { + TEST_ASSERT(mpm_read_file_from_fd(-1) == NULL, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fd_2) { + TEST_ASSERT(mpm_read_file_from_fd(2) == NULL, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fd_3) { + int fd = open("./Makefile", O_RDONLY); + + set_malloc_fail(0); + TEST_ASSERT(mpm_read_file_from_fd(fd) == NULL, "Wrong return"); + close(fd); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fd_4) { + int fd = open("./Makefile", O_RDONLY); + + set_read_fail(0); + TEST_ASSERT(mpm_read_file_from_fd(fd) == NULL, "Wrong return"); + close(fd); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fd_5) { + int fd = open("./Makefile", O_RDONLY); + char *ret; + + ret = mpm_read_file_from_fd(fd); + TEST_ASSERT(ret != NULL, "Wrong return"); + close(fd); + free(ret); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fn_1) { + TEST_ASSERT(mpm_read_file_from_fn(NULL) == NULL, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fn_2) { + TEST_ASSERT(mpm_read_file_from_fn("/non/sense/path") == NULL, "Wrong return"); + return TEST_SUCCESS; +} + +TEST(file_read_file_from_fn_3) { + char *ret; + + ret = mpm_read_file_from_fn("./Makefile"); + TEST_ASSERT(ret != NULL, "Wrong return"); + free(ret); + return TEST_SUCCESS; +} + + +void register_files_tests(void) { + reg_test("m_file", file_get_file_size_from_fd_1); + reg_test("m_file", file_get_file_size_from_fd_2); + reg_test("m_file", file_get_file_size_from_fd_3); + reg_test("m_file", file_get_file_size_from_fn_1); + reg_test("m_file", file_get_file_size_from_fn_2); + reg_test("m_file", file_get_file_size_from_fn_3); + reg_test("m_file", file_read_file_from_fd_1); + reg_test("m_file", file_read_file_from_fd_2); + reg_test("m_file", file_read_file_from_fd_3); + reg_test("m_file", file_read_file_from_fd_4); + reg_test("m_file", file_read_file_from_fd_5); + reg_test("m_file", file_read_file_from_fn_1); + reg_test("m_file", file_read_file_from_fn_2); + reg_test("m_file", file_read_file_from_fn_3); +} diff --git a/tests/test_print.c b/tests/test_print.c @@ -182,7 +182,10 @@ TEST(print_log_mpanic) { return TEST_SUCCESS; } - +TEST(print_log_cleanup) { + unlink(TEST_LOG_FN); + return TEST_SUCCESS; +} void register_print_tests(void) { reg_test("mprint", print_info); @@ -201,4 +204,5 @@ void register_print_tests(void) { reg_test("mprint", print_log_mwarning); reg_test("mprint", print_log_merror); reg_test("mprint", print_log_mpanic); + reg_test("mprint", print_log_cleanup); } diff --git a/tests/test_tests.c b/tests/test_tests.c @@ -73,9 +73,103 @@ TEST(test_group_fail) { return TEST_SUCCESS; } +TEST(test_fail_malloc) { + set_malloc_fail(1); + + char *ret = malloc(10); + TEST_ASSERT(ret != NULL, "Malloc should have succeed (Or we're very unlucky)"); + free(ret); + ret = malloc(10); + TEST_ASSERT(ret == NULL, "Malloc should have failed."); + return TEST_SUCCESS; +} + +# define TMP_FD_FN "/tmp/test_fake_functions" + +TEST(test_fail_write) { + int fd = open(TMP_FD_FN, O_CREAT | O_WRONLY, 0655); + + set_write_fail(1); + TEST_ASSERT(fd != -1, "Cannot open test file"); + TEST_ASSERT(write(fd, "0", 1) != -1, "Write should have succeed (Or we're very unlucky)"); + TEST_ASSERT(write(fd, "0", 1) == -1, "Write should have failed."); + close(fd); + return TEST_SUCCESS; +} + +TEST(test_fail_read) { + int fd = open(TMP_FD_FN, O_CREAT | O_RDONLY, 0655); + char buf[10]; + set_read_fail(1); + + TEST_ASSERT(fd != -1, "Cannot open test file"); + TEST_ASSERT(read(fd, buf, 10) != -1, "Read should have succeed (Or we're very unlucky)"); + TEST_ASSERT(read(fd, buf, 10) == -1, "Read should have failed."); + close(fd); + return TEST_SUCCESS; +} + +TEST(test_fail_close) { + int fd = open(TMP_FD_FN, O_CREAT | O_RDONLY, 0655); + set_close_fail(1); + + TEST_ASSERT(fd != -1, "Cannot open test file"); + TEST_ASSERT(close(fd) != -1, "Close should have succeed (Or we're very unlucky)"); + fd = open(TMP_FD_FN, O_CREAT | O_RDONLY, 0655); + TEST_ASSERT(fd != -1, "Cannot open test file"); + TEST_ASSERT(close(fd) == -1, "Read should have failed."); + /* Actually close the file */ + close(fd); + return TEST_SUCCESS; +} + +TEST(test_fail_strdup) { + char *ptr; + set_strdup_fail(1); + + ptr = strdup("Test"); + TEST_ASSERT(ptr != NULL, "Strdup failed (Or we're very unlucky)"); + free(ptr); + ptr = strdup("Test"); + TEST_ASSERT(ptr == NULL, "Function should have not succeed"); + ptr = strdup("Test"); + TEST_ASSERT(ptr != NULL, "Strdup failed (Or we're very unlucky)"); + free(ptr); + return TEST_SUCCESS; +} + +TEST(test_fail_fstat) { + int fd = open("./Makefile", O_RDONLY); + struct stat buf; + int ret; + + set_fstat_fail(1); + ret = fstat(fd, &buf); + TEST_ASSERT(ret != -1, "fstat failed"); + ret = fstat(fd, &buf); + TEST_ASSERT(ret == -1, "fstat succeed"); + ret = fstat(fd, &buf); + TEST_ASSERT(ret != -1, "fstat failed"); + close(fd); + return TEST_SUCCESS; +} + +TEST(test_fail_cleanup) { + unlink(TMP_FD_FN); + return TEST_SUCCESS; +} + + void register_tests_tests(void) { test_test_all_empty(); test_test_group_unknown(); test_test_result(); test_test_group_fail(); + reg_test("fake_functions", test_fail_malloc); + reg_test("fake_functions", test_fail_write); + reg_test("fake_functions", test_fail_read); + reg_test("fake_functions", test_fail_close); + reg_test("fake_functions", test_fail_strdup); + reg_test("fake_functions", test_fail_fstat); + reg_test("fake_functions", test_fail_cleanup); }