linked list V1 / phone-number typos / student setup script V1

This commit is contained in:
2021-09-04 20:57:50 +02:00
parent 33cc1343cb
commit 064f3d27a9
15 changed files with 992 additions and 6 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ core
*.out *.out
*_test.sh *_test.sh
test/ test/
test-framework/
users/ users/
.exercism .exercism
.projectile .projectile

56
c/linked-list/GNUmakefile Normal file
View File

@@ -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)

63
c/linked-list/HELP.md Normal file
View File

@@ -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/

50
c/linked-list/README.md Normal file
View File

@@ -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

45
c/linked-list/br-common.h Normal file
View File

@@ -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 <stdio.h>
# include <stdlib.h>
#endif
#ifdef TESTALL
# undef TEST_IGNORE
# define TEST_IGNORE() {}
#endif
#endif /* __BR_COMMON_H */

229
c/linked-list/br-list.h Normal file
View File

@@ -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 <stddef.h>
#include <stdbool.h>
/************ originally in <include/linux/types.h> */
struct list_head {
struct list_head *next, *prev;
};
/************ originally in <include/linux/poison.h> */
#define LIST_POISON1 ((void *) 0x100)
#define LIST_POISON2 ((void *) 0x122)
/************ originally in <include/linux/kernel.h> */
#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 */

126
c/linked-list/linked_list.c Normal file
View File

@@ -0,0 +1,126 @@
#include <malloc.h>
#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);
}

View File

@@ -0,0 +1,36 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include <stddef.h>
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"

37
c/linked-list/makefile Normal file
View File

@@ -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)

View File

@@ -0,0 +1,243 @@
#include <stddef.h>
#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();
}

View File

@@ -27,9 +27,9 @@
default: all default: all
ALLSOURCES=$(wildcard ./*.c) ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES=$(wildcard ./test_*.c) TESTSOURCES:=$(wildcard ./test_*.c)
SRC=$(filter-out $(TESTSOURCES),$(ALLSOURCES)) SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile include makefile
@@ -52,6 +52,5 @@ unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST unit: CFLAGS+=-DUNIT_TEST
unit: SOURCES=$(wildcard ./*.c)
unit: *.c *.h unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS) $(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

View File

@@ -1,3 +1,4 @@
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
#include <string.h> #include <string.h>

View File

@@ -1,10 +1,10 @@
#ifndef PHONE_NUMBER_H #ifndef PHONE_NUMBER_H
#define PHONE_NUMBER_H #define PHONE_NUMBER_H
#include "br-common.h"
#define NUMBER_LENGTH 10 #define NUMBER_LENGTH 10
char *phone_number_clean(const char *input); char *phone_number_clean(const char *input);
#endif #endif
#include "br-common.h"

44
c/setup-exercise.sh Executable file
View File

@@ -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.

56
c/templates/GNUmakefile Normal file
View File

@@ -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)