diff --git a/tests/str8/test_str8.c b/tests/str8/test_str8.c
index 0784bad..79dd88e 100644
--- a/tests/str8/test_str8.c
+++ b/tests/str8/test_str8.c
@@ -6,6 +6,8 @@
 #include "tester.h"
 #include <stdbool.h>
 
+#define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0]))
+
 TestFuncResult test_str8_lit(void) {
   bool result;
 
@@ -235,6 +237,47 @@ TestFuncResult test_str8_substr(void) {
   return wapp_tester_result(result);
 }
 
+TestFuncResult test_str8_concat(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str     = wapp_str8_lit("Hello world");
+  Str8 suffix1 = wapp_str8_lit(" from me.");
+  Str8 suffix2 = wapp_str8_lit(" This is my code.");
+  Str8 concat1 = wapp_str8_lit("Hello world from me.");
+  Str8 concat2 = wapp_str8_lit("Hello world from me. This is my code.");
+
+  Str8 *output;
+
+  output = wapp_str8_concat(&arena, &str, &suffix1);
+  result = output->size == concat1.size && wapp_str8_equal(output, &concat1);
+
+  output = wapp_str8_concat(&arena, output, &suffix2);
+  result = result && output->size == concat2.size && wapp_str8_equal(output, &concat2);
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
+
+TestFuncResult test_str8_concat_capped(void) {
+  bool result;
+
+  Str8 str     = wapp_str8_lit("Hello world");
+  Str8 suffix1 = wapp_str8_lit(" from me.");
+  Str8 suffix2 = wapp_str8_lit(" This is my code.");
+  Str8 concat1 = wapp_str8_lit("Hello world from me.");
+  Str8 concat2 = wapp_str8_lit("Hello world from me. T");
+
+  wapp_str8_concat_capped(&str, &suffix1);
+  result = str.size == concat1.size && wapp_str8_equal(&str, &concat1);
+
+  wapp_str8_concat_capped(&str, &suffix2);
+  result = result && str.size == concat2.size && wapp_str8_equal(&str, &concat2);
+
+  return wapp_tester_result(result);
+}
+
 TestFuncResult test_str8_find(void) {
   bool result;
   Str8RO s = wapp_str8_lit("Do as I say, not as I do");
@@ -266,3 +309,157 @@ TestFuncResult test_str8_rfind(void) {
 
   return wapp_tester_result(result);
 }
+
+TestFuncResult test_str8_split(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str        = wapp_str8_lit("hello world from me");
+  Str8 delim1     = wapp_str8_lit(" ");
+  Str8 delim2     = wapp_str8_lit("from");
+  Str8List *list1 = wapp_str8_split(&arena, &str, &delim1);
+  Str8List *list2 = wapp_str8_split(&arena, &str, &delim2);
+
+  Str8RO splits1[] = {
+    wapp_str8_substr(&str, 0, 5),
+    wapp_str8_substr(&str, 6, 11),
+    wapp_str8_substr(&str, 12, 16),
+    wapp_str8_substr(&str, 17, 19),
+  };
+  Str8RO splits2[] = {
+    wapp_str8_substr(&str, 0, 12),
+    wapp_str8_substr(&str, 16, 19),
+  };
+
+  u64 count1 = ARRLEN(splits1);
+  u64 count2 = ARRLEN(splits2);
+
+  result = list1->node_count == count1 && list1->total_size == str.size - 3;
+  result = result && list2->node_count == count2 && list2->total_size == str.size - 4;
+  for (u64 i = 0; i < count1; ++i) {
+    Str8Node *node = wapp_str8_list_get(list1, i);
+    result = result && wapp_str8_equal(node->string, &(splits1[i]));
+  }
+  for (u64 i = 0; i < count2; ++i) {
+    Str8Node *node = wapp_str8_list_get(list2, i);
+    result = result && wapp_str8_equal(node->string, &(splits2[i]));
+  }
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
+
+TestFuncResult test_str8_split_with_max(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str       = wapp_str8_lit("hello world from me");
+  Str8 delim     = wapp_str8_lit(" ");
+  Str8List *list = wapp_str8_split_with_max(&arena, &str, &delim, 2);
+
+  Str8RO splits[] = {
+    wapp_str8_substr(&str, 0, 5),
+    wapp_str8_substr(&str, 6, 11),
+    wapp_str8_substr(&str, 12, 19),
+  };
+
+  u64 count = ARRLEN(splits);
+
+  result = list->node_count == count && list->total_size == str.size - 2;
+  for (u64 i = 0; i < count; ++i) {
+    Str8Node *node = wapp_str8_list_get(list, i);
+    result = result && wapp_str8_equal(node->string, &(splits[i]));
+  }
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
+
+TestFuncResult test_str8_rsplit(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str        = wapp_str8_lit("hello world from me");
+  Str8 delim1     = wapp_str8_lit(" ");
+  Str8 delim2     = wapp_str8_lit("from");
+  Str8List *list1 = wapp_str8_rsplit(&arena, &str, &delim1);
+  Str8List *list2 = wapp_str8_rsplit(&arena, &str, &delim2);
+
+  Str8RO splits1[] = {
+    wapp_str8_substr(&str, 0, 5),
+    wapp_str8_substr(&str, 6, 11),
+    wapp_str8_substr(&str, 12, 16),
+    wapp_str8_substr(&str, 17, 19),
+  };
+  Str8RO splits2[] = {
+    wapp_str8_substr(&str, 0, 12),
+    wapp_str8_substr(&str, 16, 19),
+  };
+
+  u64 count1 = ARRLEN(splits1);
+  u64 count2 = ARRLEN(splits2);
+
+  result = list1->node_count == count1 && list1->total_size == str.size - 3;
+  result = result && list2->node_count == count2 && list2->total_size == str.size - 4;
+  for (u64 i = 0; i < count1; ++i) {
+    Str8Node *node = wapp_str8_list_get(list1, i);
+    result = result && wapp_str8_equal(node->string, &(splits1[i]));
+  }
+  for (u64 i = 0; i < count2; ++i) {
+    Str8Node *node = wapp_str8_list_get(list2, i);
+    result = result && wapp_str8_equal(node->string, &(splits2[i]));
+  }
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
+
+TestFuncResult test_str8_rsplit_with_max(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str       = wapp_str8_lit("hello world from me");
+  Str8 delim     = wapp_str8_lit(" ");
+  Str8List *list = wapp_str8_rsplit_with_max(&arena, &str, &delim, 2);
+
+  Str8RO splits[] = {
+    wapp_str8_substr(&str, 0, 11),
+    wapp_str8_substr(&str, 12, 16),
+    wapp_str8_substr(&str, 17, 19),
+  };
+
+  u64 count = ARRLEN(splits);
+
+  result = list->node_count == count && list->total_size == str.size - 2;
+  for (u64 i = 0; i < count; ++i) {
+    Str8Node *node = wapp_str8_list_get(list, i);
+    result = result && wapp_str8_equal(node->string, &(splits[i]));
+  }
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
+
+TestFuncResult test_str8_join(void) {
+  bool result;
+  Allocator arena = wapp_mem_arena_allocator_init(MB(10));
+
+  Str8 str        = wapp_str8_lit("hello world from me");
+  Str8 delim1     = wapp_str8_lit(" ");
+  Str8 delim2     = wapp_str8_lit("from");
+  Str8List *list1 = wapp_str8_rsplit(&arena, &str, &delim1);
+  Str8List *list2 = wapp_str8_rsplit(&arena, &str, &delim2);
+  Str8 *join1     = wapp_str8_join(&arena, list1, &delim1);
+  Str8 *join2     = wapp_str8_join(&arena, list2, &delim2);
+
+  result = join1->size == str.size && wapp_str8_equal(join1, &str);
+  result = result && join2->size == str.size && wapp_str8_equal(join2, &str);
+
+  wapp_mem_arena_allocator_destroy(&arena);
+
+  return wapp_tester_result(result);
+}
diff --git a/tests/str8/test_str8.h b/tests/str8/test_str8.h
index 956ff22..025ec52 100644
--- a/tests/str8/test_str8.h
+++ b/tests/str8/test_str8.h
@@ -18,8 +18,15 @@ TestFuncResult test_str8_get_index_out_of_bounds(void);
 TestFuncResult test_str8_set(void);
 TestFuncResult test_str8_equal(void);
 TestFuncResult test_str8_substr(void);
+TestFuncResult test_str8_concat(void);
+TestFuncResult test_str8_concat_capped(void);
 TestFuncResult test_str8_find(void);
 TestFuncResult test_str8_rfind(void);
+TestFuncResult test_str8_split(void);
+TestFuncResult test_str8_split_with_max(void);
+TestFuncResult test_str8_rsplit(void);
+TestFuncResult test_str8_rsplit_with_max(void);
+TestFuncResult test_str8_join(void);
 
 #ifdef __cplusplus
 END_C_LINKAGE
diff --git a/tests/wapptest.c b/tests/wapptest.c
index 5bc736d..805a483 100644
--- a/tests/wapptest.c
+++ b/tests/wapptest.c
@@ -24,8 +24,15 @@ int main(void) {
                         test_str8_set,
                         test_str8_equal,
                         test_str8_substr,
+                        test_str8_concat,
+                        test_str8_concat_capped,
                         test_str8_find,
                         test_str8_rfind,
+                        test_str8_split,
+                        test_str8_split_with_max,
+                        test_str8_rsplit,
+                        test_str8_rsplit_with_max,
+                        test_str8_join,
                         test_str8_list_get,
                         test_str8_list_push_front,
                         test_str8_list_push_back,