diff --git a/tests/main_tests.c b/tests/main_tests.c
index 0a3f520..d8e0c56 100644
--- a/tests/main_tests.c
+++ b/tests/main_tests.c
@@ -1,26 +1,51 @@
 #include "pl_ds_tests.h"
 #include "pl_json_tests.h"
+#include "pl_memory_tests.h"
+#include "pl_string_tests.h"
 
 int main()
 {
-    plTestContext* ptTestContext = pl_create_test_context();
-    
-    // data structure tests
-    pl_test_register_test(hashmap_test_0, NULL);
+    // create test context
+    plTestOptions tOptions = {
+        .bPrintSuiteResults = true,
+        .bPrintColor = true
+    };
+    plTestContext* ptTestContext = pl_create_test_context(tOptions);
 
-    // json tests
-    pl_test_register_test(json_test_0, NULL);
+    // pl_json.h tests
+    pl_json_tests(NULL);
+    pl_test_run_suite("pl_json.h");
 
-    if(!pl_test_run())
+    // pl_memory.h tests
+    pl_memory_tests(NULL);
+    pl_test_run_suite("pl_memory.h");
+
+    // pl_ds.h tests
+    pl_ds_tests(NULL);
+    pl_test_run_suite("pl_ds.h");
+
+    // pl_string.h tests
+    pl_string_tests(NULL);
+    pl_test_run_suite("pl_string.h");
+
+    bool bResult = pl_test_finish();
+
+    if(!bResult)
     {
         exit(1);
     }
-
     return 0;
 }
 
-#define PL_TEST_IMPLEMENTATION
-#include "pl_test.h"
-
 #define PL_JSON_IMPLEMENTATION
-#include "pl_json.h"
\ No newline at end of file
+#include "pl_json.h"
+
+#define PL_MEMORY_IMPLEMENTATION
+#include "pl_memory.h"
+
+#define PL_STRING_IMPLEMENTATION
+#include "pl_string.h"
+
+#define PL_TEST_WIN32_COLOR
+#define PL_TEST_IMPLEMENTATION
+#include "pl_test.h"
\ No newline at end of file
diff --git a/tests/pl_ds_tests.h b/tests/pl_ds_tests.h
index e82e731..49e6e3e 100644
--- a/tests/pl_ds_tests.h
+++ b/tests/pl_ds_tests.h
@@ -6,97 +6,104 @@
 #include <stdint.h>
 #include "pl_ds.h"
 
-static void
+void
 hashmap_test_0(void* pData)
 {
-    // hashmap 0
+    plHashMap* ptHashMap = NULL;
+
+    int* sbiValues = NULL;
+    pl_sb_push(sbiValues, 0);
+    pl_sb_push(sbiValues, 69);
+    pl_hm_insert(ptHashMap, pl_hm_hash_str("Dirty Number"), pl_sb_size(sbiValues) - 1);
+    pl_sb_push(sbiValues, 117);
+    pl_hm_insert(ptHashMap, pl_hm_hash_str("Spartan Number"), pl_sb_size(sbiValues) - 1);
+
+    for(uint32_t i = 0; i < 3000; i++)
     {
-        plHashMap tHashMap = {0};
-
-        int* sbiValues = NULL;
-        pl_sb_push(sbiValues, 0);
-        pl_sb_push(sbiValues, 69);
-        pl_hm_insert(&tHashMap, pl_hm_hash_str("Dirty Number"), pl_sb_size(sbiValues) - 1);
-        pl_sb_push(sbiValues, 117);
-        pl_hm_insert(&tHashMap, pl_hm_hash_str("Spartan Number"), pl_sb_size(sbiValues) - 1);
-
-        for(uint32_t i = 0; i < 3000; i++)
-        {
-            pl_sb_push(sbiValues, i);
-            pl_hm_insert(&tHashMap, pl_hm_hash("Spartan Number2", strlen("Spartan Number2"), i), pl_sb_size(sbiValues) - 1);
-        }
-
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Dirty Number"))], 69, NULL);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL);
-
-        pl_hm_remove(&tHashMap, pl_hm_hash_str("Dirty Number"));
-
-        uint64_t ulFreeIndex = pl_hm_get_free_index(&tHashMap);
-        if(ulFreeIndex == UINT64_MAX)
-        {
-            pl_sb_add(sbiValues);
-            ulFreeIndex = pl_sb_size(sbiValues) - 1;
-        }
-        sbiValues[ulFreeIndex] = 666999;
-        pl_hm_insert(&tHashMap, pl_hm_hash_str("Extra dirty number"), ulFreeIndex);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL);
-
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL);
-
-        pl_hm_free(&tHashMap);
-        pl_sb_free(sbiValues);
+        pl_sb_push(sbiValues, i);
+        pl_hm_insert(ptHashMap, pl_hm_hash("Spartan Number2", strlen("Spartan Number2"), i), pl_sb_size(sbiValues) - 1);
     }
 
-    // hashmap 1
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup(ptHashMap, pl_hm_hash_str("Dirty Number"))], 69, NULL);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup(ptHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL);
+
+    pl_hm_remove(ptHashMap, pl_hm_hash_str("Dirty Number"));
+
+    uint64_t ulFreeIndex = pl_hm_get_free_index(ptHashMap);
+    if(ulFreeIndex == UINT64_MAX)
     {
-        plHashMap tHashMap = {0};
+        pl_sb_add(sbiValues);
+        ulFreeIndex = pl_sb_size(sbiValues) - 1;
+    }
+    sbiValues[ulFreeIndex] = 666999;
+    pl_hm_insert(ptHashMap, pl_hm_hash_str("Extra dirty number"), ulFreeIndex);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup(ptHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL);
 
-        pl_hm_insert(&tHashMap, pl_hm_hash_str("Dirty Number"), 69);
-        pl_hm_insert(&tHashMap, pl_hm_hash_str("Spartan Number"), 117);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup(ptHashMap, pl_hm_hash_str("Extra dirty number"))], 666999, NULL);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup(ptHashMap, pl_hm_hash_str("Spartan Number"))], 117, NULL);
 
-        pl_test_expect_int_equal((int)pl_hm_lookup(&tHashMap, pl_hm_hash_str("Dirty Number")), 69, NULL);
-        pl_test_expect_int_equal((int)pl_hm_lookup(&tHashMap, pl_hm_hash_str("Spartan Number")), 117, NULL);
+    pl_hm_free(ptHashMap);
+    pl_sb_free(sbiValues);
+}
 
-        pl_hm_free(&tHashMap);
+void
+hashmap_test_1(void* pData)
+{
+    plHashMap* ptHashMap = NULL;
+
+    pl_hm_insert(ptHashMap, pl_hm_hash_str("Dirty Number"), 69);
+    pl_hm_insert(ptHashMap, pl_hm_hash_str("Spartan Number"), 117);
+
+    pl_test_expect_int_equal((int)pl_hm_lookup(ptHashMap, pl_hm_hash_str("Dirty Number")), 69, NULL);
+    pl_test_expect_int_equal((int)pl_hm_lookup(ptHashMap, pl_hm_hash_str("Spartan Number")), 117, NULL);
+
+    pl_hm_free(ptHashMap);
+}
+
+void
+hashmap_test_2(void* pData)
+{
+    plHashMap* ptHashMap = NULL;
+
+    int* sbiValues = NULL;
+    pl_sb_push(sbiValues, 0);
+    pl_sb_push(sbiValues, 69);
+    pl_hm_insert_str(ptHashMap, "Dirty Number", pl_sb_size(sbiValues) - 1);
+    pl_sb_push(sbiValues, 117);
+    pl_hm_insert_str(ptHashMap, "Spartan Number", pl_sb_size(sbiValues) - 1);
+
+    for(uint32_t i = 0; i < 79; i++)
+    {
+        pl_sb_push(sbiValues, 118);
+        pl_hm_insert_str(ptHashMap, "Spartan Number2", pl_sb_size(sbiValues) - 1);
     }
 
-    // hashmap 2
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(ptHashMap, "Dirty Number")], 69, NULL);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(ptHashMap, "Spartan Number")], 117, NULL);
+
+    pl_hm_remove_str(ptHashMap, "Dirty Number");
+
+    uint64_t ulFreeIndex = pl_hm_get_free_index(ptHashMap);
+    if(ulFreeIndex == UINT64_MAX)
     {
-        plHashMap tHashMap = {0};
-
-        int* sbiValues = NULL;
-        pl_sb_push(sbiValues, 0);
-        pl_sb_push(sbiValues, 69);
-        pl_hm_insert_str(&tHashMap, "Dirty Number", pl_sb_size(sbiValues) - 1);
-        pl_sb_push(sbiValues, 117);
-        pl_hm_insert_str(&tHashMap, "Spartan Number", pl_sb_size(sbiValues) - 1);
-
-        for(uint32_t i = 0; i < 79; i++)
-        {
-            pl_sb_push(sbiValues, 118);
-            pl_hm_insert_str(&tHashMap, "Spartan Number2", pl_sb_size(sbiValues) - 1);
-        }
-
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Dirty Number")], 69, NULL);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Spartan Number")], 117, NULL);
-
-        pl_hm_remove_str(&tHashMap, "Dirty Number");
-
-        uint64_t ulFreeIndex = pl_hm_get_free_index(&tHashMap);
-        if(ulFreeIndex == UINT64_MAX)
-        {
-            pl_sb_add(sbiValues);
-            ulFreeIndex = pl_sb_size(sbiValues) - 1;
-        }
-        sbiValues[ulFreeIndex] = 666999;
-        pl_hm_insert_str(&tHashMap, "Extra dirty number", ulFreeIndex);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Extra dirty number")], 666999, NULL);
-
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Extra dirty number")], 666999, NULL);
-        pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(&tHashMap, "Spartan Number")], 117, NULL);
-
-        pl_hm_free(&tHashMap);
-        pl_sb_free(sbiValues);
+        pl_sb_add(sbiValues);
+        ulFreeIndex = pl_sb_size(sbiValues) - 1;
     }
+    sbiValues[ulFreeIndex] = 666999;
+    pl_hm_insert_str(ptHashMap, "Extra dirty number", ulFreeIndex);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(ptHashMap, "Extra dirty number")], 666999, NULL);
+
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(ptHashMap, "Extra dirty number")], 666999, NULL);
+    pl_test_expect_int_equal(sbiValues[pl_hm_lookup_str(ptHashMap, "Spartan Number")], 117, NULL);
+
+    pl_hm_free(ptHashMap);
+    pl_sb_free(sbiValues);
+}
+
+void
+pl_ds_tests(void* pData)
+{
+    pl_test_register_test(hashmap_test_0, NULL);
+    pl_test_register_test(hashmap_test_1, NULL);
+    pl_test_register_test(hashmap_test_2, NULL);
 }
\ No newline at end of file
diff --git a/tests/pl_json_tests.h b/tests/pl_json_tests.h
index 94f6ea6..530125e 100644
--- a/tests/pl_json_tests.h
+++ b/tests/pl_json_tests.h
@@ -6,166 +6,176 @@
 #include <stdint.h>
 #include "pl_json.h"
 
-static void
-json_test_0(void* pData)
+void
+write_json_test(void* pData)
 {
 
-    char* pcBuffer = NULL;
+    char** ppcBuffer = pData;
 
-    // write json
-    {
+    // root object
+    plJsonObject* ptRootJsonObject = pl_json_new_root_object("ROOT");
+    pl_json_add_string_member(ptRootJsonObject, "first name", "John");
+    pl_json_add_string_member(ptRootJsonObject, "last name", "Doe");
+    pl_json_add_int_member(ptRootJsonObject, "age", 40);
+    pl_json_add_bool_member(ptRootJsonObject, "tall", false);
+    pl_json_add_bool_member(ptRootJsonObject, "hungry", true);
+    int aScores[] = {100, 86, 46};
+    pl_json_add_int_array(ptRootJsonObject, "scores", aScores, 3);
 
-        // root object
-        plJsonObject tRootJsonObject = {0};
-        pl_json_add_string_member(&tRootJsonObject, "first name", "John");
-        pl_json_add_string_member(&tRootJsonObject, "last name", "Doe");
-        pl_json_add_int_member(&tRootJsonObject, "age", 40);
-        pl_json_add_bool_member(&tRootJsonObject, "tall", false);
-        pl_json_add_bool_member(&tRootJsonObject, "hungry", true);
-        int aScores[] = {100, 86, 46};
-        pl_json_add_int_array(&tRootJsonObject, "scores", aScores, 3);
+    char* aPets[] = {"Riley", "Luna", "Chester"};
+    pl_json_add_string_array(ptRootJsonObject, "pets", aPets, 3);
 
-        char* aPets[] = {"Riley", "Luna", "Chester"};
-        pl_json_add_string_array(&tRootJsonObject, "pets", aPets, 3);
+    // member object
 
-        // member object
-        plJsonObject tBestFriend = {0};
-        pl_json_add_string_member(&tBestFriend, "first name", "John");
-        pl_json_add_string_member(&tBestFriend, "last name", "Doe");
-        pl_json_add_int_member(&tBestFriend, "age", 40);
-        pl_json_add_bool_member(&tBestFriend, "tall", false);
-        pl_json_add_bool_member(&tBestFriend, "hungry", true);
-        pl_json_add_string_array(&tBestFriend, "pets", aPets, 3);
-        pl_json_add_int_array(&tBestFriend, "scores", aScores, 3);
+    plJsonObject* ptBestFriend = pl_json_add_member(ptRootJsonObject, "best friend");
+    pl_json_add_string_member(ptBestFriend, "first name", "John");
+    pl_json_add_string_member(ptBestFriend, "last name", "Doe");
+    pl_json_add_int_member(ptBestFriend, "age", 40);
+    pl_json_add_bool_member(ptBestFriend, "tall", false);
+    pl_json_add_bool_member(ptBestFriend, "hungry", true);
+    pl_json_add_string_array(ptBestFriend, "pets", aPets, 3);
+    pl_json_add_int_array(ptBestFriend, "scores", aScores, 3);
 
-        pl_json_add_member(&tRootJsonObject, "best friend", &tBestFriend);
+    // friend member object
+    plJsonObject* ptFriends = pl_json_add_member_array(ptRootJsonObject, "friends", 2);
 
-        // friend member object
-        plJsonObject atFriends[2] = {0};
-        int aScores0[] = {88, 86, 100};
-        pl_json_add_string_member(&atFriends[0], "first name", "Jacob");
-        pl_json_add_string_member(&atFriends[0], "last name", "Smith");
-        pl_json_add_int_member(&atFriends[0], "age", 23);
-        pl_json_add_bool_member(&atFriends[0], "tall", true);
-        pl_json_add_bool_member(&atFriends[0], "hungry", false);
-        pl_json_add_int_array(&atFriends[0], "scores", aScores0, 3);
+    plJsonObject* ptFriend0 = pl_json_member_by_index(ptFriends, 0);
+    int aScores0[] = {88, 86, 100};
+    pl_json_add_string_member(ptFriend0, "first name", "Jacob");
+    pl_json_add_string_member(ptFriend0, "last name", "Smith");
+    pl_json_add_int_member(ptFriend0, "age", 23);
+    pl_json_add_bool_member(ptFriend0, "tall", true);
+    pl_json_add_bool_member(ptFriend0, "hungry", false);
+    pl_json_add_int_array(ptFriend0, "scores", aScores0, 3);
 
-        int aScores1[] = {80, 80, 100};
-        pl_json_add_string_member(&atFriends[1], "first name", "Chance");
-        pl_json_add_string_member(&atFriends[1], "last name", "Dale");
-        pl_json_add_int_member(&atFriends[1], "age", 48);
-        pl_json_add_bool_member(&atFriends[1], "tall", true);
-        pl_json_add_bool_member(&atFriends[1], "hungry", true);
-        pl_json_add_int_array(&atFriends[1], "scores", aScores1, 3);
+    plJsonObject* ptFriend1 = pl_json_member_by_index(ptFriends, 1);
+    int aScores1[] = {80, 80, 100};
+    pl_json_add_string_member(ptFriend1, "first name", "Chance");
+    pl_json_add_string_member(ptFriend1, "last name", "Dale");
+    pl_json_add_int_member(ptFriend1, "age", 48);
+    pl_json_add_bool_member(ptFriend1, "tall", true);
+    pl_json_add_bool_member(ptFriend1, "hungry", true);
+    pl_json_add_int_array(ptFriend1, "scores", aScores1, 3);
 
-        pl_json_add_member_array(&tRootJsonObject, "friends", atFriends, 2);
+    uint32_t uBufferSize = 0;
+    pl_write_json(ptRootJsonObject, NULL, &uBufferSize);
 
-        uint32_t uBufferSize = 0;
-        pl_write_json(&tRootJsonObject, NULL, &uBufferSize);
+    *ppcBuffer = malloc(uBufferSize + 1);
+    memset(*ppcBuffer, 0, uBufferSize + 1);
+    pl_write_json(ptRootJsonObject, *ppcBuffer, &uBufferSize);
 
-        pcBuffer = malloc(uBufferSize + 1);
-        memset(pcBuffer, 0, uBufferSize + 1);
-        pl_write_json(&tRootJsonObject, pcBuffer, &uBufferSize);
-
-        pl_unload_json(&tRootJsonObject);
-    }
-
-    // read json
-    {
-
-        plJsonObject tRootJsonObject = {0};
-        pl_load_json(pcBuffer, &tRootJsonObject);
-
-        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~reading~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-        // check root members
-        {
-            int aiScores[3] = {0};
-            pl_json_int_array_member(&tRootJsonObject, "scores", aiScores, NULL);
-            pl_test_expect_int_equal(aiScores[0], 100, NULL); 
-            pl_test_expect_int_equal(aiScores[1], 86, NULL); 
-            pl_test_expect_int_equal(aiScores[2], 46, NULL); 
-        }
-        {
-            char acPet0[64] = {0};
-            char acPet1[64] = {0};
-            char acPet2[64] = {0};
-            char* aacPets[3] = {acPet0, acPet1, acPet2};
-            uint32_t auLengths[3] = {64, 64, 64};
-            pl_json_string_array_member(&tRootJsonObject, "pets", aacPets, NULL, auLengths);
-            pl_test_expect_string_equal(acPet0, "Riley", NULL);
-            pl_test_expect_string_equal(acPet1, "Luna", NULL);
-            pl_test_expect_string_equal(acPet2, "Chester", NULL);
-        }
-
-        char acFirstName[64] = {0};
-        char acLastName[64] = {0};
-        pl_test_expect_string_equal(pl_json_string_member(&tRootJsonObject, "first name", acFirstName, 64), "John", NULL);
-        pl_test_expect_string_equal(pl_json_string_member(&tRootJsonObject, "last name", acLastName, 64), "Doe", NULL);
-        pl_test_expect_int_equal(pl_json_int_member(&tRootJsonObject, "age", 0), 40, NULL);
-        pl_test_expect_false(pl_json_bool_member(&tRootJsonObject, "tall", false), NULL);
-        pl_test_expect_true(pl_json_bool_member(&tRootJsonObject, "hungry", false), NULL);
-
-        // check child members
-        plJsonObject* ptBestFriend = pl_json_member(&tRootJsonObject, "best friend");
-        {
-            int aiScores[3] = {0};
-            pl_json_int_array_member(ptBestFriend, "scores", aiScores, NULL);
-            pl_test_expect_int_equal(aiScores[0], 100, NULL); 
-            pl_test_expect_int_equal(aiScores[1], 86, NULL); 
-            pl_test_expect_int_equal(aiScores[2], 46, NULL); 
-        }
-        {
-            char acPet0[64] = {0};
-            char acPet1[64] = {0};
-            char acPet2[64] = {0};
-            char* aacPets[3] = {acPet0, acPet1, acPet2};
-            uint32_t auLengths[3] = {64, 64, 64};
-            pl_json_string_array_member(ptBestFriend, "pets", aacPets, NULL, auLengths);
-            pl_test_expect_string_equal(acPet0, "Riley", NULL);
-            pl_test_expect_string_equal(acPet1, "Luna", NULL);
-            pl_test_expect_string_equal(acPet2, "Chester", NULL);
-        }
-
-        pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "first name", acFirstName, 64), "John", NULL);
-        pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "last name", acLastName, 64), "Doe", NULL);
-        pl_test_expect_int_equal(pl_json_int_member(ptBestFriend, "age", 0), 40, NULL);
-        pl_test_expect_false(pl_json_bool_member(ptBestFriend, "tall", false), NULL);
-        pl_test_expect_true(pl_json_bool_member(ptBestFriend, "hungry", false), NULL);
-
-        uint32_t uFriendCount = 0;
-        plJsonObject* sbtFriends = pl_json_array_member(&tRootJsonObject, "friends", &uFriendCount);
-
-        plJsonObject* ptFriend0 = &sbtFriends[0];
-        plJsonObject* ptFriend1 = &sbtFriends[1];
-        {
-            int aiScores[3] = {0};
-            pl_json_int_array_member(ptFriend0, "scores", aiScores, NULL);
-            pl_test_expect_int_equal(aiScores[0], 88, NULL); 
-            pl_test_expect_int_equal(aiScores[1], 86, NULL); 
-            pl_test_expect_int_equal(aiScores[2], 100, NULL); 
-        }
-
-        {
-            int aiScores[3] = {0};
-            pl_json_int_array_member(ptFriend1, "scores", aiScores, NULL);
-            pl_test_expect_int_equal(aiScores[0], 80, NULL); 
-            pl_test_expect_int_equal(aiScores[1], 80, NULL); 
-            pl_test_expect_int_equal(aiScores[2], 100, NULL); 
-        }
-
-        pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "first name", acFirstName, 64), "Jacob", NULL);
-        pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "last name", acLastName, 64), "Smith", NULL);
-        pl_test_expect_int_equal(pl_json_int_member(ptFriend0, "age", 0), 23, NULL);
-        pl_test_expect_true(pl_json_bool_member(ptFriend0, "tall", false), NULL);
-        pl_test_expect_false(pl_json_bool_member(ptFriend0, "hungry", false), NULL);  
-
-        pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "first name", acFirstName, 64), "Chance", NULL);
-        pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "last name", acLastName, 64), "Dale", NULL);
-        pl_test_expect_int_equal(pl_json_int_member(ptFriend1, "age", 0), 48, NULL);
-        pl_test_expect_true(pl_json_bool_member(ptFriend1, "tall", false), NULL);
-        pl_test_expect_true(pl_json_bool_member(ptFriend1, "hungry", false), NULL);
-
-        pl_unload_json(&tRootJsonObject);
-    }
+    pl_unload_json(&ptRootJsonObject);
 }
+
+void
+read_json_test(void* pData)
+{
+
+    char** ppcBuffer = pData;
+
+    plJsonObject* ptRootJsonObject = NULL;
+    pl_load_json(*ppcBuffer, &ptRootJsonObject);
+
+    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~reading~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    // check root members
+    {
+        int aiScores[3] = {0};
+        pl_json_int_array_member(ptRootJsonObject, "scores", aiScores, NULL);
+        pl_test_expect_int_equal(aiScores[0], 100, NULL); 
+        pl_test_expect_int_equal(aiScores[1], 86, NULL); 
+        pl_test_expect_int_equal(aiScores[2], 46, NULL); 
+    }
+    {
+        char acPet0[64] = {0};
+        char acPet1[64] = {0};
+        char acPet2[64] = {0};
+        char* aacPets[3] = {acPet0, acPet1, acPet2};
+        uint32_t auLengths[3] = {64, 64, 64};
+        pl_json_string_array_member(ptRootJsonObject, "pets", aacPets, NULL, auLengths);
+        pl_test_expect_string_equal(acPet0, "Riley", NULL);
+        pl_test_expect_string_equal(acPet1, "Luna", NULL);
+        pl_test_expect_string_equal(acPet2, "Chester", NULL);
+    }
+
+    char acFirstName[64] = {0};
+    char acLastName[64] = {0};
+    pl_test_expect_string_equal(pl_json_string_member(ptRootJsonObject, "first name", acFirstName, 64), "John", NULL);
+    pl_test_expect_string_equal(pl_json_string_member(ptRootJsonObject, "last name", acLastName, 64), "Doe", NULL);
+    pl_test_expect_int_equal(pl_json_int_member(ptRootJsonObject, "age", 0), 40, NULL);
+    pl_test_expect_false(pl_json_bool_member(ptRootJsonObject, "tall", false), NULL);
+    pl_test_expect_true(pl_json_bool_member(ptRootJsonObject, "hungry", false), NULL);
+
+    // check child members
+    plJsonObject* ptBestFriend = pl_json_member(ptRootJsonObject, "best friend");
+    {
+        int aiScores[3] = {0};
+        pl_json_int_array_member(ptBestFriend, "scores", aiScores, NULL);
+        pl_test_expect_int_equal(aiScores[0], 100, NULL); 
+        pl_test_expect_int_equal(aiScores[1], 86, NULL); 
+        pl_test_expect_int_equal(aiScores[2], 46, NULL); 
+    }
+    {
+        char acPet0[64] = {0};
+        char acPet1[64] = {0};
+        char acPet2[64] = {0};
+        char* aacPets[3] = {acPet0, acPet1, acPet2};
+        uint32_t auLengths[3] = {64, 64, 64};
+        pl_json_string_array_member(ptBestFriend, "pets", aacPets, NULL, auLengths);
+        pl_test_expect_string_equal(acPet0, "Riley", NULL);
+        pl_test_expect_string_equal(acPet1, "Luna", NULL);
+        pl_test_expect_string_equal(acPet2, "Chester", NULL);
+    }
+
+    pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "first name", acFirstName, 64), "John", NULL);
+    pl_test_expect_string_equal(pl_json_string_member(ptBestFriend, "last name", acLastName, 64), "Doe", NULL);
+    pl_test_expect_int_equal(pl_json_int_member(ptBestFriend, "age", 0), 40, NULL);
+    pl_test_expect_false(pl_json_bool_member(ptBestFriend, "tall", false), NULL);
+    pl_test_expect_true(pl_json_bool_member(ptBestFriend, "hungry", false), NULL);
+
+    uint32_t uFriendCount = 0;
+    plJsonObject* ptFriends = pl_json_array_member(ptRootJsonObject, "friends", &uFriendCount);
+
+    plJsonObject* ptFriend0 = pl_json_member_by_index(ptFriends, 0);
+    plJsonObject* ptFriend1 = pl_json_member_by_index(ptFriends, 1);
+    {
+        int aiScores[3] = {0};
+        pl_json_int_array_member(ptFriend0, "scores", aiScores, NULL);
+        pl_test_expect_int_equal(aiScores[0], 88, NULL); 
+        pl_test_expect_int_equal(aiScores[1], 86, NULL); 
+        pl_test_expect_int_equal(aiScores[2], 100, NULL); 
+    }
+
+    {
+        int aiScores[3] = {0};
+        pl_json_int_array_member(ptFriend1, "scores", aiScores, NULL);
+        pl_test_expect_int_equal(aiScores[0], 80, NULL); 
+        pl_test_expect_int_equal(aiScores[1], 80, NULL); 
+        pl_test_expect_int_equal(aiScores[2], 100, NULL); 
+    }
+
+    pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "first name", acFirstName, 64), "Jacob", NULL);
+    pl_test_expect_string_equal(pl_json_string_member(ptFriend0, "last name", acLastName, 64), "Smith", NULL);
+    pl_test_expect_int_equal(pl_json_int_member(ptFriend0, "age", 0), 23, NULL);
+    pl_test_expect_true(pl_json_bool_member(ptFriend0, "tall", false), NULL);
+    pl_test_expect_false(pl_json_bool_member(ptFriend0, "hungry", false), NULL);  
+
+    pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "first name", acFirstName, 64), "Chance", NULL);
+    pl_test_expect_string_equal(pl_json_string_member(ptFriend1, "last name", acLastName, 64), "Dale", NULL);
+    pl_test_expect_int_equal(pl_json_int_member(ptFriend1, "age", 0), 48, NULL);
+    pl_test_expect_true(pl_json_bool_member(ptFriend1, "tall", false), NULL);
+    pl_test_expect_true(pl_json_bool_member(ptFriend1, "hungry", false), NULL);
+
+    pl_unload_json(&ptRootJsonObject);
+
+}
+
+
+void
+pl_json_tests(void* pData)
+{
+    static char* pcBuffer = NULL;
+
+    pl_test_register_test(write_json_test, &pcBuffer);
+    pl_test_register_test(read_json_test, &pcBuffer);
+}
\ No newline at end of file
diff --git a/tests/pl_memory_tests.h b/tests/pl_memory_tests.h
new file mode 100644
index 0000000..e22b278
--- /dev/null
+++ b/tests/pl_memory_tests.h
@@ -0,0 +1,157 @@
+#include "pl_test.h"
+#include "pl_memory.h"
+#include <string.h> // memset
+
+typedef struct _plTestStruct
+{
+    const char* pcName;
+    int         iAge;
+} plTestStruct;
+
+void
+memory_test_aligned_alloc(void* pData)
+{
+    int* iBuffer0 = pl_aligned_alloc(0, sizeof(int));
+    pl_test_expect_uint64_equal(((uint64_t)iBuffer0) % 4, 0, NULL);
+    pl_aligned_free(iBuffer0);
+
+    int* iBuffer1 = pl_aligned_alloc(16, sizeof(int));
+    pl_test_expect_uint64_equal(((uint64_t)iBuffer1) % 16, 0, NULL);
+    pl_aligned_free(iBuffer1);
+}
+
+void
+memory_test_pool_allocator_0(void* pData)
+{
+    // here we are testing standard usage
+
+    plPoolAllocator tAllocator = {0};
+
+    // let library tell use required buffer size
+    size_t szRequiredBufferSize = 0;
+    pl_pool_allocator_init(&tAllocator, 5, sizeof(int), 0, &szRequiredBufferSize, NULL);
+
+    int* iBuffer = malloc(szRequiredBufferSize);
+    memset(iBuffer, 0, szRequiredBufferSize);
+    pl_pool_allocator_init(&tAllocator, 5, sizeof(int), 0, &szRequiredBufferSize, iBuffer);
+
+    int* iItem0 = pl_pool_allocator_alloc(&tAllocator);
+    int* iItem1 = pl_pool_allocator_alloc(&tAllocator);
+    int* iItem2 = pl_pool_allocator_alloc(&tAllocator);
+    int* iItem3 = pl_pool_allocator_alloc(&tAllocator);
+    int* iItem4 = pl_pool_allocator_alloc(&tAllocator);
+    int* iItem5 = pl_pool_allocator_alloc(&tAllocator);
+
+    *iItem0 = 0;
+    *iItem1 = 1;
+    *iItem2 = 2;
+    *iItem3 = 3;
+    *iItem4 = 4;
+    
+    pl_test_expect_int_equal(*iItem0, 0, NULL);
+    pl_test_expect_int_equal(*iItem1, 1, NULL);
+    pl_test_expect_int_equal(*iItem2, 2, NULL);
+    pl_test_expect_int_equal(*iItem3, 3, NULL);
+    pl_test_expect_int_equal(*iItem4, 4, NULL);
+    pl_test_expect_uint64_equal(((uint64_t)iItem5), 0, NULL);
+    free(iBuffer);
+}
+
+void
+memory_test_pool_allocator_1(void* pData)
+{
+    // here we are testing usage with stack allocated memory
+
+    plPoolAllocator tAllocator = {0};
+
+    plTestStruct atData[5] = {0};
+
+    // let library tell use required buffer size
+    size_t szRequiredBufferSize = sizeof(atData);
+    size_t szItemCount = pl_pool_allocator_init(&tAllocator, 0, sizeof(plTestStruct), 0, &szRequiredBufferSize, atData);
+    pl_pool_allocator_init(&tAllocator, szItemCount, sizeof(plTestStruct), 0, &szRequiredBufferSize, atData);
+
+    pl_test_expect_int_equal((int)szItemCount, 4, NULL);
+
+    plTestStruct* ptData0 = pl_pool_allocator_alloc(&tAllocator);
+    plTestStruct* ptData1 = pl_pool_allocator_alloc(&tAllocator);
+    plTestStruct* ptData2 = pl_pool_allocator_alloc(&tAllocator);
+    plTestStruct* ptData3 = pl_pool_allocator_alloc(&tAllocator);
+    plTestStruct* ptData4 = pl_pool_allocator_alloc(&tAllocator);
+
+    ptData0->iAge = 0;
+    ptData1->iAge = 1;
+    ptData2->iAge = 2;
+    ptData3->iAge = 3;
+
+    ptData0->pcName = "John";
+    ptData1->pcName = "James";
+    ptData2->pcName = "Mark";
+    ptData3->pcName = "Matt";
+
+    pl_test_expect_int_equal(ptData0->iAge, 0, NULL);
+    pl_test_expect_int_equal(ptData1->iAge, 1, NULL);
+    pl_test_expect_int_equal(ptData2->iAge, 2, NULL);
+    pl_test_expect_int_equal(ptData3->iAge, 3, NULL);
+
+    pl_test_expect_string_equal(ptData0->pcName, "John", NULL);
+    pl_test_expect_string_equal(ptData1->pcName, "James", NULL);
+    pl_test_expect_string_equal(ptData2->pcName, "Mark", NULL);
+    pl_test_expect_string_equal(ptData3->pcName, "Matt", NULL);
+    pl_test_expect_uint64_equal(((uint64_t)ptData4), 0, NULL);
+
+    pl_pool_allocator_free(&tAllocator, ptData1);
+    plTestStruct* ptData5 = pl_pool_allocator_alloc(&tAllocator);
+    pl_test_expect_uint64_not_equal(((uint64_t)ptData5), 0, NULL);
+}
+
+void
+memory_test_stack_allocator_0(void* pData)
+{
+    char acBuffer[1024] = {0};
+    plStackAllocator tAllocator = {0};
+    pl_stack_allocator_init(&tAllocator, 1024, acBuffer);
+
+    plTestStruct* ptData0 = pl_stack_allocator_alloc(&tAllocator, sizeof(plTestStruct));
+    ptData0->pcName = "John";
+    ptData0->iAge = 31;
+
+    plStackAllocatorMarker tMarker0 = pl_stack_allocator_marker(&tAllocator);
+
+    plTestStruct* ptData1 = pl_stack_allocator_alloc(&tAllocator, sizeof(plTestStruct));
+    ptData0->pcName = "Rachael";
+    ptData0->iAge = 32;
+
+    pl_stack_allocator_free_to_marker(&tAllocator, tMarker0);
+
+    plTestStruct* ptData2 = pl_stack_allocator_alloc(&tAllocator, sizeof(plTestStruct));
+    ptData0->pcName = "Charlie";
+    ptData0->iAge = 4;
+
+    pl_test_expect_uint64_equal((uint64_t)ptData1, (uint64_t)ptData2, NULL);
+}
+
+void
+memory_test_temp_allocator_0(void* pData)
+{
+    plTempAllocator tAllocator = {0};
+
+    char* pcBuffer0 = pl_temp_allocator_alloc(&tAllocator, 256);
+    char* pcBuffer1 = pl_temp_allocator_alloc(&tAllocator, 256);
+    char* pcBuffer2 = pl_temp_allocator_alloc(&tAllocator, 256);
+    char* pcBuffer3 = pl_temp_allocator_alloc(&tAllocator, 256);
+    char* pcBuffer4 = pl_temp_allocator_alloc(&tAllocator, 256);
+    
+    
+    pl_temp_allocator_free(&tAllocator);
+}
+
+void
+pl_memory_tests(void* pData)
+{
+    pl_test_register_test(memory_test_aligned_alloc, NULL);
+    pl_test_register_test(memory_test_pool_allocator_0, NULL);
+    pl_test_register_test(memory_test_pool_allocator_1, NULL);
+    pl_test_register_test(memory_test_stack_allocator_0, NULL);
+    pl_test_register_test(memory_test_temp_allocator_0, NULL);
+}
\ No newline at end of file
diff --git a/tests/pl_string_tests.h b/tests/pl_string_tests.h
new file mode 100644
index 0000000..9582320
--- /dev/null
+++ b/tests/pl_string_tests.h
@@ -0,0 +1,78 @@
+#include "pl_test.h"
+#include "pl_string.h"
+
+void
+string_test_0(void* pData)
+{
+    const char* pcFilePath0 = "C:/Users/hoffstadt/file1.txt";
+    const char* pcFilePath1 = "C:\\Users\\hoffstadt\\file1.txt";
+    const char* pcFilePath2 = "C:\\Users/hoffstadt\\file1.txt";
+    const char* pcFilePath3 = "file1.txt";
+    const char* pcFilePath4 = "file1";
+
+    const char* pcExt0 = pl_str_get_file_extension(pcFilePath0, NULL, 0);
+    const char* pcExt1 = pl_str_get_file_extension(pcFilePath1, NULL, 0);
+    const char* pcExt2 = pl_str_get_file_extension(pcFilePath2, NULL, 0);
+    const char* pcExt3 = pl_str_get_file_extension(pcFilePath3, NULL, 0);
+    const char* pcExt4 = pl_str_get_file_extension(pcFilePath4, NULL, 0);
+
+    pl_test_expect_string_equal(pcExt0, "txt", NULL);
+    pl_test_expect_string_equal(pcExt1, "txt", NULL);
+    pl_test_expect_string_equal(pcExt2, "txt", NULL);
+    pl_test_expect_string_equal(pcExt3, "txt", NULL);
+    pl_test_expect_uint64_equal(((uint64_t)pcExt4), 0, NULL);
+
+    const char* pcFile0 = pl_str_get_file_name(pcFilePath0, NULL, 0);
+    const char* pcFile1 = pl_str_get_file_name(pcFilePath1, NULL, 0);
+    const char* pcFile2 = pl_str_get_file_name(pcFilePath2, NULL, 0);
+    const char* pcFile3 = pl_str_get_file_name(pcFilePath3, NULL, 0);
+    const char* pcFile4 = pl_str_get_file_name(pcFilePath4, NULL, 0);
+
+    pl_test_expect_string_equal(pcFile0, "file1.txt", "pl_str_get_file_name");
+    pl_test_expect_string_equal(pcFile1, "file1.txt", "pl_str_get_file_name");
+    pl_test_expect_string_equal(pcFile2, "file1.txt", "pl_str_get_file_name");
+    pl_test_expect_string_equal(pcFile3, "file1.txt", "pl_str_get_file_name");
+    pl_test_expect_string_equal(pcFile4, "file1", "pl_str_get_file_name");
+
+    char acFileName0[256] = {0};
+    char acFileName1[256] = {0};
+    char acFileName2[256] = {0};
+    char acFileName3[256] = {0};
+    char acFileName4[256] = {0};
+    pl_str_get_file_name_only(pcFilePath0, acFileName0, 256);
+    pl_str_get_file_name_only(pcFilePath1, acFileName1, 256);
+    pl_str_get_file_name_only(pcFilePath2, acFileName2, 256);
+    pl_str_get_file_name_only(pcFilePath3, acFileName3, 256);
+    pl_str_get_file_name_only(pcFilePath4, acFileName4, 256);
+
+    pl_test_expect_string_equal(acFileName0, "file1", "pl_str_get_file_name_only");
+    pl_test_expect_string_equal(acFileName1, "file1", "pl_str_get_file_name_only");
+    pl_test_expect_string_equal(acFileName2, "file1", "pl_str_get_file_name_only");
+    pl_test_expect_string_equal(acFileName3, "file1", "pl_str_get_file_name_only");
+    pl_test_expect_string_equal(acFileName4, "file1", "pl_str_get_file_name_only");
+
+    char acDirectory0[128] = {0};
+    char acDirectory1[128] = {0};
+    char acDirectory2[128] = {0};
+    char acDirectory3[128] = {0};
+    char acDirectory4[128] = {0};
+
+    pl_str_get_directory(pcFilePath0, acDirectory0, 128);
+    pl_str_get_directory(pcFilePath1, acDirectory1, 128);
+    pl_str_get_directory(pcFilePath2, acDirectory2, 128);
+    pl_str_get_directory(pcFilePath3, acDirectory3, 128);
+    pl_str_get_directory(pcFilePath4, acDirectory4, 128);
+
+    pl_test_expect_string_equal(acDirectory0, "C:/Users/hoffstadt/", NULL);
+    pl_test_expect_string_equal(acDirectory1, "C:\\Users\\hoffstadt\\", NULL);
+    pl_test_expect_string_equal(acDirectory2, "C:\\Users/hoffstadt\\", NULL);
+    pl_test_expect_string_equal(acDirectory3, "./", NULL);
+    pl_test_expect_string_equal(acDirectory4, "./", NULL);
+
+}
+
+void
+pl_string_tests(void* pData)
+{
+    pl_test_register_test(string_test_0, NULL);
+}
\ No newline at end of file