diff --git a/.gitignore b/.gitignore index a8abb70..4638c05 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ core *.out *_test.sh test/ +test-framework/ users/ .exercism .projectile diff --git a/c/linked-list/GNUmakefile b/c/linked-list/GNUmakefile new file mode 100644 index 0000000..6742704 --- /dev/null +++ b/c/linked-list/GNUmakefile @@ -0,0 +1,56 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# 1) copy it into exercise directory +# 2) add ex.h to exercise include file +# 3) add ex.c to exercise source code, and create a suitable main function +# 4) use make with one of the following targets : +# all: compile and run all predefined tests. +# nowarn: compile with no -Werror, and run all predefined tests +# debug: compile with -DDEBUG and run all predefined tests +# mem: perform memcheck with all tests enabled +# unit: build standalone (unit) bimary +# unitnowarn: build standalone (unit) binary with -Werror disabled +# unitdebug: build standalone binary with -DDEBUG +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all nowarn debug mem unit unitnowarn unitdebug standalone + +default: all + +ALLSOURCES:=$(wildcard ./*.c) +TESTSOURCES:=$(wildcard ./test_*.c) +SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES)) + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +nowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS)) +nowarn: clean all + +debug: CFLAGS+=-DDEBUG +debug: all + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unitnowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS)) +unitnowarn: clean unit + +unitdebug: CFLAGS+=-DDEBUG +unitdebug: clean unit + +unit: CFLAGS+=-DUNIT_TEST +unit: *.c *.h + $(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS) diff --git a/c/linked-list/HELP.md b/c/linked-list/HELP.md new file mode 100644 index 0000000..cc5bf61 --- /dev/null +++ b/c/linked-list/HELP.md @@ -0,0 +1,63 @@ +# Help + +## Running the tests + +Get the first test compiling, linking and passing by following the [three rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` task. + +```console +$ make test +``` + +Create just the functions you need to satisfy any compiler errors and get the test to fail. +Then write just enough code to get the test to pass. +Once you've done that, move onto the next test. + +As you progress through the tests, take the time to refactor your implementation for readability and expressiveness and then go on to the next test. + +Try to use standard C99 facilities in preference to writing your own low-level algorithms or facilities by hand. + +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd + +## Submitting your solution + +You can submit your solution using the `exercism submit linked_list.c linked_list.h` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [C track's documentation](https://exercism.org/docs/tracks/c) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +Make sure you have read the "Guides" section of the [C track][c-track] on the Exercism site. +This covers the basic information on setting up the development environment expected by the exercises. + +## Submitting Incomplete Solutions + +If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise. + +## Resources + +To get help if having trouble, you can use the following resources: + +- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. +- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more. +- [TutorialsPoint][] has similar content as CPPReference in its C programming section. +- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today. + +[c-track]: https://exercism.io/my/tracks/c +[stackoverflow]: http://stackoverflow.com/questions/tagged/c +[cppreference]: https://en.cppreference.com/w/c +[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/ +[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/ \ No newline at end of file diff --git a/c/linked-list/README.md b/c/linked-list/README.md new file mode 100644 index 0000000..758addb --- /dev/null +++ b/c/linked-list/README.md @@ -0,0 +1,50 @@ +# Linked List + +Welcome to Linked List on Exercism's C Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Instructions + +Implement a doubly linked list. + +Like an array, a linked list is a simple linear data structure. Several +common data types can be implemented using linked lists, like queues, +stacks, and associative arrays. + +A linked list is a collection of data elements called *nodes*. In a +*singly linked list* each node holds a value and a link to the next node. +In a *doubly linked list* each node also holds a link to the previous +node. + +You will write an implementation of a doubly linked list. Implement a +Node to hold a value and pointers to the next and previous nodes. Then +implement a List which holds references to the first and last node and +offers an array-like interface for adding and removing items: + +* `push` (*insert value at back*); +* `pop` (*remove value at back*); +* `shift` (*remove value at front*). +* `unshift` (*insert value at front*); + +To keep your implementation simple, the tests will not cover error +conditions. Specifically: `pop` or `shift` will never be called on an +empty list. + +If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/Linked_list). + +## Source + +### Created by + +- @wolf99 + +### Contributed to by + +- @patricksjackson +- @QLaille +- @ryanplusplus +- @siebenschlaefer + +### Based on + +Classic computer science topic \ No newline at end of file diff --git a/c/linked-list/br-common.h b/c/linked-list/br-common.h new file mode 100644 index 0000000..ca7968e --- /dev/null +++ b/c/linked-list/br-common.h @@ -0,0 +1,45 @@ +#ifndef __BR_COMMON_H +#define __BR_COMMON_H + +/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30 + * + * __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17. + * For other compilers, simple implementation (for __falltrough__ only) + */ +#ifndef __has_attribute +# define __has_attribute(x) __GCC4_has_attribute_##x +# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9) +# define __GCC4_has_attribute___copy__ 0 +# define __GCC4_has_attribute___designated_init__ 0 +# define __GCC4_has_attribute___externally_visible__ 1 +# define __GCC4_has_attribute___no_caller_saved_registers__ 0 +# define __GCC4_has_attribute___noclone__ 1 +# define __GCC4_has_attribute___nonstring__ 0 +# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8) +# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9) +# define __GCC4_has_attribute___fallthrough__ 0 +# define __GCC4_has_attribute___fallthrough__ 0 +#endif + +/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200 + */ +#if __has_attribute(__fallthrough__) +# define fallthrough __attribute__((__fallthrough__)) +#else +# define fallthrough do {} while (0); /* fallthrough */ +#endif + +/* See GNUmakefile below for explanation + * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile + */ +#if defined UNIT_TEST || defined DEBUG +# include +# include +#endif + +#ifdef TESTALL +# undef TEST_IGNORE +# define TEST_IGNORE() {} +#endif + +#endif /* __BR_COMMON_H */ diff --git a/c/linked-list/br-list.h b/c/linked-list/br-list.h new file mode 100644 index 0000000..9562e4f --- /dev/null +++ b/c/linked-list/br-list.h @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* stripped down version of my adaptation of Linux kernel lists management : + * https://github.com/braoult/Tools/blob/master/C/list.h + */ +#ifndef __LIST_H +#define __LIST_H + +#include +#include + +/************ originally in */ +struct list_head { + struct list_head *next, *prev; +}; + +/************ originally in */ +#define LIST_POISON1 ((void *) 0x100) +#define LIST_POISON2 ((void *) 0x122) + +/************ originally in */ +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del_entry(entry); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, __typeof__(*(pos)), member) + +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, __typeof__(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_entry_is_head - test if the entry points to the head of the list + * @pos: the type * to cursor + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_entry_is_head(pos, head, member) \ + (&pos->member == (head)) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, __typeof__(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, __typeof__(*pos), member), \ + n = list_next_entry(pos, member); \ + !list_entry_is_head(pos, head, member); \ + pos = n, n = list_next_entry(n, member)) + +#endif /* __LIST_H */ diff --git a/c/linked-list/linked_list.c b/c/linked-list/linked_list.c new file mode 100644 index 0000000..977109c --- /dev/null +++ b/c/linked-list/linked_list.c @@ -0,0 +1,126 @@ +#include + +#include "br-list.h" +#include "linked_list.h" + +struct list_node { + ll_data_t data; + struct list_head list; +}; + +/* duplicate of list_head struct + */ +struct list { + struct list_head *next, *prev; +}; + +/* constructs a new (empty) list + */ +struct list *list_create() +{ + struct list_head *list; + + if (!(list=malloc(sizeof (*list)))) + return NULL; + INIT_LIST_HEAD(list); + return (struct list *) list; +} + +/* counts the items on a list + */ +size_t list_count(const struct list *list_head) +{ + size_t len = 0; + struct list_head *p; + + list_for_each(p, (struct list_head *)list_head) + len++; + return len; +} + +/* inserts item at back of a list + */ +void list_push(struct list *list, ll_data_t item_data) +{ + struct list_node *p; + + if ((p=malloc(sizeof(*p)))) { + p->data = item_data; + list_add_tail(&p->list, (struct list_head*)list); + } +} + +/* deletes an element + */ +static ll_data_t _list_del(struct list_head *list) +{ + struct list_node *node; + ll_data_t data; + + node = list_entry(list, struct list_node, list); + data = node->data; + list_del(&node->list); + free(node); + return data; +} + +/* removes item from back of a list + */ +ll_data_t list_pop(struct list *list) +{ + return list_empty((struct list_head*)list) ? -1 : _list_del(list->prev); +} + +/* inserts item at front of a list + */ +void list_unshift(struct list *list, ll_data_t item_data) +{ + struct list_node *p; + + if ((p=malloc(sizeof(*p)))) { + p->data = item_data; + list_add(&p->list, (struct list_head*)list); + } +} + +/* removes item from front of a list + */ +ll_data_t list_shift(struct list *list) +{ + return list_empty((struct list_head*)list) ? -1 : _list_del(list->next); +} + +/* finds a element that matches data + */ +static struct list_node *_node_find(struct list *list, ll_data_t data) +{ + struct list_node *p; + + list_for_each_entry(p, (struct list_head *)list, list) { + if (p->data == data) + return p; + } + return NULL; +} + +/* deletes a node that holds the matching data + */ +void list_delete(struct list *list, ll_data_t data) +{ + struct list_node *p; + + if ((p = _node_find(list, data))) + _list_del(&p->list); +} + +/* destroys an entire list + * list will be a dangling pointer after calling this method on it + */ +void list_destroy(struct list *list) +{ + struct list_head *cur, *tmp; + + list_for_each_safe(cur, tmp, (struct list_head *)list) + _list_del(cur); + free(list); +} diff --git a/c/linked-list/linked_list.h b/c/linked-list/linked_list.h new file mode 100644 index 0000000..b524caf --- /dev/null +++ b/c/linked-list/linked_list.h @@ -0,0 +1,36 @@ +#ifndef LINKED_LIST_H +#define LINKED_LIST_H + +#include + +typedef int ll_data_t; +struct list; + +// constructs a new (empty) list +struct list *list_create(void); + +// counts the items on a list +size_t list_count(const struct list *list); + +// inserts item at back of a list +void list_push(struct list *list, ll_data_t item_data); + +// removes item from back of a list +ll_data_t list_pop(struct list *list); + +// inserts item at front of a list +void list_unshift(struct list *list, ll_data_t item_data); + +// removes item from front of a list +ll_data_t list_shift(struct list *list); + +// deletes a node that holds the matching data +void list_delete(struct list *list, ll_data_t data); + +// destroys an entire list +// list will be a dangling pointer after calling this method on it +void list_destroy(struct list *list); + +#endif + +#include "br-common.h" diff --git a/c/linked-list/makefile b/c/linked-list/makefile new file mode 100644 index 0000000..d5ff0a1 --- /dev/null +++ b/c/linked-list/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: ./*.c ./*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: ./*.c ./*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS) diff --git a/c/linked-list/test_linked_list.c b/c/linked-list/test_linked_list.c new file mode 100644 index 0000000..f871bd9 --- /dev/null +++ b/c/linked-list/test_linked_list.c @@ -0,0 +1,243 @@ +#include +#include "test-framework/unity.h" +#include "linked_list.h" + +struct list *list = NULL; + +void setUp(void) +{ + list = list_create(); +} + +void tearDown(void) +{ + if (list) { + list_destroy(list); + list = NULL; + } +} + +static void test_pop_gets_element_from_the_list(void) +{ + list_push(list, 7); + TEST_ASSERT_EQUAL(7, list_pop(list)); +} + +static void test_push_pop_respectively_add_remove_at_the_end_of_the_list(void) +{ + TEST_IGNORE(); // delete this line to run test + list_push(list, 11); + list_push(list, 13); + TEST_ASSERT_EQUAL(13, list_pop(list)); + TEST_ASSERT_EQUAL(11, list_pop(list)); +} + +static void test_shift_gets_an_element_from_the_list(void) +{ + TEST_IGNORE(); + list_push(list, 17); + TEST_ASSERT_EQUAL(17, list_shift(list)); +} + +static void test_shift_gets_first_element_from_the_list(void) +{ + TEST_IGNORE(); + list_push(list, 23); + list_push(list, 5); + TEST_ASSERT_EQUAL(23, list_shift(list)); + TEST_ASSERT_EQUAL(5, list_shift(list)); +} + +static void test_unshift_adds_element_at_start_of_the_list(void) +{ + TEST_IGNORE(); + list_unshift(list, 23); + list_unshift(list, 5); + TEST_ASSERT_EQUAL(5, list_shift(list)); + TEST_ASSERT_EQUAL(23, list_shift(list)); +} + +static void test_pop_push_shift_and_unshift_can_be_used_in_any_order(void) +{ + TEST_IGNORE(); + list_push(list, 1); + list_push(list, 2); + TEST_ASSERT_EQUAL(2, list_pop(list)); + list_push(list, 3); + TEST_ASSERT_EQUAL(1, list_shift(list)); + list_unshift(list, 4); + list_push(list, 5); + TEST_ASSERT_EQUAL(4, list_shift(list)); + TEST_ASSERT_EQUAL(5, list_pop(list)); + TEST_ASSERT_EQUAL(3, list_shift(list)); +} + +static void test_count_an_empty_list(void) +{ + TEST_IGNORE(); + TEST_ASSERT_EQUAL(0, list_count(list)); +} + +static void test_count_a_list_with_items(void) +{ + TEST_IGNORE(); + list_push(list, 37); + list_push(list, 1); + TEST_ASSERT_EQUAL(2, list_count(list)); +} + +static void test_count_is_correct_after_mutation(void) +{ + TEST_IGNORE(); + list_push(list, 31); + TEST_ASSERT_EQUAL(1, list_count(list)); + list_unshift(list, 43); + TEST_ASSERT_EQUAL(2, list_count(list)); + list_shift(list); + TEST_ASSERT_EQUAL(1, list_count(list)); + list_pop(list); + TEST_ASSERT_EQUAL(0, list_count(list)); +} + +static void test_popping_to_empty_does_not_break_the_list(void) +{ + TEST_IGNORE(); + list_push(list, 41); + list_push(list, 59); + list_pop(list); + list_pop(list); + list_push(list, 47); + TEST_ASSERT_EQUAL(1, list_count(list)); + TEST_ASSERT_EQUAL(47, list_pop(list)); +} + +static void test_shifting_to_empty_does_not_break_the_list(void) +{ + TEST_IGNORE(); + list_push(list, 41); + list_push(list, 59); + list_shift(list); + list_shift(list); + list_push(list, 47); + TEST_ASSERT_EQUAL(1, list_count(list)); + TEST_ASSERT_EQUAL(47, list_shift(list)); +} + +static void test_deletes_the_only_element(void) +{ + TEST_IGNORE(); + list_push(list, 61); + list_delete(list, 61); + TEST_ASSERT_EQUAL(0, list_count(list)); +} + +static void +test_deletes_the_element_with_the_specified_value_from_the_list(void) +{ + TEST_IGNORE(); + list_push(list, 71); + list_push(list, 83); + list_push(list, 79); + list_delete(list, 83); + TEST_ASSERT_EQUAL(2, list_count(list)); + TEST_ASSERT_EQUAL(79, list_pop(list)); + TEST_ASSERT_EQUAL(71, list_shift(list)); +} + +static void + test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_tail + (void) { + TEST_IGNORE(); + list_push(list, 71); + list_push(list, 83); + list_push(list, 79); + list_delete(list, 83); + TEST_ASSERT_EQUAL(2, list_count(list)); + TEST_ASSERT_EQUAL(79, list_pop(list)); + TEST_ASSERT_EQUAL(71, list_pop(list)); +} + +static void + test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_head + (void) { + TEST_IGNORE(); + list_push(list, 71); + list_push(list, 83); + list_push(list, 79); + list_delete(list, 83); + TEST_ASSERT_EQUAL(2, list_count(list)); + TEST_ASSERT_EQUAL(71, list_shift(list)); + TEST_ASSERT_EQUAL(79, list_shift(list)); +} + +static void test_deletes_the_first_of_two_elements(void) +{ + TEST_IGNORE(); + list_push(list, 97); + list_push(list, 101); + list_delete(list, 97); + TEST_ASSERT_EQUAL(1, list_count(list)); + TEST_ASSERT_EQUAL(101, list_pop(list)); +} + +static void test_deletes_the_second_of_two_elements(void) +{ + TEST_IGNORE(); + list_push(list, 97); + list_push(list, 101); + list_delete(list, 101); + TEST_ASSERT_EQUAL(1, list_count(list)); + TEST_ASSERT_EQUAL(97, list_pop(list)); +} + +static void +test_delete_does_not_modify_the_list_if_the_element_is_not_found(void) +{ + TEST_IGNORE(); + list_push(list, 89); + list_delete(list, 103); + TEST_ASSERT_EQUAL(1, list_count(list)); +} + +static void test_deletes_only_the_first_occurrence(void) +{ + TEST_IGNORE(); + list_push(list, 73); + list_push(list, 9); + list_push(list, 9); + list_push(list, 107); + list_delete(list, 9); + TEST_ASSERT_EQUAL(3, list_count(list)); + TEST_ASSERT_EQUAL(107, list_pop(list)); + TEST_ASSERT_EQUAL(9, list_pop(list)); + TEST_ASSERT_EQUAL(73, list_pop(list)); +} + +int main(void) +{ + UnityBegin("test_linked_list.c"); + + RUN_TEST(test_pop_gets_element_from_the_list); + RUN_TEST(test_push_pop_respectively_add_remove_at_the_end_of_the_list); + RUN_TEST(test_shift_gets_an_element_from_the_list); + RUN_TEST(test_shift_gets_first_element_from_the_list); + RUN_TEST(test_unshift_adds_element_at_start_of_the_list); + RUN_TEST(test_pop_push_shift_and_unshift_can_be_used_in_any_order); + RUN_TEST(test_count_an_empty_list); + RUN_TEST(test_count_a_list_with_items); + RUN_TEST(test_count_is_correct_after_mutation); + RUN_TEST(test_popping_to_empty_does_not_break_the_list); + RUN_TEST(test_shifting_to_empty_does_not_break_the_list); + RUN_TEST(test_deletes_the_only_element); + RUN_TEST(test_deletes_the_element_with_the_specified_value_from_the_list); + RUN_TEST + (test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_tail); + RUN_TEST + (test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_head); + RUN_TEST(test_deletes_the_first_of_two_elements); + RUN_TEST(test_deletes_the_second_of_two_elements); + RUN_TEST(test_delete_does_not_modify_the_list_if_the_element_is_not_found); + RUN_TEST(test_deletes_only_the_first_occurrence); + + return UnityEnd(); +} diff --git a/c/phone-number/GNUmakefile b/c/phone-number/GNUmakefile index 4399a55..6742704 100644 --- a/c/phone-number/GNUmakefile +++ b/c/phone-number/GNUmakefile @@ -27,9 +27,9 @@ default: all -ALLSOURCES=$(wildcard ./*.c) -TESTSOURCES=$(wildcard ./test_*.c) -SRC=$(filter-out $(TESTSOURCES),$(ALLSOURCES)) +ALLSOURCES:=$(wildcard ./*.c) +TESTSOURCES:=$(wildcard ./test_*.c) +SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES)) include makefile @@ -52,6 +52,5 @@ unitdebug: CFLAGS+=-DDEBUG unitdebug: clean unit unit: CFLAGS+=-DUNIT_TEST -unit: SOURCES=$(wildcard ./*.c) unit: *.c *.h $(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS) diff --git a/c/phone-number/phone_number.c b/c/phone-number/phone_number.c index e71f357..1db00dc 100644 --- a/c/phone-number/phone_number.c +++ b/c/phone-number/phone_number.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/c/phone-number/phone_number.h b/c/phone-number/phone_number.h index a24b3b6..d27c3c6 100644 --- a/c/phone-number/phone_number.h +++ b/c/phone-number/phone_number.h @@ -1,10 +1,10 @@ #ifndef PHONE_NUMBER_H #define PHONE_NUMBER_H -#include "br-common.h" - #define NUMBER_LENGTH 10 char *phone_number_clean(const char *input); #endif + +#include "br-common.h" diff --git a/c/setup-exercise.sh b/c/setup-exercise.sh new file mode 100755 index 0000000..5fed444 --- /dev/null +++ b/c/setup-exercise.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +CMD=${0##*/} +ROOT=${0%/*} + +usage() { + printf "usage: %s destination_dir\n" "$CMD" +} + +if (($# == 0)); then + "$0" "$PWD" + exit 0 +fi + +if (($# != 1)) || [[ ! -d $1 ]]; then + usage + exit 1 +fi +TEMPLATES="$ROOT/templates" + +# copy GNUmakefile and br-common.h +echo -n "copying GNUmakefile and br-common.h files... " +cp -p "$TEMPLATES/"{GNUmakefile,br-common.h} "$1" +echo done. +# add include br-common.h in exercise include file +INC=${1%/} +INC=${INC##*/} +INC="$1/${INC//-/_}.h" + +if [[ ! -f "$INC" ]]; then + printf "cannot find <%s> include file\n" "$INC" + exit 1 +fi + +STR='#include "br-common.h"' +# likely very weak +# TODO: check if not already inserted +#echo -n "editing $INC include file... " +#sed -i "/#define /a $STR" "$INC" +#echo done. +echo -n "editing $INC include file... " +echo "" >> "$INC" +echo "$STR" >> "$INC" +echo done. diff --git a/c/templates/GNUmakefile b/c/templates/GNUmakefile new file mode 100644 index 0000000..6742704 --- /dev/null +++ b/c/templates/GNUmakefile @@ -0,0 +1,56 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# 1) copy it into exercise directory +# 2) add ex.h to exercise include file +# 3) add ex.c to exercise source code, and create a suitable main function +# 4) use make with one of the following targets : +# all: compile and run all predefined tests. +# nowarn: compile with no -Werror, and run all predefined tests +# debug: compile with -DDEBUG and run all predefined tests +# mem: perform memcheck with all tests enabled +# unit: build standalone (unit) bimary +# unitnowarn: build standalone (unit) binary with -Werror disabled +# unitdebug: build standalone binary with -DDEBUG +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all nowarn debug mem unit unitnowarn unitdebug standalone + +default: all + +ALLSOURCES:=$(wildcard ./*.c) +TESTSOURCES:=$(wildcard ./test_*.c) +SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES)) + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +nowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS)) +nowarn: clean all + +debug: CFLAGS+=-DDEBUG +debug: all + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unitnowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS)) +unitnowarn: clean unit + +unitdebug: CFLAGS+=-DDEBUG +unitdebug: clean unit + +unit: CFLAGS+=-DUNIT_TEST +unit: *.c *.h + $(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)