diff --git a/check_flemme.sh b/check_flemme.sh index 12398ae..0543001 100755 --- a/check_flemme.sh +++ b/check_flemme.sh @@ -32,7 +32,7 @@ run_cmd "Running autoreconf" autoreconf --force --verbose --install if [[ "$(uname)" == "Darwin" ]]; then run_cmd "Configuring for MacOS" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib' else - run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g' + run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address' fi run_cmd "Cleaning build" make clean diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 60ec0c3..9a72c10 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -137,10 +137,7 @@ static int exec_assignment(struct list *assignment_list, struct hash_map *vars) struct ast_assignment *assignment = ast_get_assignment(assignment_list->data); - char *key = strdup(assignment->name); - char *value = strdup(assignment->value); - hash_map_insert(vars, key, value, NULL); - + set_var_copy(vars, assignment->name, assignment->value); assignment_list = assignment_list->next; } return 0; @@ -148,7 +145,6 @@ static int exec_assignment(struct list *assignment_list, struct hash_map *vars) int exec_ast_command(struct ast_command *command, struct hash_map *vars) { - (void)vars; exec_assignment(command->assignments, vars); set_all_redir(command->redirections); diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index 622c9fe..ebf6858 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -10,5 +10,4 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars); int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars); void unset_all_redir(struct list *redir_list); - #endif // EXECUTION_HELPERS_H diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c index 3029394..02f0a3f 100644 --- a/src/lexer/lexer_utils.c +++ b/src/lexer/lexer_utils.c @@ -72,10 +72,8 @@ static void set_token_keyword(struct token *tok, char *begin, ssize_t size) tok->type = TOKEN_ELSE; else if (strncmp(begin, "elif", size) == 0 && size == 4) tok->type = TOKEN_ELIF; - else if (strncmp(begin, "&&", size) == 0 && size == 2) - tok->type = TOKEN_AND; - else if (strncmp(begin, "||", size) == 0 && size == 2) - tok->type = TOKEN_OR; + else if (strncmp(begin, "export", size) == 0 && size == 6) + tok->type = TOKEN_EXPORT; // no keywords found. if (tok->type == TOKEN_NULL) @@ -97,35 +95,44 @@ static void set_token_operator(struct token *tok, char *begin, ssize_t size) { if (tok->type != TOKEN_NULL) return; - if (strncmp(begin, ">", size) == 0) + if (strncmp(begin, "&&", size) == 0 && size == 2) + { + tok->type = TOKEN_AND; + } + else if (strncmp(begin, "||", size) == 0 && size == 2) + { + tok->type = TOKEN_OR; + } + + else if (strncmp(begin, ">", size) == 0 && size == 1) { tok->type = TOKEN_REDIR_RIGHT; } - else if (strncmp(begin, "<", size) == 0) + else if (strncmp(begin, "<", size) == 0 && size == 1) { tok->type = TOKEN_REDIR_LEFT; } - else if (strncmp(begin, ">>", size) == 0) + else if (strncmp(begin, ">>", size) == 0 && size == 2) { tok->type = TOKEN_REDIR_DOUBLE_RIGHT; } - else if (strncmp(begin, ">&", size) == 0) + else if (strncmp(begin, ">&", size) == 0 && size == 2) { tok->type = TOKEN_REDIR_RIGHT_AMP; } - else if (strncmp(begin, ">|", size) == 0) + else if (strncmp(begin, ">|", size) == 0 && size == 2) { tok->type = TOKEN_REDIR_RIGHT_PIPE; } - else if (strncmp(begin, "<&", size) == 0) + else if (strncmp(begin, "<&", size) == 0 && size == 2) { tok->type = TOKEN_REDIR_LEFT_AMP; } - else if (strncmp(begin, "<>", size) == 0) + else if (strncmp(begin, "<>", size) == 0 && size == 2) { tok->type = TOKEN_REDIR_LEFT_RIGHT; } - else if (strncmp(begin, "|", size) == 0) + else if (strncmp(begin, "|", size) == 0 && size == 1) { tok->type = TOKEN_PIPE; } @@ -231,9 +238,9 @@ struct token *new_token(char *begin, ssize_t size, struct token_info *info) set_token_assignment(tok, begin, size); else { + set_token_keyword(tok, begin, size); set_token_operator(tok, begin, size); set_token_spechar(tok, begin, size); - set_token_keyword(tok, begin, size); set_token_word(tok, begin, size); } @@ -286,10 +293,19 @@ ssize_t len_op_sepchar(char *stream, ssize_t i) if (!is_special_char(stream, i)) return -1; // should never happen - if (stream[i] != '>' && stream[i] != '<') - return 1; // special character (cannot be operator) + // OR + if (stream[i] == '|' && stream[i + 1] == '|') + return 2; - // operator + // AND + if (stream[i] == '|' && stream[i + 1] == '|') + return 2; + + // special chars + if (stream[i] != '>' && stream[i] != '<') + return 1; + + // REDIRS if (stream[i] == '<') { diff --git a/src/lexer/lexer_utils.h b/src/lexer/lexer_utils.h index 4a09968..364f7cc 100644 --- a/src/lexer/lexer_utils.h +++ b/src/lexer/lexer_utils.h @@ -72,9 +72,10 @@ enum token_type TOKEN_FOR, TOKEN_WHILE, TOKEN_UNTIL, + TOKEN_CASE, + TOKEN_EXPORT, TOKEN_DO, - TOKEN_DONE, - TOKEN_CASE + TOKEN_DONE }; struct token diff --git a/src/parser/grammar.c b/src/parser/grammar.c index b89a99e..0680a79 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -89,9 +89,21 @@ static bool init_firsts_map(void) return true; } +static void add_first_redir(void) +{ + add_first(RULE_REDIRECTION, TOKEN_IONUMBER); + add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT); + add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT); + add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT); + add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT); + add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP); + add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP); + add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); +} + // === Functions -bool grammar_init(void) +int grammar_init(void) { // Initialize the firsts map bool success = init_firsts_map(); @@ -128,15 +140,9 @@ bool grammar_init(void) add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM)); // Redirection - add_first(RULE_REDIRECTION, TOKEN_IONUMBER); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); + add_first_redir(); // %RIP Matteo 30/01/2026 + // %RAX Guillem 30/01/2026 hehe // Element add_first(RULE_ELEMENT, TOKEN_WORD); @@ -153,6 +159,7 @@ bool grammar_init(void) // Simple command add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX)); add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); + add_first(RULE_SIMPLE_COMMAND, TOKEN_EXPORT); // Funcdec add_first(RULE_FUNCDEC, TOKEN_WORD); diff --git a/src/parser/grammar.h b/src/parser/grammar.h index 663d37b..020d733 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -64,7 +64,7 @@ struct firsts_list * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error * @warning Do not use outside the parser */ -bool grammar_init(void); +int grammar_init(void); /* * @brief Closes the grammar submodule diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index a1235c1..22e3b9b 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -138,7 +138,7 @@ struct ast *parse_prefix(struct lexer_context *ctx) if (token->type == TOKEN_ASSIGNMENT_WORD) { token = POP_TOKEN(); - return ast_create_assignment(token->data); + return ast_create_assignment(token->data, false); } else if (is_first(*token, RULE_REDIRECTION)) return parse_redirection(ctx); diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index f0f96f9..dcd6b31 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -26,17 +26,6 @@ static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) } } -/* @brief: frees command_elements and redirections lists (helper func) - * @return: NULL - */ -static void *err_simple_command(struct list *command_elements, - struct list *redirections) -{ - list_deep_destroy(command_elements); - list_deep_destroy(redirections); - return NULL; -} - /* @brief: frees all the arguments. (helper func) * @return: NULL. */ @@ -49,6 +38,46 @@ static void *err_if_rule(struct ast **cond, struct ast **then_clause, return NULL; } +/* @brief: frees command_elements and redirections lists (helper func) + * @return: NULL + */ +static void *err_s_com(struct list *command_elements, struct list *redirections, + struct list *assignments) +{ + list_deep_destroy(command_elements); + list_deep_destroy(redirections); + list_deep_destroy(assignments); + return NULL; +} + +/* @brief: used when export keyword is found, and expects an assignment after. + * @return: an ast_assignment with the field [global] set to true. + */ +static struct ast *parse_export(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + if (token->type != TOKEN_EXPORT) + { + fprintf(stderr, "expected the export keyword in parse_export"); + return NULL; + } + // export + POP_TOKEN(); + + token = PEEK_TOKEN(); + + if (token->type != TOKEN_ASSIGNMENT_WORD) + { + fprintf(stderr, "in parser: export must be followed by 'x=y'"); + return NULL; + } + + // assignment + POP_TOKEN(); + + return ast_create_assignment(token->data, true); +} + // === Functions struct ast *parse_list(struct lexer_context *ctx) @@ -224,7 +253,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) struct ast *prefix = parse_prefix(ctx); if (prefix == NULL) { - return err_simple_command(command_elements, redirections); + return err_s_com(command_elements, redirections, assignments); } if (prefix->type == AST_ASSIGNMENT) { @@ -240,66 +269,70 @@ struct ast *parse_simple_command(struct lexer_context *ctx) if (token->type != TOKEN_WORD) { - if (!has_prefix) + if (!has_prefix && token->type != TOKEN_EXPORT) { perror("Expected a command but got a different token type"); - return err_simple_command(command_elements, redirections); + return err_s_com(command_elements, redirections, assignments); + } + if (token->type == TOKEN_EXPORT) + { + struct ast *assignment_export = parse_export(ctx); + if (assignment_export == NULL) + return err_s_com(command_elements, redirections, assignments); + + assignments = list_append(assignments, assignment_export); } - // else : only prefixes } - else + else // TOKEN WORD { - if (token->type == TOKEN_WORD) + char *command = strdup(token->data); + command_elements = list_append(command_elements, command); + + POP_TOKEN(); + } + token = PEEK_TOKEN(); + // Eventual elements + while (is_first(*token, RULE_ELEMENT)) + { + // Get element + struct ast *element = parse_element(ctx); + if (element == NULL) { - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); - - POP_TOKEN(); - token = PEEK_TOKEN(); + return err_s_com(command_elements, redirections, assignments); } - // Eventual elements - while (is_first(*token, RULE_ELEMENT)) + + // Get element type + if (ast_is_word(element)) { - // Get element - struct ast *element = parse_element(ctx); - if (element == NULL) - { - return err_simple_command(command_elements, redirections); - } + struct ast_word *element_word = ast_get_word(element); - // Get element type - if (ast_is_word(element)) - { - struct ast_word *element_word = ast_get_word(element); - - // TODO test this fix for the memory leaks - char *word = strdup(element_word->word); - ast_free(&element); - command_elements = list_append(command_elements, word); - // end of fix - } - else if (ast_is_redir(element)) - { - // append redirections to the list of redirections - redirections = list_append(redirections, element); - } - else - { - perror("Internal error: unexpected return value from " - "parse_element in parse_simple_command"); - return err_simple_command(command_elements, redirections); - } - - // Forward - token = PEEK_TOKEN(); + // TODO test this fix for the memory leaks + char *word = strdup(element_word->word); + ast_free(&element); + command_elements = list_append(command_elements, word); + // end of fix } + else if (ast_is_redir(element)) + { + // append redirections to the list of redirections + redirections = list_append(redirections, element); + } + else + { + perror("Internal error: unexpected return value from " + "parse_element in parse_simple_command"); + return err_s_com(command_elements, redirections, assignments); + } + + // Forward + token = PEEK_TOKEN(); } struct ast *result = ast_create_command(command_elements, redirections, assignments); if (result == NULL) { - return err_simple_command(command_elements, redirections); + return err_s_com(command_elements, redirections, assignments); } return result; } @@ -334,6 +367,10 @@ struct ast *parse_element(struct lexer_context *ctx) { return parse_redirection(ctx); } + else if (token->type == TOKEN_EXPORT) + { + return parse_export(ctx); + } else { perror("Syntax error: unexpected token at parse_element"); @@ -531,12 +568,11 @@ struct ast *parse_else_clause(struct lexer_context *ctx) token = POP_TOKEN(); if (token->type != TOKEN_THEN) { - perror( - "Expected the 'then' keyword but got a different token type"); + perror("Expected the 'then' keyword but got a different token " + "type"); return NULL; } - // Then clause struct ast *then_content = parse_compound_list(ctx); if (then_content == NULL) { diff --git a/src/parser/parser.c b/src/parser/parser.c index c2b8aaf..d1f0ebc 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -10,12 +10,12 @@ static enum parser_state state = PARSER_STATE_NOT_INITIALIZED; // === Functions -bool parser_init(void) +int parser_init(void) { if (state == PARSER_STATE_READY) { perror("Internal error: tried to initialize the parser module twice."); - return NULL; + return false; } int success = grammar_init(); if (success == false) diff --git a/src/parser/parser.h b/src/parser/parser.h index a79497b..dc01a5e 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -18,7 +18,7 @@ enum parser_state * * @return Returns false on error and true on success */ -bool parser_init(void); +int parser_init(void); /* @brief Closes the parser module after use */ diff --git a/src/utils/args/args.c b/src/utils/args/args.c index a957382..c34599f 100644 --- a/src/utils/args/args.c +++ b/src/utils/args/args.c @@ -121,7 +121,7 @@ int args_handler(int argc, char **argv, struct args_options *options, } args_in_var(vars, args_list); - list_destroy(args_list); + list_destroy(&args_list); if (options->type == INPUT_UNDEFINED) options->type = INPUT_STDIN; diff --git a/src/utils/ast/ast_assignment.c b/src/utils/ast/ast_assignment.c index e859912..72cfdb6 100644 --- a/src/utils/ast/ast_assignment.c +++ b/src/utils/ast/ast_assignment.c @@ -34,7 +34,7 @@ static void init_assignments(struct ast_assignment *ast_assignment, ast_assignment->value = strdup(split_pos + 1); } -struct ast *ast_create_assignment(char *assignment) +struct ast *ast_create_assignment(char *assignment, bool global) { struct ast_assignment *assignment_data = calloc(1, sizeof(struct ast_assignment)); @@ -42,6 +42,7 @@ struct ast *ast_create_assignment(char *assignment) return NULL; init_assignments(assignment_data, assignment); + assignment_data->global = global; return ast_create(AST_ASSIGNMENT, assignment_data); } diff --git a/src/utils/ast/ast_assignment.h b/src/utils/ast/ast_assignment.h index 5be56c8..3ebac14 100644 --- a/src/utils/ast/ast_assignment.h +++ b/src/utils/ast/ast_assignment.h @@ -7,11 +7,12 @@ struct ast_assignment { char *name; char *value; + bool global; }; bool ast_is_assignment(struct ast *node); struct ast_assignment *ast_get_assignment(struct ast *node); -struct ast *ast_create_assignment(char *assignment); +struct ast *ast_create_assignment(char *assignment, bool global); void ast_free_assignment(struct ast_assignment *assignment_data); #endif /* ! AST_ASSIGNMENT_H */ diff --git a/src/utils/lists/lists.h b/src/utils/lists/lists.h index 9b5d38e..9f8cebb 100644 --- a/src/utils/lists/lists.h +++ b/src/utils/lists/lists.h @@ -31,7 +31,7 @@ void list_print(struct list *list); ** Release the memory used by the list. ** Does nothing if `list` is `NULL`. */ -void list_destroy(struct list *list); +void list_destroy(struct list **list); /* ** Release the memory used by the list and its content diff --git a/src/utils/lists/lists1.c b/src/utils/lists/lists1.c index d191bf7..521e131 100644 --- a/src/utils/lists/lists1.c +++ b/src/utils/lists/lists1.c @@ -49,9 +49,9 @@ void list_print(struct list *list) } } -void list_destroy(struct list *list) +void list_destroy(struct list **list) { - struct list *elt = list; + struct list *elt = *list; struct list *next_elt; while (elt != NULL) { @@ -59,6 +59,7 @@ void list_destroy(struct list *list) free(elt); elt = next_elt; } + *list = NULL; } struct list *list_append(struct list *list, void *value) diff --git a/tests/and_ors.sh b/tests/and_ors.sh new file mode 100755 index 0000000..c2f4cf0 --- /dev/null +++ b/tests/and_ors.sh @@ -0,0 +1,17 @@ +true +echo 'true =' $? + +false +echo 'false =' $? + +false && true +echo 'false && true =' $? + +true && false +echo 'true && false =' $? + +true || false +echo 'true || false =' $? + +false || true +echo 'false || true =' $? diff --git a/tests/unit/utils/lists.c b/tests/unit/utils/lists.c new file mode 100644 index 0000000..c852803 --- /dev/null +++ b/tests/unit/utils/lists.c @@ -0,0 +1,305 @@ +#define _POSIX_C_SOURCE 200809L + +#include "../../../src/utils/lists/lists.h" + +#include +#include +#include +#include +#include +#include +#include + +TestSuite(lists); + +Test(lists, append_empty) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, prepend_empty) +{ + struct list *lst = NULL; + lst = list_prepend(lst, (void *)1); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, insert_empty) +{ + struct list *lst = NULL; + lst = list_insert(lst, (void *)1, 0); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, insert_out_of_bounds) +{ + struct list *lst = NULL; + lst = list_insert(lst, (void *)1, 5); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, remove_out_of_bounds) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_remove(lst, 5); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, append_multiple) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)2); + lst = list_append(lst, (void *)3); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next->data == (void *)2); + cr_expect(lst->next->next->data == (void *)3); + cr_expect(lst->next->next->next == NULL); + cr_expect(list_length(lst) == 3); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, prepend_multiple) +{ + struct list *lst = NULL; + lst = list_prepend(lst, (void *)1); + lst = list_prepend(lst, (void *)2); + lst = list_prepend(lst, (void *)3); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)3); + cr_expect(lst->next->data == (void *)2); + cr_expect(lst->next->next->data == (void *)1); + cr_expect(lst->next->next->next == NULL); + cr_expect(list_length(lst) == 3); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, insert_multiple) +{ + struct list *lst = NULL; + lst = list_insert(lst, (void *)1, 0); + lst = list_insert(lst, (void *)3, 1); + lst = list_insert(lst, (void *)2, 1); + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next->data == (void *)2); + cr_expect(lst->next->next->data == (void *)3); + cr_expect(lst->next->next->next == NULL); + cr_expect(list_length(lst) == 3); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, append) +{ + struct list *lst = NULL; + lst = list_prepend(lst, (void *)2); + lst = list_prepend(lst, (void *)1); + + lst = list_append(lst, (void *)3); + + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)1); + cr_expect(lst->next->data == (void *)2); + cr_expect(lst->next->next->data == (void *)3); + cr_expect(lst->next->next->next == NULL); + cr_expect(list_length(lst) == 3); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, prepend) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)2); + + lst = list_prepend(lst, (void *)0); + + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)0); + cr_expect(lst->next->data == (void *)1); + cr_expect(lst->next->next->data == (void *)2); + cr_expect(lst->next->next->next == NULL); + cr_expect(list_length(lst) == 3); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, insert) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)3); + + lst = list_insert(lst, (void *)2, 1); + lst = list_insert(lst, (void *)0, 0); + + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)0); + cr_expect(lst->next->data == (void *)1); + cr_expect(lst->next->next->data == (void *)2); + cr_expect(lst->next->next->next->data == (void *)3); + cr_expect(lst->next->next->next->next == NULL); + cr_expect(list_length(lst) == 4); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, remove) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)2); + lst = list_append(lst, (void *)3); + lst = list_append(lst, (void *)4); + + lst = list_remove(lst, 1); // remove 2 + lst = list_remove(lst, 2); // remove 4 + lst = list_remove(lst, 0); // remove 1 + + cr_expect(lst != NULL); + cr_expect(lst->data == (void *)3); + cr_expect(lst->next == NULL); + cr_expect(list_length(lst) == 1); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, destroy_null) +{ + struct list *lst = NULL; + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, deep_destroy_null) +{ + struct list *lst = NULL; + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, deep_destroy) +{ + struct list *lst = NULL; + lst = list_append(lst, strdup("string1")); + lst = list_append(lst, strdup("string2")); + lst = list_append(lst, strdup("string3")); + + list_deep_destroy(lst); +} + +Test(lists, length_empty) +{ + struct list *lst = NULL; + cr_expect(list_length(lst) == 0); +} + +Test(lists, print_empty, .init = cr_redirect_stdout) +{ + struct list *lst = NULL; + list_print(lst); + cr_expect_stdout_eq_str(""); +} + +Test(lists, print_non_empty, .init = cr_redirect_stdout) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)2); + lst = list_append(lst, (void *)3); + + list_print(lst); + fflush(stdout); + cr_expect_stdout_eq_str("0x1 0x2 0x3\n"); + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +Test(lists, find_empty) +{ + struct list *lst = NULL; + cr_expect(list_find(lst, (void *)1) == -1); +} + +Test(lists, find_non_empty) +{ + struct list *lst = NULL; + lst = list_append(lst, (void *)1); + lst = list_append(lst, (void *)2); + lst = list_append(lst, (void *)3); + + cr_expect(list_find(lst, (void *)1) == 0); + cr_expect(list_find(lst, (void *)2) == 1); + cr_expect(list_find(lst, (void *)3) == 2); + cr_expect(list_find(lst, (void *)4) == -1); // not found + + list_destroy(&lst); + cr_expect(lst == NULL); +} + +static void fold_func(void *acc, void *data) +{ + *(int *)acc += *(int *)data; +} + +Test(lists, fold) +{ + struct list *lst = NULL; + int v1 = 10, v2 = 20, v3 = 30; + lst = list_append(lst, &v1); + lst = list_append(lst, &v2); + lst = list_append(lst, &v3); + + int sum = 0; + list_fold(lst, &sum, fold_func); + cr_expect(sum == 60); + + list_destroy(&lst); + cr_expect(lst == NULL); +} diff --git a/tests/unit/utils/vars.c b/tests/unit/utils/vars.c new file mode 100644 index 0000000..0c6d6ea --- /dev/null +++ b/tests/unit/utils/vars.c @@ -0,0 +1,84 @@ +#define _POSIX_C_SOURCE 200809L +#include "../../../src/utils/vars/vars.h" + +#include +#include +#include +#include + +#include "../../../src/utils/hash_map/hash_map.h" +#include "../../../src/utils/string_utils/string_utils.h" + +TestSuite(utils_vars); + +Test(utils_vars, init_free) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + hash_map_free(&map); +} + +Test(utils_vars, get_defaults) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + cr_assert_str_eq(get_var(map, "?"), "0"); + char int_str[11]; + int_to_str((int)getpid(), int_str); + cr_assert_str_eq(get_var(map, "$"), int_str); + int_to_str((int)getuid(), int_str); + cr_assert_str_eq(get_var(map, "UID"), int_str); + + hash_map_free(&map); +} + +Test(utils_vars, set_vars) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + set_var_copy(map, "key1", "value1"); + cr_assert_str_eq(get_var(map, "key1"), "value1"); + set_var_copy(map, "key2", "value2"); + cr_assert_str_eq(get_var(map, "key2"), "value2"); + + hash_map_free(&map); +} + +Test(utils_vars, get_env_vars) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + cr_assert_eq(get_var_or_env(map, "ENV_TEST"), NULL); + setenv("ENV_TEST", "value1", 0); + cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value1"); + setenv("ENV_TEST", "value2", 1); + cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value2"); + + hash_map_free(&map); +} + +Test(utils_vars, set_vars_update) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + set_var_copy(map, "key", "value1"); + cr_assert_str_eq(get_var(map, "key"), "value1"); + set_var_copy(map, "key", "value2"); + cr_assert_str_eq(get_var(map, "key"), "value2"); + + hash_map_free(&map); +} + +Test(utils_vars, set_vars_int) +{ + struct hash_map *map = vars_init(); + cr_expect_not_null(map); + set_var_int(map, "key1", 100); + cr_assert_str_eq(get_var(map, "key1"), "100"); + set_var_int(map, "key1", 200); + cr_assert_str_eq(get_var(map, "key1"), "200"); + set_var_int(map, "key2", 10); + cr_assert_str_eq(get_var(map, "key2"), "10"); + + hash_map_free(&map); +}