diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-21 16:29:51 +0100 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-21 16:29:51 +0100 |
| commit | a8f7e6e83bc4846c94c0489e0f6e0f807b35073e (patch) | |
| tree | 0d245c73490427a39d4338da483e335a267c6917 | |
| parent | fd62678a9cf38e3f70efbdd093c1012d448548e1 (diff) | |
| download | Libft-a8f7e6e83bc4846c94c0489e0f6e0f807b35073e.tar.gz Libft-a8f7e6e83bc4846c94c0489e0f6e0f807b35073e.zip | |
Implement libft Part 2 with tests
Add ft_substr, ft_strjoin, ft_strtrim, ft_split, ft_itoa,
ft_strmapi, ft_striteri, ft_putchar_fd, ft_putstr_fd,
ft_putendl_fd, ft_putnbr_fd.
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | inc/libft.h | 32 | ||||
| -rw-r--r-- | src/ft_itoa.c | 45 | ||||
| -rw-r--r-- | src/ft_putchar_fd.c | 7 | ||||
| -rw-r--r-- | src/ft_putendl_fd.c | 9 | ||||
| -rw-r--r-- | src/ft_putnbr_fd.c | 21 | ||||
| -rw-r--r-- | src/ft_putstr_fd.c | 8 | ||||
| -rw-r--r-- | src/ft_split.c | 67 | ||||
| -rw-r--r-- | src/ft_striteri.c | 14 | ||||
| -rw-r--r-- | src/ft_strjoin.c | 19 | ||||
| -rw-r--r-- | src/ft_strmapi.c | 23 | ||||
| -rw-r--r-- | src/ft_strtrim.c | 25 | ||||
| -rw-r--r-- | src/ft_substr.c | 23 | ||||
| -rw-r--r-- | tests/Makefile | 3 | ||||
| -rw-r--r-- | tests/src/test_itoa.c | 99 | ||||
| -rw-r--r-- | tests/src/test_put.c | 197 | ||||
| -rw-r--r-- | tests/src/test_split.c | 234 | ||||
| -rw-r--r-- | tests/src/test_striteri.c | 105 | ||||
| -rw-r--r-- | tests/src/test_strjoin.c | 111 | ||||
| -rw-r--r-- | tests/src/test_strmapi.c | 108 | ||||
| -rw-r--r-- | tests/src/test_strtrim.c | 162 | ||||
| -rw-r--r-- | tests/src/test_substr.c | 135 |
22 files changed, 1450 insertions, 2 deletions
@@ -14,7 +14,10 @@ SRCS = ft_isalpha.c ft_isdigit.c ft_isalnum.c ft_isascii.c ft_isprint.c \ ft_strlcpy.c ft_strlcat.c \ ft_strchr.c ft_strrchr.c ft_strnstr.c ft_atoi.c \ ft_calloc.c ft_strdup.c \ - ft_toupper.c ft_tolower.c + ft_toupper.c ft_tolower.c \ + ft_substr.c ft_strjoin.c ft_strtrim.c ft_split.c ft_itoa.c \ + ft_strmapi.c ft_striteri.c \ + ft_putchar_fd.c ft_putstr_fd.c ft_putendl_fd.c ft_putnbr_fd.c OBJS = $(SRCS:%.c=$(OBJDIR)/%.o) diff --git a/inc/libft.h b/inc/libft.h index d57cd8d..05a5ae9 100644 --- a/inc/libft.h +++ b/inc/libft.h @@ -72,6 +72,25 @@ size_t ft_strlcpy (char *dst, const char *src, size_t size); size_t ft_strlcat (char *dst, const char *src, size_t size); /** @brief Return a malloc'd duplicate of @p s. */ char *ft_strdup (const char *s); +/** + * @brief Allocate a substring from @p s starting at @p start, at most @p len + * chars. + */ +char *ft_substr (char const *s, unsigned int start, size_t len); +/** @brief Allocate the concatenation of @p s1 and @p s2. */ +char *ft_strjoin (char const *s1, char const *s2); +/** @brief Trim characters in @p set from both ends of @p s1. */ +char *ft_strtrim (char const *s1, char const *set); +/** + * @brief Split @p s by delimiter @p c into a NULL-terminated array of strings. + */ +char **ft_split (char const *s, char c); +/** @brief Allocate the decimal string representation of @p n. */ +char *ft_itoa (int n); +/** @brief Apply @p f to each character of @p s, returning a new string. */ +char *ft_strmapi (char const *s, char (*f) (unsigned int, char)); +/** @brief Apply @p f to each character of @p s in place, passing its index. */ +void ft_striteri (char *s, void (*f) (unsigned int, char *)); /* ====================================== * Memory operations @@ -103,4 +122,17 @@ int ft_memcmp (const void *s1, const void *s2, size_t n); */ void *ft_calloc (size_t nmemb, size_t size); +/* ====================================== + * File descriptor output + * ====================================== */ + +/** @brief Write character @p c to file descriptor @p fd. */ +void ft_putchar_fd (char c, int fd); +/** @brief Write string @p s to file descriptor @p fd. */ +void ft_putstr_fd (char *s, int fd); +/** @brief Write string @p s followed by a newline to @p fd. */ +void ft_putendl_fd (char *s, int fd); +/** @brief Write integer @p n in decimal to @p fd. */ +void ft_putnbr_fd (int n, int fd); + #endif diff --git a/src/ft_itoa.c b/src/ft_itoa.c new file mode 100644 index 0000000..05f3fae --- /dev/null +++ b/src/ft_itoa.c @@ -0,0 +1,45 @@ +#include "libft.h" +#include <stdlib.h> + +static size_t +_s_numlen (int n) +{ + size_t len; + + len = 1; + if (n < 0) + len++; + while (n / 10) + { + len++; + n /= 10; + } + return (len); +} + +char * +ft_itoa (int n) +{ + size_t len; + char *str; + unsigned int nb; + + len = _s_numlen (n); + str = malloc (len + 1); + if (!str) + return (NULL); + str[len] = '\0'; + if (n < 0) + { + str[0] = '-'; + nb = -n; + } + else + nb = n; + while (len > 0 && str[len - 1] != '-') + { + str[--len] = '0' + nb % 10; + nb /= 10; + } + return (str); +} diff --git a/src/ft_putchar_fd.c b/src/ft_putchar_fd.c new file mode 100644 index 0000000..9885816 --- /dev/null +++ b/src/ft_putchar_fd.c @@ -0,0 +1,7 @@ +#include <unistd.h> + +void +ft_putchar_fd (char c, int fd) +{ + write (fd, &c, 1); +} diff --git a/src/ft_putendl_fd.c b/src/ft_putendl_fd.c new file mode 100644 index 0000000..f7c7334 --- /dev/null +++ b/src/ft_putendl_fd.c @@ -0,0 +1,9 @@ +#include "libft.h" +#include <unistd.h> + +void +ft_putendl_fd (char *s, int fd) +{ + write (fd, s, ft_strlen (s)); + write (fd, "\n", 1); +} diff --git a/src/ft_putnbr_fd.c b/src/ft_putnbr_fd.c new file mode 100644 index 0000000..031f9f6 --- /dev/null +++ b/src/ft_putnbr_fd.c @@ -0,0 +1,21 @@ +#include "libft.h" +#include <unistd.h> + +void +ft_putnbr_fd (int n, int fd) +{ + char c; + unsigned int nb; + + if (n < 0) + { + write (fd, "-", 1); + nb = -n; + } + else + nb = n; + if (nb >= 10) + ft_putnbr_fd (nb / 10, fd); + c = '0' + nb % 10; + write (fd, &c, 1); +} diff --git a/src/ft_putstr_fd.c b/src/ft_putstr_fd.c new file mode 100644 index 0000000..663af0d --- /dev/null +++ b/src/ft_putstr_fd.c @@ -0,0 +1,8 @@ +#include "libft.h" +#include <unistd.h> + +void +ft_putstr_fd (char *s, int fd) +{ + write (fd, s, ft_strlen (s)); +} diff --git a/src/ft_split.c b/src/ft_split.c new file mode 100644 index 0000000..bb51c85 --- /dev/null +++ b/src/ft_split.c @@ -0,0 +1,67 @@ +#include "libft.h" +#include <stdlib.h> + +static size_t +_s_count_words (char const *s, char c) +{ + size_t count; + + count = 0; + while (*s) + { + while (*s == c) + s++; + if (*s) + { + count++; + while (*s && *s != c) + s++; + } + } + return (count); +} + +static void +_s_free_all (char **arr, size_t n) +{ + size_t i; + + i = 0; + while (i < n) + free (arr[i++]); + free (arr); +} + +char ** +ft_split (char const *s, char c) +{ + size_t count; + char **arr; + size_t i; + + count = _s_count_words (s, c); + arr = malloc ((count + 1) * sizeof (char *)); + if (!arr) + return (NULL); + i = 0; + while (*s) + { + while (*s == c) + s++; + if (*s) + { + const char *start = s; + while (*s && *s != c) + s++; + arr[i] = ft_substr (start, 0, s - start); + if (!arr[i]) + { + _s_free_all (arr, i); + return (NULL); + } + i++; + } + } + arr[i] = NULL; + return (arr); +} diff --git a/src/ft_striteri.c b/src/ft_striteri.c new file mode 100644 index 0000000..ba701db --- /dev/null +++ b/src/ft_striteri.c @@ -0,0 +1,14 @@ +#include "libft.h" + +void +ft_striteri (char *s, void (*f) (unsigned int, char *)) +{ + unsigned int i; + + i = 0; + while (s[i]) + { + f (i, &s[i]); + i++; + } +} diff --git a/src/ft_strjoin.c b/src/ft_strjoin.c new file mode 100644 index 0000000..f6e22f0 --- /dev/null +++ b/src/ft_strjoin.c @@ -0,0 +1,19 @@ +#include "libft.h" +#include <stdlib.h> + +char * +ft_strjoin (char const *s1, char const *s2) +{ + size_t len1; + size_t len2; + char *join; + + len1 = ft_strlen (s1); + len2 = ft_strlen (s2); + join = malloc (len1 + len2 + 1); + if (!join) + return (NULL); + ft_memcpy (join, s1, len1); + ft_memcpy (join + len1, s2, len2 + 1); + return (join); +} diff --git a/src/ft_strmapi.c b/src/ft_strmapi.c new file mode 100644 index 0000000..8d419c2 --- /dev/null +++ b/src/ft_strmapi.c @@ -0,0 +1,23 @@ +#include "libft.h" +#include <stdlib.h> + +char * +ft_strmapi (char const *s, char (*f) (unsigned int, char)) +{ + size_t len; + char *result; + size_t i; + + len = ft_strlen (s); + result = malloc (len + 1); + if (!result) + return (NULL); + i = 0; + while (i < len) + { + result[i] = f (i, s[i]); + i++; + } + result[len] = '\0'; + return (result); +} diff --git a/src/ft_strtrim.c b/src/ft_strtrim.c new file mode 100644 index 0000000..e9fa3eb --- /dev/null +++ b/src/ft_strtrim.c @@ -0,0 +1,25 @@ +#include "libft.h" +#include <stdlib.h> + +char * +ft_strtrim (char const *s1, char const *set) +{ + size_t start; + size_t end; + size_t len; + char *trimmed; + + start = 0; + while (s1[start] && ft_strchr (set, s1[start])) + start++; + end = ft_strlen (s1); + while (end > start && ft_strchr (set, s1[end - 1])) + end--; + len = end - start; + trimmed = malloc (len + 1); + if (!trimmed) + return (NULL); + ft_memcpy (trimmed, s1 + start, len); + trimmed[len] = '\0'; + return (trimmed); +} diff --git a/src/ft_substr.c b/src/ft_substr.c new file mode 100644 index 0000000..0db72d0 --- /dev/null +++ b/src/ft_substr.c @@ -0,0 +1,23 @@ +#include "libft.h" +#include <stdlib.h> + +char * +ft_substr (char const *s, unsigned int start, size_t len) +{ + size_t slen; + size_t actual; + char *sub; + + slen = ft_strlen (s); + if (start >= slen) + return (ft_strdup ("")); + actual = slen - start; + if (actual > len) + actual = len; + sub = malloc (actual + 1); + if (!sub) + return (NULL); + ft_memcpy (sub, s + start, actual); + sub[actual] = '\0'; + return (sub); +} diff --git a/tests/Makefile b/tests/Makefile index c0c8059..61b7d44 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,8 @@ BINDIR = bin LIB = ../lib/libft.a TESTS = test_strlen test_is test_mem test_cmp test_case test_strl test_search \ - test_atoi test_alloc + test_atoi test_alloc test_substr test_strjoin test_strtrim \ + test_split test_itoa test_strmapi test_striteri test_put BINS = $(TESTS:%=$(BINDIR)/%) diff --git a/tests/src/test_itoa.c b/tests/src/test_itoa.c new file mode 100644 index 0000000..17cafe3 --- /dev/null +++ b/tests/src/test_itoa.c @@ -0,0 +1,99 @@ +#include "libft.h" +#include "test_utils.h" +#include <limits.h> + +static void +_s_test_itoa (void) +{ + int i; + char label[128]; + char expected[64]; + + _s_section ("ft_itoa"); + + /* zero */ + { + char *p = ft_itoa (0); + _s_check ("zero", p && strcmp (p, "0") == 0); + free (p); + } + + /* positive */ + { + char *p = ft_itoa (42); + _s_check ("positive", p && strcmp (p, "42") == 0); + free (p); + } + + /* negative */ + { + char *p = ft_itoa (-42); + _s_check ("negative", p && strcmp (p, "-42") == 0); + free (p); + } + + /* INT_MAX */ + { + char *p = ft_itoa (INT_MAX); + snprintf (expected, sizeof (expected), "%d", INT_MAX); + _s_check ("INT_MAX", p && strcmp (p, expected) == 0); + free (p); + } + + /* INT_MIN */ + { + char *p = ft_itoa (INT_MIN); + snprintf (expected, sizeof (expected), "%d", INT_MIN); + _s_check ("INT_MIN", p && strcmp (p, expected) == 0); + free (p); + } + + /* single digit positive */ + { + char *p = ft_itoa (7); + _s_check ("single digit", p && strcmp (p, "7") == 0); + free (p); + } + + /* single digit negative */ + { + char *p = ft_itoa (-1); + _s_check ("neg single digit", p && strcmp (p, "-1") == 0); + free (p); + } + + /* powers of ten */ + { + char *p; + + p = ft_itoa (10); + _s_check ("10", p && strcmp (p, "10") == 0); + free (p); + p = ft_itoa (100); + _s_check ("100", p && strcmp (p, "100") == 0); + free (p); + p = ft_itoa (-1000); + _s_check ("-1000", p && strcmp (p, "-1000") == 0); + free (p); + } + + /* randomized */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int n = rand () - RAND_MAX / 2; + char *p = ft_itoa (n); + snprintf (expected, sizeof (expected), "%d", n); + snprintf (label, sizeof (label), "random n=%d", n); + _s_check (label, p && strcmp (p, expected) == 0); + free (p); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_itoa (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_put.c b/tests/src/test_put.c new file mode 100644 index 0000000..3c7f6dc --- /dev/null +++ b/tests/src/test_put.c @@ -0,0 +1,197 @@ +#include "libft.h" +#include "test_utils.h" +#include <limits.h> + +/* ── helper: capture fd output into a buffer ──────────────────────── */ + +static int +_s_read_pipe (void (*fn) (int), char *buf, size_t bufsz) +{ + int fds[2]; + ssize_t n; + + if (pipe (fds) < 0) + return (-1); + fn (fds[1]); + close (fds[1]); + n = read (fds[0], buf, bufsz - 1); + close (fds[0]); + if (n < 0) + return (-1); + buf[n] = '\0'; + return (n); +} + +/* ====================================== + * ft_putchar_fd + * ====================================== */ + +static void +_s_putchar_a (int fd) +{ + ft_putchar_fd ('a', fd); +} +static void +_s_putchar_nl (int fd) +{ + ft_putchar_fd ('\n', fd); +} +static void +_s_putchar_zero (int fd) +{ + ft_putchar_fd ('\0', fd); +} + +static void +_s_test_putchar_fd (void) +{ + char buf[64]; + + _s_section ("ft_putchar_fd"); + + _s_read_pipe (_s_putchar_a, buf, sizeof (buf)); + _s_check ("char 'a'", strcmp (buf, "a") == 0); + + _s_read_pipe (_s_putchar_nl, buf, sizeof (buf)); + _s_check ("newline", strcmp (buf, "\n") == 0); + + { + int n = _s_read_pipe (_s_putchar_zero, buf, sizeof (buf)); + _s_check ("null byte", n == 1 && buf[0] == '\0'); + } +} + +/* ====================================== + * ft_putstr_fd + * ====================================== */ + +static void +_s_putstr_hello (int fd) +{ + ft_putstr_fd ("hello", fd); +} +static void +_s_putstr_empty (int fd) +{ + ft_putstr_fd ("", fd); +} + +_S_CRASH_V (ft_putstr_null, ft_putstr_fd (NULL, 1)) + +static void +_s_test_putstr_fd (void) +{ + char buf[256]; + + _s_section ("ft_putstr_fd"); + + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_putstr_null)); + + _s_read_pipe (_s_putstr_hello, buf, sizeof (buf)); + _s_check ("hello", strcmp (buf, "hello") == 0); + + { + int n = _s_read_pipe (_s_putstr_empty, buf, sizeof (buf)); + _s_check ("empty", n == 0); + } +} + +/* ====================================== + * ft_putendl_fd + * ====================================== */ + +static void +_s_putendl_hello (int fd) +{ + ft_putendl_fd ("hello", fd); +} +static void +_s_putendl_empty (int fd) +{ + ft_putendl_fd ("", fd); +} + +_S_CRASH_V (ft_putendl_null, ft_putendl_fd (NULL, 1)) + +static void +_s_test_putendl_fd (void) +{ + char buf[256]; + + _s_section ("ft_putendl_fd"); + + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_putendl_null)); + + _s_read_pipe (_s_putendl_hello, buf, sizeof (buf)); + _s_check ("hello\\n", strcmp (buf, "hello\n") == 0); + + _s_read_pipe (_s_putendl_empty, buf, sizeof (buf)); + _s_check ("empty\\n", strcmp (buf, "\n") == 0); +} + +/* ====================================== + * ft_putnbr_fd + * ====================================== */ + +static void +_s_putnbr_0 (int fd) +{ + ft_putnbr_fd (0, fd); +} +static void +_s_putnbr_42 (int fd) +{ + ft_putnbr_fd (42, fd); +} +static void +_s_putnbr_neg (int fd) +{ + ft_putnbr_fd (-42, fd); +} +static void +_s_putnbr_max (int fd) +{ + ft_putnbr_fd (INT_MAX, fd); +} +static void +_s_putnbr_min (int fd) +{ + ft_putnbr_fd (INT_MIN, fd); +} + +static void +_s_test_putnbr_fd (void) +{ + char buf[64]; + char expected[64]; + + _s_section ("ft_putnbr_fd"); + + _s_read_pipe (_s_putnbr_0, buf, sizeof (buf)); + _s_check ("zero", strcmp (buf, "0") == 0); + + _s_read_pipe (_s_putnbr_42, buf, sizeof (buf)); + _s_check ("positive", strcmp (buf, "42") == 0); + + _s_read_pipe (_s_putnbr_neg, buf, sizeof (buf)); + _s_check ("negative", strcmp (buf, "-42") == 0); + + _s_read_pipe (_s_putnbr_max, buf, sizeof (buf)); + snprintf (expected, sizeof (expected), "%d", INT_MAX); + _s_check ("INT_MAX", strcmp (buf, expected) == 0); + + _s_read_pipe (_s_putnbr_min, buf, sizeof (buf)); + snprintf (expected, sizeof (expected), "%d", INT_MIN); + _s_check ("INT_MIN", strcmp (buf, expected) == 0); +} + +int +main (void) +{ + _s_test_putchar_fd (); + _s_test_putstr_fd (); + _s_test_putendl_fd (); + _s_test_putnbr_fd (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_split.c b/tests/src/test_split.c new file mode 100644 index 0000000..55847a2 --- /dev/null +++ b/tests/src/test_split.c @@ -0,0 +1,234 @@ +#include "libft.h" +#include "test_utils.h" + +_S_CRASH (ft_split_null, ft_split (NULL, ' ')) + +static void +_s_free_split (char **arr) +{ + size_t i; + + if (!arr) + return; + i = 0; + while (arr[i]) + free (arr[i++]); + free (arr); +} + +static size_t +_s_arr_len (char **arr) +{ + size_t n; + + n = 0; + while (arr[n]) + n++; + return (n); +} + +static void +_s_test_split (void) +{ + int i; + char label[128]; + + _s_section ("ft_split"); + + /* NULL crashes */ + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_split_null)); + + /* basic split */ + { + char **arr = ft_split ("hello world foo", ' '); + _s_check ("basic count", arr && _s_arr_len (arr) == 3); + _s_check ("basic [0]", arr && strcmp (arr[0], "hello") == 0); + _s_check ("basic [1]", arr && strcmp (arr[1], "world") == 0); + _s_check ("basic [2]", arr && strcmp (arr[2], "foo") == 0); + _s_check ("basic NULL-term", arr && arr[3] == NULL); + _s_free_split (arr); + } + + /* leading delimiters */ + { + char **arr = ft_split (" hello", ' '); + _s_check ("leading delim count", arr && _s_arr_len (arr) == 1); + _s_check ("leading delim [0]", arr && strcmp (arr[0], "hello") == 0); + _s_free_split (arr); + } + + /* trailing delimiters */ + { + char **arr = ft_split ("hello ", ' '); + _s_check ("trailing delim count", arr && _s_arr_len (arr) == 1); + _s_check ("trailing delim [0]", arr && strcmp (arr[0], "hello") == 0); + _s_free_split (arr); + } + + /* consecutive delimiters */ + { + char **arr = ft_split ("a,,b,,c", ','); + _s_check ("consec count", arr && _s_arr_len (arr) == 3); + _s_check ("consec [0]", arr && strcmp (arr[0], "a") == 0); + _s_check ("consec [1]", arr && strcmp (arr[1], "b") == 0); + _s_check ("consec [2]", arr && strcmp (arr[2], "c") == 0); + _s_free_split (arr); + } + + /* no delimiter in string */ + { + char **arr = ft_split ("hello", ' '); + _s_check ("no delim count", arr && _s_arr_len (arr) == 1); + _s_check ("no delim [0]", arr && strcmp (arr[0], "hello") == 0); + _s_free_split (arr); + } + + /* empty string */ + { + char **arr = ft_split ("", ' '); + _s_check ("empty count", arr && _s_arr_len (arr) == 0); + _s_check ("empty NULL-term", arr && arr[0] == NULL); + _s_free_split (arr); + } + + /* string is only delimiters */ + { + char **arr = ft_split (" ", ' '); + _s_check ("only delims count", arr && _s_arr_len (arr) == 0); + _s_check ("only delims NULL-term", arr && arr[0] == NULL); + _s_free_split (arr); + } + + /* single character string, is delimiter */ + { + char **arr = ft_split (",", ','); + _s_check ("single delim count", arr && _s_arr_len (arr) == 0); + _s_free_split (arr); + } + + /* single character string, not delimiter */ + { + char **arr = ft_split ("a", ','); + _s_check ("single non-delim count", arr && _s_arr_len (arr) == 1); + _s_check ("single non-delim [0]", arr && strcmp (arr[0], "a") == 0); + _s_free_split (arr); + } + + /* delimiter '\0' — whole string is one word */ + { + char **arr = ft_split ("hello", '\0'); + _s_check ("nul delim count", arr && _s_arr_len (arr) == 1); + _s_check ("nul delim [0]", arr && strcmp (arr[0], "hello") == 0); + _s_free_split (arr); + } + + /* each word is independently allocated */ + { + char **arr = ft_split ("a b c", ' '); + _s_check ("independent ptrs", arr && arr[0] != arr[1] && arr[1] != arr[2]); + _s_free_split (arr); + } + + /* randomized: build string from words, split, verify */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int nwords = rand () % 10 + 1; + int pads = rand () % 5; + char **words = malloc (nwords * sizeof (char *)); + char *src; + char **arr; + size_t total; + int j, k; + int ok; + + if (!words) + continue; + + /* generate random words (letters only) */ + total = 0; + ok = 1; + for (j = 0; j < nwords; j++) + { + int wlen = rand () % 20 + 1; + words[j] = malloc (wlen + 1); + if (!words[j]) + { + ok = 0; + break; + } + for (k = 0; k < wlen; k++) + words[j][k] = 'A' + rand () % 26; + words[j][wlen] = '\0'; + total += wlen; + } + if (!ok) + { + for (k = 0; k < j; k++) + free (words[k]); + free (words); + continue; + } + + /* build: pads + word + delimiters + ... + pads */ + total += (nwords - 1) + 2 * pads; + src = malloc (total + 1); + if (!src) + { + for (j = 0; j < nwords; j++) + free (words[j]); + free (words); + continue; + } + + k = 0; + for (j = 0; j < pads; j++) + src[k++] = ','; + for (j = 0; j < nwords; j++) + { + size_t wlen = strlen (words[j]); + memcpy (src + k, words[j], wlen); + k += wlen; + if (j < nwords - 1) + src[k++] = ','; + } + for (j = 0; j < pads; j++) + src[k++] = ','; + src[k] = '\0'; + + arr = ft_split (src, ','); + snprintf (label, sizeof (label), "random nwords=%d pads=%d", nwords, + pads); + if (!arr || (int)_s_arr_len (arr) != nwords) + { + _s_check (label, 0); + } + else + { + int match = 1; + for (j = 0; j < nwords; j++) + { + if (strcmp (arr[j], words[j]) != 0) + { + match = 0; + break; + } + } + _s_check (label, match && arr[nwords] == NULL); + } + + for (j = 0; j < nwords; j++) + free (words[j]); + free (words); + free (src); + _s_free_split (arr); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_split (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_striteri.c b/tests/src/test_striteri.c new file mode 100644 index 0000000..0874c02 --- /dev/null +++ b/tests/src/test_striteri.c @@ -0,0 +1,105 @@ +#include "libft.h" +#include "test_utils.h" +#include <ctype.h> + +_S_CRASH_V (ft_striteri_null, ft_striteri (NULL, NULL)) + +static void +_s_to_upper (unsigned int i, char *c) +{ + (void)i; + *c = toupper (*c); +} + +static void +_s_add_index (unsigned int i, char *c) +{ + *c = *c + i; +} + +static void +_s_test_striteri (void) +{ + int i; + char label[128]; + + _s_section ("ft_striteri"); + + /* NULL crashes */ + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_striteri_null)); + + /* basic: to uppercase in place */ + { + char s[] = "hello"; + ft_striteri (s, _s_to_upper); + _s_check ("to_upper", strcmp (s, "HELLO") == 0); + } + + /* function receives correct index */ + { + char s[] = "aaaa"; + ft_striteri (s, _s_add_index); + _s_check ("index passed", + s[0] == 'a' && s[1] == 'b' && s[2] == 'c' && s[3] == 'd'); + } + + /* empty string: no crash, no change */ + { + char s[] = ""; + ft_striteri (s, _s_to_upper); + _s_check ("empty", strcmp (s, "") == 0); + } + + /* single character */ + { + char s[] = "a"; + ft_striteri (s, _s_to_upper); + _s_check ("single char", strcmp (s, "A") == 0); + } + + /* modifies in place (same pointer) */ + { + char s[] = "hello"; + char *orig = s; + ft_striteri (s, _s_to_upper); + _s_check ("in place", s == orig); + } + + /* randomized: apply to_upper, compare with manual */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int len = rand () % 200 + 1; + char *src = malloc (len + 1); + char *expected = malloc (len + 1); + int j; + + if (!src || !expected) + { + free (src); + free (expected); + continue; + } + for (j = 0; j < len; j++) + { + src[j] = 'a' + rand () % 26; + expected[j] = toupper (src[j]); + } + src[len] = '\0'; + expected[len] = '\0'; + + ft_striteri (src, _s_to_upper); + snprintf (label, sizeof (label), "random len=%d", len); + _s_check (label, strcmp (src, expected) == 0); + free (src); + free (expected); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_striteri (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_strjoin.c b/tests/src/test_strjoin.c new file mode 100644 index 0000000..023f84d --- /dev/null +++ b/tests/src/test_strjoin.c @@ -0,0 +1,111 @@ +#include "libft.h" +#include "test_utils.h" + +_S_CRASH (ft_strjoin_null_s1, ft_strjoin (NULL, "hello")) +_S_CRASH (ft_strjoin_null_s2, ft_strjoin ("hello", NULL)) +_S_CRASH (ft_strjoin_null_both, ft_strjoin (NULL, NULL)) + +static void +_s_test_strjoin (void) +{ + int i; + char label[128]; + + _s_section ("ft_strjoin"); + + /* NULL crashes */ + _s_check ("NULL s1 crashes", _s_crashes (_s_crash_ft_strjoin_null_s1)); + _s_check ("NULL s2 crashes", _s_crashes (_s_crash_ft_strjoin_null_s2)); + _s_check ("NULL both crashes", _s_crashes (_s_crash_ft_strjoin_null_both)); + + /* two normal strings */ + { + char *p = ft_strjoin ("hello ", "world"); + _s_check ("basic", p && strcmp (p, "hello world") == 0); + free (p); + } + + /* first string empty */ + { + char *p = ft_strjoin ("", "world"); + _s_check ("s1 empty", p && strcmp (p, "world") == 0); + free (p); + } + + /* second string empty */ + { + char *p = ft_strjoin ("hello", ""); + _s_check ("s2 empty", p && strcmp (p, "hello") == 0); + free (p); + } + + /* both empty */ + { + char *p = ft_strjoin ("", ""); + _s_check ("both empty", p && strcmp (p, "") == 0); + free (p); + } + + /* returns independent copy */ + { + char s1[] = "abc"; + char s2[] = "def"; + char *p = ft_strjoin (s1, s2); + _s_check ("independent ptr", p && p != s1 && p != s2); + s1[0] = 'X'; + _s_check ("mutation safe", p[0] == 'a'); + free (p); + } + + /* randomized */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int len1 = rand () % 200 + 1; + int len2 = rand () % 200 + 1; + char *s1 = malloc (len1 + 1); + char *s2 = malloc (len2 + 1); + char *expected; + char *p; + int j; + + if (!s1 || !s2) + { + free (s1); + free (s2); + continue; + } + for (j = 0; j < len1; j++) + s1[j] = 'A' + rand () % 26; + s1[len1] = '\0'; + for (j = 0; j < len2; j++) + s2[j] = 'a' + rand () % 26; + s2[len2] = '\0'; + + expected = malloc (len1 + len2 + 1); + if (!expected) + { + free (s1); + free (s2); + continue; + } + memcpy (expected, s1, len1); + memcpy (expected + len1, s2, len2 + 1); + + p = ft_strjoin (s1, s2); + snprintf (label, sizeof (label), "random len1=%d len2=%d", len1, len2); + _s_check (label, p && strcmp (p, expected) == 0); + free (s1); + free (s2); + free (expected); + free (p); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_strjoin (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_strmapi.c b/tests/src/test_strmapi.c new file mode 100644 index 0000000..31dba65 --- /dev/null +++ b/tests/src/test_strmapi.c @@ -0,0 +1,108 @@ +#include "libft.h" +#include "test_utils.h" +#include <ctype.h> + +_S_CRASH (ft_strmapi_null, ft_strmapi (NULL, NULL)) + +static char +_s_to_upper (unsigned int i, char c) +{ + (void)i; + return (toupper (c)); +} + +static char +_s_add_index (unsigned int i, char c) +{ + return (c + i); +} + +static void +_s_test_strmapi (void) +{ + int i; + char label[128]; + + _s_section ("ft_strmapi"); + + /* NULL crashes */ + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_strmapi_null)); + + /* basic: to uppercase */ + { + char *p = ft_strmapi ("hello", _s_to_upper); + _s_check ("to_upper", p && strcmp (p, "HELLO") == 0); + free (p); + } + + /* function receives correct index */ + { + char *p = ft_strmapi ("aaaa", _s_add_index); + _s_check ("index passed", + p && p[0] == 'a' && p[1] == 'b' && p[2] == 'c' && p[3] == 'd'); + free (p); + } + + /* empty string */ + { + char *p = ft_strmapi ("", _s_to_upper); + _s_check ("empty", p && strcmp (p, "") == 0); + free (p); + } + + /* returns independent copy */ + { + char s[] = "hello"; + char *p = ft_strmapi (s, _s_to_upper); + _s_check ("independent ptr", p && p != s); + _s_check ("original unchanged", strcmp (s, "hello") == 0); + free (p); + } + + /* single character */ + { + char *p = ft_strmapi ("a", _s_to_upper); + _s_check ("single char", p && strcmp (p, "A") == 0); + free (p); + } + + /* randomized: apply to_upper, compare with manual */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int len = rand () % 200 + 1; + char *src = malloc (len + 1); + char *expected = malloc (len + 1); + char *p; + int j; + + if (!src || !expected) + { + free (src); + free (expected); + continue; + } + for (j = 0; j < len; j++) + { + src[j] = 'a' + rand () % 26; + expected[j] = toupper (src[j]); + } + src[len] = '\0'; + expected[len] = '\0'; + + p = ft_strmapi (src, _s_to_upper); + snprintf (label, sizeof (label), "random len=%d", len); + _s_check (label, p && strcmp (p, expected) == 0); + free (src); + free (expected); + free (p); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_strmapi (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_strtrim.c b/tests/src/test_strtrim.c new file mode 100644 index 0000000..aa57ef7 --- /dev/null +++ b/tests/src/test_strtrim.c @@ -0,0 +1,162 @@ +#include "libft.h" +#include "test_utils.h" + +_S_CRASH (ft_strtrim_null_s1, ft_strtrim (NULL, " ")) +_S_CRASH (ft_strtrim_null_set, ft_strtrim ("hello", NULL)) +_S_CRASH (ft_strtrim_null_both, ft_strtrim (NULL, NULL)) + +static void +_s_test_strtrim (void) +{ + int i; + char label[128]; + + _s_section ("ft_strtrim"); + + /* NULL crashes */ + _s_check ("NULL s1 crashes", _s_crashes (_s_crash_ft_strtrim_null_s1)); + _s_check ("NULL set crashes", _s_crashes (_s_crash_ft_strtrim_null_set)); + _s_check ("NULL both crashes", _s_crashes (_s_crash_ft_strtrim_null_both)); + + /* trim spaces from both ends */ + { + char *p = ft_strtrim (" hello ", " "); + _s_check ("spaces both ends", p && strcmp (p, "hello") == 0); + free (p); + } + + /* trim multiple characters */ + { + char *p = ft_strtrim ("xxhelloxx", "x"); + _s_check ("single trim char", p && strcmp (p, "hello") == 0); + free (p); + } + + /* trim set with multiple chars */ + { + char *p = ft_strtrim (".-hello-.", ".-"); + _s_check ("multi-char set", p && strcmp (p, "hello") == 0); + free (p); + } + + /* nothing to trim */ + { + char *p = ft_strtrim ("hello", " "); + _s_check ("nothing to trim", p && strcmp (p, "hello") == 0); + free (p); + } + + /* everything trimmed */ + { + char *p = ft_strtrim ("aaaa", "a"); + _s_check ("all trimmed", p && strcmp (p, "") == 0); + free (p); + } + + /* empty string */ + { + char *p = ft_strtrim ("", "abc"); + _s_check ("empty s1", p && strcmp (p, "") == 0); + free (p); + } + + /* empty set */ + { + char *p = ft_strtrim (" hello ", ""); + _s_check ("empty set", p && strcmp (p, " hello ") == 0); + free (p); + } + + /* trim only leading */ + { + char *p = ft_strtrim ("xxhello", "x"); + _s_check ("leading only", p && strcmp (p, "hello") == 0); + free (p); + } + + /* trim only trailing */ + { + char *p = ft_strtrim ("helloxx", "x"); + _s_check ("trailing only", p && strcmp (p, "hello") == 0); + free (p); + } + + /* set chars in the middle are preserved */ + { + char *p = ft_strtrim (" hello world ", " "); + _s_check ("middle preserved", p && strcmp (p, "hello world") == 0); + free (p); + } + + /* single character string, in set */ + { + char *p = ft_strtrim ("x", "x"); + _s_check ("single char trimmed", p && strcmp (p, "") == 0); + free (p); + } + + /* single character string, not in set */ + { + char *p = ft_strtrim ("x", "y"); + _s_check ("single char kept", p && strcmp (p, "x") == 0); + free (p); + } + + /* returns independent copy */ + { + char s1[] = "hello"; + char *p = ft_strtrim (s1, ""); + _s_check ("independent ptr", p && p != s1); + free (p); + } + + /* randomized */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int core_len = rand () % 100 + 1; + int pad_len = rand () % 20; + char *src; + char *p; + char *expected; + int j; + + src = malloc (pad_len + core_len + pad_len + 1); + expected = malloc (core_len + 1); + if (!src || !expected) + { + free (src); + free (expected); + continue; + } + + /* pad with spaces, core with letters */ + for (j = 0; j < pad_len; j++) + src[j] = ' '; + for (j = 0; j < core_len; j++) + { + src[pad_len + j] = 'A' + rand () % 26; + expected[j] = src[pad_len + j]; + } + expected[core_len] = '\0'; + for (j = 0; j < pad_len; j++) + src[pad_len + core_len + j] = ' '; + src[pad_len + core_len + pad_len] = '\0'; + + p = ft_strtrim (src, " "); + snprintf (label, sizeof (label), "random core=%d pad=%d", core_len, + pad_len); + _s_check (label, p && strcmp (p, expected) == 0); + free (src); + free (expected); + free (p); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_strtrim (); + _s_print_results (); + return (_s_fail != 0); +} diff --git a/tests/src/test_substr.c b/tests/src/test_substr.c new file mode 100644 index 0000000..30e59ce --- /dev/null +++ b/tests/src/test_substr.c @@ -0,0 +1,135 @@ +#include "libft.h" +#include "test_utils.h" + +_S_CRASH (ft_substr_null, ft_substr (NULL, 0, 5)) + +static void +_s_test_substr (void) +{ + int i; + char label[128]; + + _s_section ("ft_substr"); + + /* NULL crashes */ + _s_check ("NULL crashes", _s_crashes (_s_crash_ft_substr_null)); + + /* basic substring from the middle */ + { + char *p = ft_substr ("hello world", 6, 5); + _s_check ("middle", p && strcmp (p, "world") == 0); + free (p); + } + + /* from the start */ + { + char *p = ft_substr ("hello", 0, 3); + _s_check ("from start", p && strcmp (p, "hel") == 0); + free (p); + } + + /* len larger than remaining string */ + { + char *p = ft_substr ("hello", 2, 100); + _s_check ("len > remaining", p && strcmp (p, "llo") == 0); + free (p); + } + + /* start beyond string length */ + { + char *p = ft_substr ("hello", 10, 5); + _s_check ("start > slen", p && strcmp (p, "") == 0); + free (p); + } + + /* start == string length */ + { + char *p = ft_substr ("hello", 5, 5); + _s_check ("start == slen", p && strcmp (p, "") == 0); + free (p); + } + + /* empty source string */ + { + char *p = ft_substr ("", 0, 10); + _s_check ("empty src", p && strcmp (p, "") == 0); + free (p); + } + + /* len == 0 */ + { + char *p = ft_substr ("hello", 0, 0); + _s_check ("len=0", p && strcmp (p, "") == 0); + free (p); + } + + /* full string copy */ + { + char *p = ft_substr ("hello", 0, 5); + _s_check ("full copy", p && strcmp (p, "hello") == 0); + free (p); + } + + /* single character */ + { + char *p = ft_substr ("hello", 4, 1); + _s_check ("single char", p && strcmp (p, "o") == 0); + free (p); + } + + /* returns independent copy */ + { + char src[] = "hello"; + char *p = ft_substr (src, 0, 5); + _s_check ("independent ptr", p && p != src); + free (p); + } + + /* randomized */ + for (i = 0; i < _S_RAND_ITERS; i++) + { + int slen = rand () % 200 + 1; + char *src = malloc (slen + 1); + unsigned int start; + size_t len; + char *p; + int j; + + if (!src) + continue; + for (j = 0; j < slen; j++) + src[j] = 'A' + rand () % 26; + src[slen] = '\0'; + + start = rand () % (slen + 5); + len = rand () % (slen + 5); + p = ft_substr (src, start, len); + + if (start >= (unsigned int)slen) + { + snprintf (label, sizeof (label), "random start=%u >= slen=%d", start, + slen); + _s_check (label, p && strcmp (p, "") == 0); + } + else + { + size_t remaining = slen - start; + size_t expected = remaining < len ? remaining : len; + snprintf (label, sizeof (label), "random start=%u len=%zu slen=%d", + start, len, slen); + _s_check (label, p && strlen (p) == expected + && strncmp (p, src + start, expected) == 0); + } + free (p); + free (src); + } +} + +int +main (void) +{ + srand (time (NULL)); + _s_test_substr (); + _s_print_results (); + return (_s_fail != 0); +} |
