diff --git a/CMakeLists.txt b/CMakeLists.txt
index 02413b5..fbbddda 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,3 +20,6 @@ IF(GCRYPT_FOUND)
add_compile_definitions(DROPOUT_DL_GCRYPT)
ENDIF()
+# IF(DROPOUT_BUILD_TESTS)
+ add_subdirectory(tests)
+# ENDIF()
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..8ffbb9c
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.23)
+project(dropout-dl-tests)
+
+set(CMAKE_CXX_STANDARD 17)
+
+add_executable(episode-test test.cpp episode.cpp ../src/episode.cpp)
+
+target_link_libraries(episode-test curl sqlite3 gcrypt)
+
+message(STATUS "Building Tests")
+
+include_directories(episode ../src)
diff --git a/tests/episode.cpp b/tests/episode.cpp
new file mode 100644
index 0000000..ecf738d
--- /dev/null
+++ b/tests/episode.cpp
@@ -0,0 +1,322 @@
+//
+// Created by moss on 9/30/22.
+//
+#include "episode.h"
+#include "test.h"
+
+namespace dropout_dl {
+ tests test_episode_name_parsing() {
+ bool success;
+
+ std::string (*test_function)(const std::string&) = episode::get_episode_name;
+
+ std::string base_test_solution = "Base Test Title";
+ std::string base_test = "
\n"
+ " " + base_test_solution + "\n"
+ "
";
+
+ test base("Basic Episode Name Parsing", test_function, base_test, base_test_solution);
+
+
+ std::string multiple_header_test_solution = "Multi Header Test Title";
+ std::string multiple_header_test = "\n"
+ "Header without class or strong\n"
+ "
\n"
+ "\n"
+ "Header with incorrect classes"
+ "
\n"
+ "\n"
+ "Header with strong"
+ "
\n"
+ "\n"
+ " " + multiple_header_test_solution + "\n"
+ "
\n"
+ "\n"
+ " Valid Header and Strong After Correct Title \n"
+ "
";
+
+
+ test multiple_header("Multiple Header Episode Name Parsing", test_function, multiple_header_test, multiple_header_test_solution);
+
+ std::string no_valid_header_test_solution = "ERROR";
+ std::string no_valid_header_test = "\n"
+ "Header without class or Strong\n"
+ "
\n"
+ "\n"
+ "Header with incorrect classes"
+ "
\n"
+ "\n"
+ "Header with strong"
+ "
\n";
+
+ test no_valid_header("No Valid Header Episode Name Parsing", test_function, no_valid_header_test, no_valid_header_test_solution);
+
+
+ std::string html_character_test_solution = "'&;";
+ std::string html_character_test = "\n"
+ " '&;\n"
+ "
";
+
+ test html_character_code("Html Character Code Episode Name Parsing", test_function, html_character_test, html_character_test_solution);
+
+
+ return tests("Episode Name Parsing", {base, multiple_header, no_valid_header, html_character_code});
+ }
+
+ tests test_episode_number_parsing() {
+ bool success;
+
+ std::string (*test_function)(const std::string&) = episode::get_episode_number;
+
+ std::string base_test_solution = "1";
+ std::string base_test = "\n"
+ " Season 1, Episode 1\n"
+ "";
+
+ test base("Basic Episode Number Parsing", test_function, base_test, base_test_solution);
+
+
+ std::string multiple_link_test_solution = "1";
+ std::string multiple_link_test = "\n"
+ "asjdhgaorihg\n"
+ "\n"
+ "\n"
+ " Season 1, Episode 1\n"
+ "\n"
+ "\n"
+ " Season 1, Episode 2\n";
+
+
+ test multiple_link("Multiple Link Episode Number Parsing", test_function, multiple_link_test, multiple_link_test_solution);
+
+ std::string no_valid_number_test_solution = "-1";
+ std::string no_valid_number_test = "\n"
+ "816\n"
+ "\n"
+ "\n"
+ "157"
+ "\n"
+ "\n"
+ "Episode"
+ "\n";
+
+ test no_valid_number("No Valid Episode Number Parsing", test_function, no_valid_number_test, no_valid_number_test_solution);
+
+
+ std::string earlier_episode_text_test_solution = "15";
+ std::string earlier_episode_text_test = " \n"
+ " Episode Wrong\n"
+ "
\n"
+ "\n"
+ " Season 1, Episode 15\n"
+ "";
+
+ test earlier_episode_text("Earlier Episode Text Number Parsing", test_function, earlier_episode_text_test, earlier_episode_text_test_solution);
+
+ return tests("Episode Name Parsing", {base, multiple_link, no_valid_number, earlier_episode_text});
+ }
+
+ tests test_episode_series_name_parsing() {
+ bool success;
+
+ std::string (*test_function)(const std::string&) = episode::get_series_name;
+
+ std::string base_test_solution = "Base Test Title";
+ std::string base_test = "";
+
+ test base("Basic Episode Series Name Parsing", test_function, base_test, base_test_solution);
+
+
+ std::string multiple_header_test_solution = "Multi Header Test Title";
+ std::string multiple_header_test = "\n"
+ "Header without class or link\n"
+ "
\n"
+ "\n"
+ "Header with incorrect classes"
+ "
\n"
+ "\n"
+ "\n"
+ "";
+
+
+ test multiple_header("Multiple Header Episode Series Name Parsing", test_function, multiple_header_test, multiple_header_test_solution);
+
+ std::string no_valid_header_test_solution = "ERROR";
+ std::string no_valid_header_test = "\n"
+ "Header without class or link\n"
+ "
\n"
+ "\n"
+ "Header with incorrect classes"
+ "
\n"
+ "\n";
+
+ test no_valid_header("No Valid Header Episode Series Name Parsing", test_function, no_valid_header_test, no_valid_header_test_solution);
+
+
+ std::string html_character_test_solution = "'&;";
+ std::string html_character_test = "\n"
+ " '&;\n"
+ "
";
+
+ test html_character("Html Character Code Episode Series Name Parsing", test_function, html_character_test, html_character_test_solution);
+
+ return tests("Episode Name Parsing", {base, multiple_header, no_valid_header, html_character});
+ }
+
+ tests test_episode_embedded_url_parsing() {
+ bool success;
+
+ std::string (*test_function)(const std::string&) = episode::get_embed_url;
+
+ std::string base_test_solution = "Base Test URL";
+ std::string base_test = " window.VHX.config = {\n"
+ " embed_url: \"" + base_test_solution + "\"\n"
+ " };";
+
+ test base("Basic Episode Embedded URL Parsing", test_function, base_test, base_test_solution);
+
+
+ std::string multiple_script_test_solution = "Multi Header Test Title";
+ std::string multiple_script_test = " "
+ ""
+ ""
+ ""
+ ""
+ ""
+ "";
+
+
+ test multiple_script("Multiple Script Embedded URL Parsing", test_function, multiple_script_test, multiple_script_test_solution);
+
+ std::string no_valid_URL_test_solution = "";
+ std::string no_valid_URL_test = " ";
+
+ test no_valid_URL("No Valid Embedded URL Parsing", test_function, no_valid_URL_test, no_valid_URL_test_solution);
+
+ return tests("Episode Name Parsing", {base, multiple_script, no_valid_URL});
+ }
+
+ tests test_episode_config_url_parsing() {
+ bool success;
+
+ std::string (*test_function)(const std::string&) = episode::get_config_url;
+
+ std::string base_test_solution = "Base Test URL";
+ std::string base_test = R"(window.OTTData = {"config_url":")" + base_test_solution + "\"};";
+
+ test base("Basic Episode Config URL Parsing", test_function, base_test, base_test_solution);
+
+
+ std::string no_valid_URL_test_solution = "";
+ std::string no_valid_URL_test = R"(window.OTTData = {"api_data":{"api_host":"","api_token":"","user_auth_token":{"auth_user_token":"","embed_referrer_host":""}},"buy_button":{"label":null,"url":null},"collection":{"id":null},"product":{"id":null},"":{"label":"","url":""},"site":{"id":null,"subdomain":"","twitter_name":""},"video":{"duration":null,"id":null,"is_trailer":"","title":"","is_live_video":false,"live_event_id":null},"google_cast_app_id":"","hide_chrome":null,"initial_time":null,"js_api_enabled":null,"locale":"","show_share_actions":null,"user":{"id":,"email":""},"analytics_url":""})";
+
+ test no_valid_URL("No Valid Config URL Parsing", test_function, no_valid_URL_test, no_valid_URL_test_solution);
+
+ return tests("Episode Name Parsing", {base, no_valid_URL});
+ }
+}
+
+int main() {
+
+ dropout_dl::tests name_tests = dropout_dl::test_episode_name_parsing();
+
+
+ dropout_dl::tests number_tests = dropout_dl::test_episode_number_parsing();
+
+
+ dropout_dl::tests series_tests = dropout_dl::test_episode_series_name_parsing();
+
+
+ dropout_dl::tests embedded_tests = dropout_dl::test_episode_embedded_url_parsing();
+
+
+ dropout_dl::tests config_tests = dropout_dl::test_episode_config_url_parsing();
+
+
+ if (name_tests.success && number_tests.success && series_tests.success && embedded_tests.success && config_tests.success) {
+ std::cout << TESTNAME << BOLDRED << "Episode Tests" << RESET << std::endl;
+ }
+ else {
+ std::cout << TESTNAME << BOLDGREEN << "Episode Tests" << RESET << std::endl;
+ }
+
+ name_tests.display();
+
+ number_tests.display();
+
+ series_tests.display();
+
+ embedded_tests.display();
+
+ config_tests.display();
+}
\ No newline at end of file
diff --git a/tests/test.cpp b/tests/test.cpp
new file mode 100644
index 0000000..0338903
--- /dev/null
+++ b/tests/test.cpp
@@ -0,0 +1,26 @@
+//
+// Created by moss on 9/30/22.
+//
+#include "test.h"
+
+template
+void dropout_dl::test::display_result() {
+ if (!this->success) {
+ std::cout << RED << name << ": \"" << result << "\" =/= \"" << expected_result << '"' << RESET << std::endl;
+ } else {
+ std::cout << GREEN << name << RESET << std::endl;
+ }
+}
+
+void dropout_dl::tests::display() {
+
+ if (!this->success) {
+ std::cout << '\n' << TESTNAME << BOLDRED << name << RESET << std::endl;
+ } else {
+ std::cout << '\n' << TESTNAME << BOLDGREEN << name << RESET << std::endl;
+ }
+
+ for (auto& test : tests_vector) {
+ test.display_result();
+ }
+}
\ No newline at end of file
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..af9f94d
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,83 @@
+//
+// Created by moss on 9/30/22.
+//
+#pragma once
+
+#include
+#include "iostream"
+
+namespace dropout_dl {
+
+#define RESET "\033[0m"
+#define BLACK "\033[30m" /* Black */
+#define RED "\033[31m" /* Red */
+#define GREEN "\033[32m" /* Green */
+#define YELLOW "\033[33m" /* Yellow */
+#define BLUE "\033[34m" /* Blue */
+#define MAGENTA "\033[35m" /* Magenta */
+#define CYAN "\033[36m" /* Cyan */
+#define WHITE "\033[37m" /* White */
+#define BOLDBLACK "\033[1m\033[30m" /* Bold Black */
+#define BOLDRED "\033[1m\033[31m" /* Bold Red */
+#define FAIL "\033[31mFAIL: \033[0m" // Test Failure
+#define BOLDFAIL "\033[1m\033[31mFAIL: \033[0m" // Test Failure
+#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */
+#define SUCCESS "\033[32mSUCCESS: \033[0m" /* Test Success */
+#define BOLDSUCCESS "\033[1m\033[32mSUCCESS: \033[0m" /* Test Success */
+#define BOLDYELLOW "\033[1m\033[33m" /* Bold Yellow */
+#define WARN "\033[1m\033[33mWARNING: \033[0m" /* Test Warning */
+#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */
+#define TESTNAME "\033[1m\033[34mTEST: \033[0m" /* Bold Blue */
+#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
+#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */
+#define BOLDWHITE "\033[1m\033[37m" /* Bold White */
+
+
+ template class test {
+ public:
+ std::string name;
+ t result;
+ t expected_result;
+ bool success;
+
+ test(const std::string& name, const t& result, const t& expected_result) {
+ this->name = name;
+ this->result = result;
+ this->expected_result = expected_result;
+ this->success = (result == expected_result);
+ }
+
+ test(const std::string& test_name, t (*function)(const t &), const t &argument, const t& expected_result) {
+ t test_result = function(argument);
+ this->name = test_name;
+ this->result = test_result;
+ this->expected_result = expected_result;
+ this->success = test_result == expected_result;
+ }
+
+ void display_result();
+
+
+ };
+
+
+ class tests {
+ public:
+ std::vector> tests_vector;
+ std::string name;
+ bool success;
+
+ tests(const std::string& name, const std::vector>& tests) {
+ this->name = name;
+ this->tests_vector = tests;
+ this->success = tests_vector.front().success;
+ for (const auto& test : tests_vector) {
+ success = success && test.success;
+ }
+ }
+
+ void display();
+ };
+
+
+}
\ No newline at end of file