diff --git a/.gitgnore b/.gitgnore
new file mode 100644
index 0000000000000000000000000000000000000000..c795b054e5ade51b7031abab1581a5b7e2d2f5ba
--- /dev/null
+++ b/.gitgnore
@@ -0,0 +1 @@
+build
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..fdfd20ebcd4c8747f6e13236306a0c8d7d0bc602
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "simpletest_test"]
+	path = simpletest_test
+	url = https://github.com/kudaba/simpletest_test.git
+[submodule "simpletest"]
+	path = simpletest
+	url = https://github.com/kudaba/simpletest.git
diff --git a/.vscode/configurationCache.log b/.vscode/configurationCache.log
new file mode 100644
index 0000000000000000000000000000000000000000..697d0b38a516fcaba5a912884a09fa1d1c8f2848
--- /dev/null
+++ b/.vscode/configurationCache.log
@@ -0,0 +1 @@
+{"buildTargets":["all","build","build/","build/app","build/app2","build/log.o","build/main.o","build/main_part2.o","build/simpletest.o","build/test","clean","log_level","log_message","run"],"launchTargets":["Makefile | /home/ewanseiha2.odion/iot_starter-1/build>app()"],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":["/home/ewanseiha2.odion/iot_starter-1/include","/home/ewanseiha2.odion/iot_starter-1/src"],"compilerArgs":["-lpthread","-o","build/app","./src/main.cpp","-lpthread","-o","build/app","./src/main.cpp","-lpthread","-o","build/app","./src/main.cpp","-lpthread","-o","build/app","./src/main.cpp","-lpthread","-o","build/app","./src/main.cpp","-lpthread","-o","build/app","./src/main.cpp","build/main_part2.o","-lpthread","-o","build/app","./src/main.cpp","build/main_part2.o","build/main.o","-lpthread","-o","build/app2","./src/main_part2.cpp","build/main_part2.o","build/main.o","-lpthread","-o","build/app2","./src/main_part2.cpp","build/log.o","-lpthread","-o","build/app2","./src/unit_test.cpp","build/log.o","build/simpletest.o"]},"fileIndex":[["/home/ewanseiha2.odion/iot_starter-1/src/main.cpp",{"uri":{"$mid":1,"fsPath":"/home/ewanseiha2.odion/iot_starter-1/src/main.cpp","path":"/home/ewanseiha2.odion/iot_starter-1/src/main.cpp","scheme":"file"},"configuration":{"defines":[],"standard":"c++17","includePath":["/home/ewanseiha2.odion/iot_starter-1/include"],"forcedInclude":[],"intelliSenseMode":"clang-x64","compilerPath":"/usr/bin/clang++","compilerArgs":["-lpthread","-o","build/app","./src/main.cpp","build/log.o"],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -std=c++17  -I./include  -lpthread -o build/app ./src/main.cpp build/log.o","directory":"/home/ewanseiha2.odion/iot_starter-1","file":"/home/ewanseiha2.odion/iot_starter-1/src/main.cpp"}}],["/home/ewanseiha2.odion/iot_starter-1/src/main_part2.cpp",{"uri":{"$mid":1,"fsPath":"/home/ewanseiha2.odion/iot_starter-1/src/main_part2.cpp","path":"/home/ewanseiha2.odion/iot_starter-1/src/main_part2.cpp","scheme":"file"},"configuration":{"defines":[],"standard":"c++17","includePath":["/home/ewanseiha2.odion/iot_starter-1/include"],"forcedInclude":[],"intelliSenseMode":"clang-x64","compilerPath":"/usr/bin/clang++","compilerArgs":["-lpthread","-o","build/app2","./src/main_part2.cpp","build/log.o"],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -std=c++17  -I./include  -lpthread -o build/app2 ./src/main_part2.cpp build/log.o","directory":"/home/ewanseiha2.odion/iot_starter-1","file":"/home/ewanseiha2.odion/iot_starter-1/src/main_part2.cpp"}}],["/home/ewanseiha2.odion/iot_starter-1/src/log.cpp",{"uri":{"$mid":1,"fsPath":"/home/ewanseiha2.odion/iot_starter-1/src/log.cpp","path":"/home/ewanseiha2.odion/iot_starter-1/src/log.cpp","scheme":"file"},"configuration":{"defines":[],"standard":"c++17","includePath":["/home/ewanseiha2.odion/iot_starter-1/include"],"forcedInclude":[],"intelliSenseMode":"clang-x64","compilerPath":"/usr/bin/clang++","compilerArgs":["-c","-o","build/log.o"],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -c -std=c++17  -I./include  ./src/log.cpp -o build/log.o","directory":"/home/ewanseiha2.odion/iot_starter-1","file":"/home/ewanseiha2.odion/iot_starter-1/src/log.cpp"}}],["/home/ewanseiha2.odion/iot_starter-1/src/unit_test.cpp",{"uri":{"$mid":1,"fsPath":"/home/ewanseiha2.odion/iot_starter-1/src/unit_test.cpp","path":"/home/ewanseiha2.odion/iot_starter-1/src/unit_test.cpp","scheme":"file"},"configuration":{"defines":[],"standard":"c++17","includePath":["/home/ewanseiha2.odion/iot_starter-1/include"],"forcedInclude":[],"intelliSenseMode":"clang-x64","compilerPath":"/usr/bin/clang++","compilerArgs":["-lpthread","-o","build/app2","./src/unit_test.cpp","build/log.o","build/simpletest.o"],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -std=c++17  -I./include  -lpthread -o build/app2 ./src/unit_test.cpp build/log.o build/simpletest.o","directory":"/home/ewanseiha2.odion/iot_starter-1","file":"/home/ewanseiha2.odion/iot_starter-1/src/unit_test.cpp"}}]]}}
\ No newline at end of file
diff --git a/.vscode/dryrun.log b/.vscode/dryrun.log
new file mode 100644
index 0000000000000000000000000000000000000000..5cd357da5db6811188a649a360acab541bb2ede4
--- /dev/null
+++ b/.vscode/dryrun.log
@@ -0,0 +1,7 @@
+make --dry-run --keep-going --print-directory
+make: Entering directory '/home/ewanseiha2.odion/iot_starter-1'
+ 
+make: Nothing to be done for 'all'.
+ 
+make: Leaving directory '/home/ewanseiha2.odion/iot_starter-1'
+ 
diff --git a/README.md b/README.md
index 54b50313247f1c0d26e169f05a2fd8e9394c3835..5bbc6c578ad0c5b2abaf56aa7ced73721e2c7c12 100644
--- a/README.md
+++ b/README.md
@@ -1,93 +1,84 @@
-# iot-starter-0
 
 
+The first task I worked with function, it was divided into three part
+In the first part I implemented a function "std::string line(std::string message);" that returns a log line'd message 
+"line("[ERROR]: Invalid operation")
+// => "Invalid operation""
+I added this line function in the main_part source file and then included some tests for the line fuction 
+    std::string logMessage = "[ERROR]: Invalid Operation";
+    std::string logMessage_2 = "[INFO]: This is a log message";
+After i saved changes ,built and ran the program.
+As i encountered minor issues i fixed it. At the end It  printed "Invalid operation" in the terminal.
+In the second part of the task I implemented the function "std::string level(std::string message);" that returns a log line's log level whuch should return  
+"level("[ERROR]: Invalid operation")
+// => "ERROR""
+Once again I added the line function in the main_part source file and include some tests for the level function then saved the changes.
 
-## Getting started
+Build and run the program, then fixed any issues.Once the program builds successfully, I ran it to verify that the level function behaves as expected. It should print "ERROR" to the console in uppercase.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
 
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
 
-## Add your files
+For the third part, I implement was std::string reformat(std::string ) that reformat the log line, puttting the message first before and the log level after it in parentheses.
+firstly, i added the 'reformat' function to the main_part source file  and included some tests for the reformat function.Saved the changes followed up by building and run the program. If there were any issues, fixed them.
 
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+Once the program builds successfully,I ran it to verify that the reformat function behaves as expected. It should print success to the terminal.
 
-```
-cd existing_repo
-git remote add origin https://gitlab.uwe.ac.uk/eo2-odion/iot-starter-0.git
-git branch -M main
-git push -uf origin main
-```
+Below is a screenshot of the expected output
+![alt text](<screenshots/task 1.png>)
 
-## Integrate with your tools
 
-- [ ] [Set up project integrations](https://gitlab.uwe.ac.uk/eo2-odion/iot-starter-0/-/settings/integrations)
+For the second task I was expected to develop a simple class for process log files, intergrating the operations from task 1. For this i implemented  new files namely 'log.hpp' and 'log.cpp'
 
-## Collaborate with your team
+Firstly, I began by creating two new files, log.hpp and log.cpp, where the header file contains the interface for the logging API and the source file contains its implementation. Ensuring the header file starts with #pragma once to prevent multiple inclusions. Modifying the makefile to include these new source files by adding them to the CPP_SOURCES variable.
 
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
+ In the log.hpp file,I defined the Log class with methods for creating a log, navigating to the next message, retrieving the current line and level, and reformatting the log message. Unlike before, these methods don't take any arguments. Implementing each method in log.cpp using the Log::method_name(arguments) syntax.
 
-## Test and Deploy
+ The constructor Log() initializes the log object, while the destructor ~Log() closes the log file if it was opened successfully with create_log(). Implementing these appropriately in the log.cpp.
 
-Use the built-in continuous integration in GitLab.
+ I made use of file handling libraries such as <iostream> and <fstream> for C++ streams or <stdio.h> for C file handling to open and manipulate log files. The create_log() method opens the specified log file, returning true if successful and false otherwise.
 
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+I implemented each method defined in the Log class, ensuring that they perform their intended functions, such as moving to the next message, retrieving the line and level, and reformatting the log message.
 
-***
+I created a new main_part2.cpp file to test the implementation of the Log class.I used this file to instantiate a Log object, call its methods, and verify that they work as expected.
 
-# Editing this README
+I added a new target in the makefile to build the app2 executable for testing the Log class implementation. Ensured that this rule compiles the main_part2.cpp file along with other necessary source files.
 
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+I utilized a provided example log file (log.in) to test the implementation of the Log class and ensure that it handles log files correctly.
 
-## Suggestions for a good README
 
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+Below is a screenshot of the expected output for this task
+![alt text](screenshots/task2.png)
 
-## Name
-Choose a self-explaining name for your project.
 
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+For the third task I was expected to develop unit tests to validate the Log class implementation. For this i was expected to use the framework simpletest and add simpletest as a submodule to my git repo.
+Firstly, I added Simpletest as a submodule to your git repository using the command:
 
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+csharp
+Copy code
+git submodule add repo-url
+Replacing repo-url with the URL of the Simpletest repository.
 
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+I created a new file, simpletest_log.cpp, where I write unit tests for the Log class. Use the provided example as a template to structure the tests.
 
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+I included the log.hpp header file and the simpletest.h header file in the test_log.cpp file.
 
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+I define test groups as an array of character pointers. Each group represents a set of related tests.
 
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+Wrote test functions using the DEFINE_TEST_G macro. Each test function should have a unique name and belong to a test group.
 
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
+ Inside each test function, I instantiate the Log class, perform operations, and use TEST_MESSAGE macro to assert conditions and report test results.
 
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
+I wrote the main function to execute all test groups. Iterate through each group, execute its tests, and aggregate the test results.
+
+I modified the makefile to include a new rule for building the tests. This rule should compile the test_log.cpp file along with the log.cpp and any other necessary source files.
+
+ Once everything was set up, I built and run the tests using the appropriate commands specified in the makefile. Ensure that all tests pass successfully.
+
+
+Below is a screenshot of the expected output for this task
+![alt text](screenshots/task3.png)
 
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
 
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
 
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
 
-## License
-For open source projects, say how it is licensed.
 
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
diff --git a/build/app b/build/app
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/app2 b/build/app2
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/log.o b/build/log.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/main.o b/build/main.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/main_part2.o b/build/main_part2.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/simpletest.o b/build/simpletest.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/build/test b/build/test
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/iot-starter-0 b/iot-starter-0
new file mode 160000
index 0000000000000000000000000000000000000000..f3dfebab3c8e817ed2648a051e07f629739117b3
--- /dev/null
+++ b/iot-starter-0
@@ -0,0 +1 @@
+Subproject commit f3dfebab3c8e817ed2648a051e07f629739117b3
diff --git a/iot-starter-0-1 b/iot-starter-0-1
new file mode 160000
index 0000000000000000000000000000000000000000..f3dfebab3c8e817ed2648a051e07f629739117b3
--- /dev/null
+++ b/iot-starter-0-1
@@ -0,0 +1 @@
+Subproject commit f3dfebab3c8e817ed2648a051e07f629739117b3
diff --git a/log.in b/log.in
new file mode 100644
index 0000000000000000000000000000000000000000..86571c085f3c9447342a72a31ccb1f58a2f57120
--- /dev/null
+++ b/log.in
@@ -0,0 +1,2 @@
+[ERROR]: Invalid operation
+[INFO]: Operation completed
\ No newline at end of file
diff --git a/makeFile b/makeFile
new file mode 100644
index 0000000000000000000000000000000000000000..7dc517716c96776e6a8c745a4b49287eb1ebc093
--- /dev/null
+++ b/makeFile
@@ -0,0 +1,75 @@
+
+
+CC = clang++
+LD = clang++
+CPPFLAGS = -std=c++17  -I./include 
+LDFLAGS = -lpthread
+ROOTDIR = ./
+CP = cp
+ECHO = echo
+BUILD_DIR = build
+CPP_SOURCES = ./src/log.cpp ./simpletest/simpletest.cpp
+CPP_HEADERS = ./src/log.hpp ./simpletest/simpletest.h
+C_SOURCES = 
+APP = app
+APP2 = app2
+TEST = test
+
+
+
+OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o)))
+
+vpath %.cpp $(sort $(dir $(CPP_SOURCES)))
+vpath %.cpp src
+
+# OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
+# vpath %.c $(sort $(dir $(C_SOURCES)))
+
+$(BUILD_DIR)/%.o: %.cpp $(CPP_HEADERS) Makefile | $(BUILD_DIR)
+	$(ECHO) compiling $<
+	$(CC) -c $(CPPFLAGS) $< -o $@
+
+$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
+	$(ECHO) compiling $<
+	clang -c $(CFLAGS) $< -o $@
+
+all: $(BUILD_DIR) $(BUILD_DIR)/$(APP)  $(BUILD_DIR)/$(APP2) $(BUILD_DIR)/$(TEST) 
+
+run: $(BUILD_DIR)/$(APP) $(BUILD_DIR)/$(APP2) $(BUILD_DIR)/$(TEST) 
+	$(BUILD_DIR)/$(APP)
+	$(BUILD_DIR)/$(APP2)
+	$(BUILD_DIR)/$(TEST) 
+
+$(BUILD_DIR)/$(APP): main.cpp $(OBJECTS) Makefile
+	$(ECHO) linking $<
+	$(CC) $(CPPFLAGS) $(LDFLAGS) -o $@ ./src/main.cpp $(OBJECTS)
+	$(ECHO) success
+
+$(BUILD_DIR)/$(APP2): main_part2.cpp $(OBJECTS) Makefile
+	$(ECHO) linking $<
+	$(CC) $(CPPFLAGS) $(LDFLAGS) -o $@ ./src/main_part2.cpp $(OBJECTS)
+	$(ECHO) success
+
+$(BUILD_DIR)/$(TEST): unit_test.cpp $(OBJECTS) Makefile
+	$(ECHO) linking $<
+	$(CC) $(CPPFLAGS) $(LDFLAGS) -o $@ ./src/unit_test.cpp $(OBJECTS)
+	$(ECHO) success
+
+
+$(BUILD_DIR):
+	mkdir $@
+
+#######################################
+# Clean up
+#######################################
+clean:
+	-rm -fR $(BUILD_DIR)/$(APP) $(BUILD_DIR)/*.o
+
+#######################################
+# Dependencies
+#######################################
+-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
+
+.PHONY: clean all
+
+
diff --git a/screenshots/task 1.png b/screenshots/task 1.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f6bc9bb7bee729c722fa2efcc414f71e7a66cee
Binary files /dev/null and b/screenshots/task 1.png differ
diff --git a/screenshots/task2.png b/screenshots/task2.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b86dd5f0b4b04c74e14c9f592afcf81e11bb82a
Binary files /dev/null and b/screenshots/task2.png differ
diff --git a/screenshots/task3.png b/screenshots/task3.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c338a1eaca723ebdd787ee49679c538861e17ca
Binary files /dev/null and b/screenshots/task3.png differ
diff --git a/src/log.cpp b/src/log.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a268a95532991b64a146a914adf277fdb530d1e
--- /dev/null
+++ b/src/log.cpp
@@ -0,0 +1,86 @@
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <fstream>
+#include "log.hpp"
+
+Log::Log(){
+
+}
+Log::~Log(){
+
+}
+
+std::string Log::line(std::string message) {
+    size_t pos = message.find(':');
+    if (pos != std::string::npos) {
+        return message.substr(pos + 2);
+    } else {
+        return message;
+    }
+}
+
+
+std::string Log::level(std::string message) {
+    std::string level;
+    bool foundStart = false;
+
+    for (char& c : message) {
+        if (c == '[') {
+            foundStart = true;
+            continue;
+        }
+
+        if (c == ']' && foundStart) {
+            break;
+        }
+
+        if (foundStart) {
+            level += c;
+        }
+    }
+
+    return level;
+}
+std::string Log::reformat(std::string message) {
+    // Find the position of ':'
+    size_t colonPos = message.find(':');
+
+    if (colonPos != std::string::npos) {
+        // Extract the info between '[' and ']'
+        size_t bracketPos = message.find('[');
+        if (bracketPos != std::string::npos) {
+            std::string info = message.substr(bracketPos + 1, colonPos - bracketPos - 1);
+
+            // Remove trailing square brackets if present
+            size_t bracketEndPos = info.find(']');
+            if (bracketEndPos != std::string::npos) {
+                info.erase(bracketEndPos, 1);
+            }
+
+            // Extract the message after ':'
+            std::string msg = message.substr(colonPos + 2); // Skipping ': ' characters
+
+            // Construct the reformatted message
+            return msg + " (" + info + ")";
+        }
+    }
+
+    // Return original message if format is not as expected
+    return message;
+}
+
+bool Log::create_log(std::string filename) {
+        std::ifstream file(filename);
+        return file.good();
+    }
+
+bool Log::next(std::ifstream& file) {
+        std::string line;
+        while (std::getline(file, line)) {
+            if (!line.empty()) {
+                return true;
+            }
+        }
+        return false;
+    }
diff --git a/src/log.hpp b/src/log.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fce7344c32c3bb3921bd0fd2e83435ff01ff6923
--- /dev/null
+++ b/src/log.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <string> 
+
+class Log {
+private:
+public:
+    Log();
+    ~Log();
+    bool create_log(std::string filename);
+    bool next(std::ifstream& file);
+    std::string line(std::string message);
+    std::string level(std::string message);
+    std::string reformat(std::string message);
+};
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0079568d99365fef7c0b0203a929b2dbf352122
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,34 @@
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <fstream>
+#include "log.hpp"
+
+       
+
+int main() {
+
+    Log logger;
+
+    bool create = logger.create_log("log.in");
+    std::cout << create << std::endl;
+
+    std::ifstream file("log.in");
+
+    bool nxt = logger.next(file);
+    std::cout << nxt << std::endl;
+
+    std::string logMessage = "[ERROR]: Invalid Operation";
+    std::string logMessage_2 = "[INFO]: This is a log message";
+
+    std::string logLine = logger.line(logMessage);
+    std::cout << logLine << std::endl;
+
+    std::string logLine_2 = logger.level(logMessage);
+    std::cout << logLine_2 << std::endl;
+
+    std::string logLine_3 = logger.reformat(logMessage);
+    std::cout << logLine_3 << std::endl;
+
+    return 0;
+}
diff --git a/src/main_part2.cpp b/src/main_part2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0079568d99365fef7c0b0203a929b2dbf352122
--- /dev/null
+++ b/src/main_part2.cpp
@@ -0,0 +1,34 @@
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <fstream>
+#include "log.hpp"
+
+       
+
+int main() {
+
+    Log logger;
+
+    bool create = logger.create_log("log.in");
+    std::cout << create << std::endl;
+
+    std::ifstream file("log.in");
+
+    bool nxt = logger.next(file);
+    std::cout << nxt << std::endl;
+
+    std::string logMessage = "[ERROR]: Invalid Operation";
+    std::string logMessage_2 = "[INFO]: This is a log message";
+
+    std::string logLine = logger.line(logMessage);
+    std::cout << logLine << std::endl;
+
+    std::string logLine_2 = logger.level(logMessage);
+    std::cout << logLine_2 << std::endl;
+
+    std::string logLine_3 = logger.reformat(logMessage);
+    std::cout << logLine_3 << std::endl;
+
+    return 0;
+}
diff --git a/src/simpletest.cpp b/src/simpletest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85abbb4ca2c9ed544e51e155e74f6f2bfe4422fc
--- /dev/null
+++ b/src/simpletest.cpp
@@ -0,0 +1,391 @@
+#include "simpletest.h"
+#include <stdio.h>
+#include <string.h>
+#include <cstdint>
+#include <stdarg.h>
+#include <time.h>
+
+//---------------------------------------------------------------------------------
+// statics
+//---------------------------------------------------------------------------------
+void DefaultPrint(char const* string) { printf("%s", string); }
+
+TestFixture* TestFixture::ourFirstTest;
+TestFixture* TestFixture::ourLastTest;
+thread_local TestFixture* TestFixture::ourCurrentTest;
+void (*TestFixture::Print)(char const* string) = DefaultPrint;
+
+//---------------------------------------------------------------------------------
+// Standard type printers
+//---------------------------------------------------------------------------------
+TempString::TempString(const TempString& other)
+{
+	if (other.myTextPointer == other.myTextBuffer)
+	{
+		strcpy(myTextBuffer, other.myTextBuffer);
+		myTextPointer = myTextBuffer;
+	}
+	else
+	{
+		myTextPointer = other.myTextPointer;
+	}
+}
+TempString TypeToString(int value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%08X", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%d", value);
+	return tempString;
+}
+TempString TypeToString(unsigned int value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%08X", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%u", value);
+	return tempString;
+}
+TempString TypeToString(long value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%016lX", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%ld", value);
+	return tempString;
+}
+TempString TypeToString(unsigned long value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%016lX", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%lu", value);
+	return tempString;
+}
+TempString TypeToString(long long value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%016llX", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%lld", value);
+	return tempString;
+}
+TempString TypeToString(unsigned long long value)
+{
+	TempString tempString;
+	if (TestFixture::GetCurrentTest()->GetPrintMethod() == TestFixture::PrintHexadecimal)
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%016llX", value);
+	else
+		snprintf(tempString.myTextBuffer, STRING_LENGTH, "%llu", value);
+	return tempString;
+}
+TempString TypeToString(float value)
+{
+	TempString tempString;
+	snprintf(tempString.myTextBuffer, STRING_LENGTH, "%.16g", value);
+	return tempString;
+}
+TempString TypeToString(double value)
+{
+	TempString tempString;
+	snprintf(tempString.myTextBuffer, STRING_LENGTH, "%.16g", value);
+	return tempString;
+}
+TempString TypeToString(bool value)
+{
+	return TempString(value ? "true" : "false");
+}
+TempString TypeToString(char const* value)
+{
+	return TempString(value ? value : "(nullptr)");
+}
+TempString TypeToString(void const* value)
+{
+	if (value == nullptr)
+		return TempString("(nullptr)");
+	TempString tempString;
+	snprintf(tempString.myTextBuffer, STRING_LENGTH, "0x%p", value);
+	return tempString;
+}
+TempString TypeToString(void const* value, char const* extra)
+{
+	if (value == nullptr)
+		return TempString("(nullptr)");
+	TempString tempString;
+	snprintf(tempString.myTextBuffer, STRING_LENGTH, "(0x%p) %s", value, extra);
+	return tempString;
+}
+
+//---------------------------------------------------------------------------------
+// Fixture implementation
+//---------------------------------------------------------------------------------
+TestFixture::TestFixture()
+	: myNextTest(nullptr)
+	, myNextError(nullptr)
+	, myNumTestsChecked(0)
+	, myNumErrors(0)
+	, myPrintMethod(PrintDefault)
+{
+	// global link list registration, add in order of discovery
+	if (ourFirstTest == nullptr)
+	{
+		ourFirstTest = this;
+		ourLastTest = this;
+	}
+	else
+	{
+		ourLastTest->myNextTest = this;
+		ourLastTest = this;
+	}
+}
+//---------------------------------------------------------------------------------
+bool TestFixture::ExecuteTest()
+{
+	myNumTestsChecked = myNumErrors = 0;
+	myNextError = (TestError*)myMessageSpace;
+
+	TestFixture* lastCurrent = ourCurrentTest;
+	ourCurrentTest = this;
+	Setup();
+	RunTest();
+	TearDown();
+	ourCurrentTest = lastCurrent;
+
+	return myNumErrors == 0;
+}
+//---------------------------------------------------------------------------------
+// Utility to print a part of a string to show where the error is and put elipse
+// where the string is truncated
+//---------------------------------------------------------------------------------
+static void locCopyStringWithElipse(char dest[STRING_EQ_PRINT_LENGTH], char const* string, size_t offset = 0)
+{
+	char const* start = string + offset - STRING_EQ_PRINT_LENGTH / 2;
+	if (start < string)
+		start = string;
+
+	int i = 0;
+	for (; i < STRING_EQ_PRINT_LENGTH - 1 && start[i]; ++i)
+	{
+		if (i < 3 && start > string)
+			dest[i] = '.';
+		else if (start[i] == '\r' || start[i] == '\n' || start[i] == '\t')
+			dest[i] = '\\'; // simply replace this with '\', we're just aiming for a general idea not an exact representation
+		else
+			dest[i] = start[i];
+	}
+
+	dest[i] = 0;
+
+	if (i == STRING_EQ_PRINT_LENGTH - 1 && start[i])
+	{
+		dest[i - 1] = '.';
+		dest[i - 2] = '.';
+		dest[i - 3] = '.';
+	}
+}
+//---------------------------------------------------------------------------------
+// Instead of just check for error and printing the string, try go be smart about
+// how the information is written out:
+// ... quick brown fox jumps over ...
+//                      ^
+// ... quick brown fox jamps over ...
+//---------------------------------------------------------------------------------
+bool TestFixture::TestStrings(char const* left, char const* right, char const* prefix, char const* condition)
+{
+	AddTest();
+	if (left == right)
+	{
+		return true;
+	}
+
+	char leftLine[STRING_EQ_PRINT_LENGTH];
+	char rightLine[STRING_EQ_PRINT_LENGTH];
+	char locationLine[STRING_EQ_PRINT_LENGTH];
+
+	if (left == nullptr || right == nullptr)
+	{
+		locationLine[0] = '^';
+		locationLine[1] = 0;
+		if (left == nullptr)
+		{
+			strcpy(leftLine, "nullptr");
+			locCopyStringWithElipse(rightLine, right);
+		}
+		else
+		{
+			locCopyStringWithElipse(leftLine, left);
+			strcpy(rightLine, "nullptr");
+		}
+	}
+	else
+	{
+		char const* testLeft = left;
+		char const* testRight = right;
+
+		int offset = 0;
+		for (; *testLeft && *testRight; ++offset, ++testLeft, ++testRight)
+		{
+			if (*testLeft != *testRight)
+				break;
+		}
+
+		// reached the end of both strings, so they're the same
+		if (!*testLeft && !*testRight)
+			return true;
+
+		locCopyStringWithElipse(leftLine, left, offset);
+		locCopyStringWithElipse(rightLine, right, offset);
+
+		if (offset > STRING_EQ_PRINT_LENGTH / 2)
+			offset = STRING_EQ_PRINT_LENGTH / 2;
+
+		memset(locationLine, ' ', offset);
+		locationLine[offset] = '^';
+		locationLine[offset + 1] = 0;
+	}
+
+	AddError();
+	LogMessage(prefix, condition, leftLine, locationLine, rightLine);
+	return false;
+}
+//---------------------------------------------------------------------------------
+// Write error into current error object and advance pointer if there's still enough space
+//---------------------------------------------------------------------------------
+void TestFixture::LogMessage(char const* string, ...)
+{
+	uintptr_t spaceLeft = (myMessageSpace + MESSAGE_SPACE) - (char*)myNextError;
+
+	if (spaceLeft == 0)
+	{
+		return;
+	}
+
+	spaceLeft -= sizeof(TestError);
+
+	va_list args;
+
+	va_start(args, string);
+
+	int printedChars = vsnprintf(myNextError->message, spaceLeft, string, args);
+
+	va_end(args);
+
+	// if there isn't a reasonable amount of space left then just advance to end and stop printing errors
+	if (printedChars < (int)(spaceLeft - sizeof(TestError) - 64))
+	{
+		uintptr_t nextOffset = (uintptr_t(myNextError->message) + printedChars + sizeof(TestError) * 2 - 1);
+		nextOffset -= nextOffset % alignof(TestError);
+		TestError* next = (TestError*)(nextOffset);
+		myNextError->next = next;
+	}
+	else
+	{
+		myNextError->next = (TestError*)(myMessageSpace + MESSAGE_SPACE);
+	}
+
+	myNextError = myNextError->next;
+}
+TestFixture const* TestFixture::LinkTest(TestFixture* test)
+{
+	test->myNextTest = TestFixture::ourFirstTest;
+	TestFixture::ourFirstTest = test;
+	return test;
+}
+
+//---------------------------------------------------------------------------------
+// Standard / Example runners
+//---------------------------------------------------------------------------------
+static bool locExecuteTest(TestFixture* test, TestFixture::OutputMode output)
+{
+	clock_t start = 0;
+	if (output == TestFixture::Verbose)
+	{
+		TestFixture::Printf("Running [%s/%s]", test->TestGroup(), test->TestName());
+		start = clock();
+	}
+
+	if (test->ExecuteTest())
+	{
+		if (output == TestFixture::Verbose)
+		{
+			clock_t end = clock();
+			TestFixture::Printf(": Passed %d out of %d tests in %g seconds\n", test->NumTests(), test->NumTests(), float(end - start) / (float)CLOCKS_PER_SEC);
+		}
+		return true;
+	}
+
+	if (output != TestFixture::Silent)
+	{
+		if (output != TestFixture::Verbose)
+			TestFixture::Printf("[%s/%s]", test->TestGroup(), test->TestName());
+
+		TestFixture::Printf(": Failed %d out of %d tests\n", test->NumErrors(), test->NumTests());
+
+		for (TestError const* err = test->GetFirstError(), *e = test->GetLastError(); err != e; err = err->next)
+		{
+			TestFixture::Printf("%s\n", err->message);
+		}
+	}
+
+	return false;
+}
+void TestFixture::Printf(char const* string, ...)
+{
+	char tempSpace[4096];
+	va_list args;
+
+	va_start(args, string);
+
+	vsnprintf(tempSpace, sizeof(tempSpace), string, args);
+
+	va_end(args);
+
+	TestFixture::Print(tempSpace);
+}
+bool TestFixture::ExecuteAllTests(char const* groupFilter, char const* nameFilter, OutputMode output)
+{
+	if (output != Silent)
+	{
+		if (groupFilter == nullptr && nameFilter == nullptr)
+			Printf("Running all tests.\n");
+		else if (groupFilter != nullptr && nameFilter == nullptr)
+			Printf("Running all tests in groups [%s].\n", groupFilter);
+		else if (groupFilter == nullptr && nameFilter != nullptr)
+			Printf("Running all tests named [%s].\n", nameFilter);
+		else
+			Printf("Running all tests named [%s/%s].\n", groupFilter, nameFilter);
+	}
+
+	int count = 0;
+	int passes = 0;
+	int fails = 0;
+	bool passed = true;
+	for (auto i = TestFixture::GetFirstTest(); i; i = i->GetNextTest())
+	{
+		bool matchGroup = groupFilter == nullptr || strcmp(groupFilter, i->TestGroup()) == 0;
+		bool matchName = nameFilter == nullptr || strcmp(nameFilter, i->TestName()) == 0;
+		if (matchGroup && matchName)
+		{
+			++count;
+			passed &= locExecuteTest(i, output);
+			passes += i->NumTests();
+			fails += i->NumErrors();
+		}
+	}
+
+	if (output != Silent)
+	{
+		if (count == 0)
+			Printf("Failed to find any tests.\n");
+		else if (passed)
+			Printf("%d Tests finished. All %d assertions are passing.\n", count, passes);
+		else
+			Printf("%d Tests finished, %d of %d assertions failed. Some tests are reporting errors.\n", count, fails, passes);
+	}
+	return passed;
+}
diff --git a/src/simpletest.h b/src/simpletest.h
new file mode 100644
index 0000000000000000000000000000000000000000..46595614d472ea3053bf71fff224951a6c4a4376
--- /dev/null
+++ b/src/simpletest.h
@@ -0,0 +1,235 @@
+#pragma once
+
+//---------------------------------------------------------------------------------
+// Config
+//---------------------------------------------------------------------------------
+#if !defined(MESSAGE_SPACE)
+#define MESSAGE_SPACE 10 * 1024 // default 10k of message space is reserved per test
+#endif
+#if !defined(STRING_LENGTH)
+#define STRING_LENGTH 64 // size of temp strings for converting types
+#endif
+#if !defined(STRING_EQ_PRINT_LENGTH)
+#define STRING_EQ_PRINT_LENGTH 80 // max line length to show when comparing two strings
+#endif
+#if !defined(BASE_FIXTURE)
+#define BASE_FIXTURE TestFixture // use TestFixture as the test base class by default
+#endif
+#if !defined(ERROR_ACTION)
+#define ERROR_ACTION // Defined any code to run on error. You can use this to debug break or do anything really
+#endif
+
+//---------------------------------------------------------------------------------
+// Link list of errors build into MESSAGE_SPACE
+//---------------------------------------------------------------------------------
+struct TestError
+{
+	TestError* next;
+	char message[1];
+};
+
+//---------------------------------------------------------------------------------
+// simple converter of basic types to text
+// TODO: Use a global template function for conversion so users can override this
+//---------------------------------------------------------------------------------
+struct TempString
+{
+	TempString() : myTextPointer(myTextBuffer) { myTextBuffer[0] = 0; }
+	TempString(const TempString& other);
+	TempString(char const* string) : myTextPointer(string) {}
+
+	char const* operator*() const { return myTextPointer; }
+
+	char const* myTextPointer;
+	char myTextBuffer[STRING_LENGTH];
+};
+
+TempString TypeToString(int value);
+TempString TypeToString(unsigned int value);
+TempString TypeToString(long value);
+TempString TypeToString(unsigned long value);
+TempString TypeToString(long long value);
+TempString TypeToString(unsigned long long value);
+TempString TypeToString(float value);
+TempString TypeToString(double value);
+TempString TypeToString(bool value);
+TempString TypeToString(char const* value);
+TempString TypeToString(void const* value);
+TempString TypeToString(void const* value, char const* extra);
+
+inline TempString TypeToString(char value) { return TypeToString((int)value); }
+inline TempString TypeToString(unsigned char value) { return TypeToString((unsigned int)value); }
+inline TempString TypeToString(short value) { return TypeToString((int)value); }
+inline TempString TypeToString(unsigned short value) { return TypeToString((unsigned int)value); }
+inline TempString TypeToString(char* value) { return TypeToString((char const*)value); }
+inline TempString TypeToString(void* value) { return TypeToString((void const*)value); }
+
+// if nothing specified then print some memory
+template<typename T>
+TempString TypeToString(T const&) { return TempString(); }
+
+template<typename T>
+TempString TypeToString(T const* pointer)
+{
+	return pointer == nullptr ?
+		TypeToString((void const*)pointer) :
+		TypeToString((void const*)pointer, *TypeToString(*pointer));
+}
+
+template<typename T>
+TempString TypeToString(T* pointer) { return TypeToString((T const*)pointer); }
+
+inline TempString TypeToStringFallback(TempString string, char const* fallback) { return (*string)[0] ?  string : TempString(fallback); }
+
+//---------------------------------------------------------------------------------
+// Test fixture is the core of SimpleTest. It provides fixture behavior, access
+// to registered tests and stores the results of a test run
+// Everything is local here so tests can be multithreaded without any extra work
+//---------------------------------------------------------------------------------
+class TestFixture
+{
+public:
+	TestFixture();
+	virtual ~TestFixture() {};
+
+	virtual bool ExecuteTest();
+
+	virtual char const* TestName() const = 0;
+	virtual char const* TestGroup() const = 0;
+
+	// Reporting used during testing process
+	void AddTest() { ++myNumTestsChecked; }
+	void AddError() { ++myNumErrors; }
+	void LogMessage(char const* string, ...);
+
+	// Custom test for strings to print out where the comparison failed
+	bool TestStrings(char const* left, char const* right, char const* prefix, char const* condition);
+
+	// Stats from execution
+	int NumTests() const { return myNumTestsChecked; }
+	int NumErrors() const { return myNumErrors; }
+
+	// Access to any errrors generated
+	TestError const* GetFirstError() const { return (TestError*)myMessageSpace; }
+	TestError const* GetLastError() const { return myNextError; }
+
+	// Access to registered tests
+	static TestFixture* GetFirstTest() { return ourFirstTest; }
+	static TestFixture* GetCurrentTest() { return ourCurrentTest; }
+	TestFixture* GetNextTest() const { return myNextTest; }
+
+	enum OutputMode
+	{
+		Silent,
+		Normal,
+		Verbose
+	};
+
+	enum PrintMethod
+	{
+		PrintDefault,
+		PrintHexadecimal,
+	};
+
+	PrintMethod GetPrintMethod() const { return myPrintMethod; }
+	void SetPrintMethod(PrintMethod aPrintMethod) { myPrintMethod = aPrintMethod; }
+
+	// Default execution implementation
+	static void (*Print)(char const* string);
+	static void Printf(char const* string, ...);
+
+	static bool ExecuteAllTests(char const* groupFilter = nullptr, char const* nameFilter = nullptr, OutputMode output = Normal);
+	static bool ExecuteAllTests(OutputMode output) { return ExecuteAllTests(nullptr, nullptr, output); }
+
+	static bool ExecuteTestGroup(char const* groupFilter, OutputMode output = Normal) { return ExecuteAllTests(groupFilter, nullptr, output); }
+
+protected:
+	virtual void RunTest() = 0;
+	virtual void Setup() {}
+	virtual void TearDown() {}
+	
+	// Test registration
+	static TestFixture const* LinkTest(TestFixture* test);
+	static TestFixture* ourFirstTest;
+	static TestFixture* ourLastTest;
+
+	TestFixture* myNextTest;
+	TestError* myNextError;
+
+	int myNumTestsChecked;
+	int myNumErrors;
+
+	PrintMethod myPrintMethod;
+
+	char myMessageSpace[MESSAGE_SPACE];
+
+	// allow access to current test outside of main code block
+	static thread_local TestFixture* ourCurrentTest;
+};
+
+//---------------------------------------------------------------------------------
+// Test definition macros
+//---------------------------------------------------------------------------------
+#define DEFINE_TEST_FULL(name, group, fixture) \
+struct TOK(group, name) final : public fixture { \
+	char const* TestName() const override { return #name; } \
+	char const* TestGroup() const override { return #group; } \
+	void RunTest() override; \
+} TOK(TOK(group, name), Instance); \
+void TOK(group, name)::RunTest()
+
+#define DEFINE_TEST(name) DEFINE_TEST_FULL(name, Global, BASE_FIXTURE)
+#define DEFINE_TEST_G(name, group) DEFINE_TEST_FULL(name, group, BASE_FIXTURE)
+#define DEFINE_TEST_F(name, fixture) DEFINE_TEST_FULL(name, Global, fixture)
+#define DEFINE_TEST_GF(name, group, fixture) DEFINE_TEST_FULL(name, group, fixture)
+
+//---------------------------------------------------------------------------------
+// Utils
+//---------------------------------------------------------------------------------
+template <typename T>
+T TestDifference(T const& a, T const& b) { return a > b ? a - b : b - a; }
+
+// why are these still needed?
+#define STR2(x) #x
+#define STR(x) STR2(x)
+
+#define TOK2(a, b) a ## b
+#define TOK(a, b) TOK2(a, b)
+
+//---------------------------------------------------------------------------------
+// Error reporting and setup, don't call directly
+//---------------------------------------------------------------------------------
+#define TEST_TYPE_TO_STRING(var, arg) *TypeToStringFallback(TypeToString(var), STR(arg))
+#define TEST_ERROR_PREFIX_ __FILE__ "(" STR(__LINE__) "): Condition [%s] Failed. "
+#define TEST_ERROR_(message, ...) do { TestFixture* __fx = TestFixture::GetCurrentTest(); __fx->AddError(); __fx->LogMessage(TEST_ERROR_PREFIX_ message, ##__VA_ARGS__); ERROR_ACTION; } while(0)
+#define TEST_BEGIN_(a) do { auto const& test_value_ = a
+#define TEST_CHECK_(cond, condtext, message, ...) do { TestFixture::GetCurrentTest()->AddTest(); if (!(cond)) TEST_ERROR_(message, condtext, ##__VA_ARGS__); } while(0)
+#define TEST_END_ } while(0)
+
+//---------------------------------------------------------------------------------
+// Tests
+//
+// Note: Value caching is only enabled on left hand side. This splits the difference
+// between preventing side effects (i.e. x++ double incrementing) and allowing the
+// compiler to infer values (i.e. TEST_EQ(unsigned(1), 1) will try to cache 1 as an int then omit a compile warning).
+// This means that the right hand side will get evaluated multiple times, so please avoid
+// expressions like: TEST_EQ(a++, b++) as they won't work. Tests should always be written
+// as following:
+// TEST_EQ(expression, constant)
+//---------------------------------------------------------------------------------
+#define TEST_OPERATOR(a, b, op1, op2) TEST_BEGIN_(a); TEST_CHECK_((test_value_) op1 (b), STR(a) " " STR(op1) " " STR(b), "'%s' " STR(op2) " '%s'", TEST_TYPE_TO_STRING(test_value_, a), TEST_TYPE_TO_STRING(b, b)); TEST_END_
+
+#define TEST(cond) TEST_EQ(cond, true)
+#define TEST_FAIL(cond) TEST_EQ(cond, false)
+
+#define TEST_EQ(a, b) TEST_OPERATOR(a, b, ==, !=)
+#define TEST_NEQ(a, b) TEST_OPERATOR(a, b, !=, ==)
+#define TEST_GREATER(a, b) TEST_OPERATOR(a, b, >, <=)
+#define TEST_GREATER_EQUAL(a, b) TEST_OPERATOR(a, b, >=, <)
+#define TEST_LESS(a, b) TEST_OPERATOR(a, b, <, >=)
+#define TEST_LESS_EQUAL(a, b) TEST_OPERATOR(a, b, <=, >)
+
+#define TEST_STR_EQ(a, b) do { if(!TestFixture::GetCurrentTest()->TestStrings(a, b, TEST_ERROR_PREFIX_ "\n%s\n%s\n%s", STR(a) " == " STR(b))) { ERROR_ACTION; } } while(0)
+#define TEST_CLOSE(a, b, eps) TEST_BEGIN_(TestDifference(a, b)); TEST_CHECK_(test_value_ <= eps, STR(a) " Close to " STR(b), "Difference of %s is greater than expected amount of " STR(eps) " when comparing %s and %s", TEST_TYPE_TO_STRING(test_value_, TestDifference(a, b)), TEST_TYPE_TO_STRING(a, a), TEST_TYPE_TO_STRING(b, b)); TEST_END_
+#define TEST_DIFFERS(a, b, eps) TEST_BEGIN_(TestDifference(a, b)); TEST_CHECK_(test_value_ >= eps, STR(a) " Differs from " STR(b), "Difference of %s is less than expected amount of " STR(eps) " when comparing %s and %s", TEST_TYPE_TO_STRING(test_value_, TestDifference(a, b)), TEST_TYPE_TO_STRING(a, a), TEST_TYPE_TO_STRING(b, b)); TEST_END_
+#define TEST_MESSAGE(cond, message, ...) TEST_BEGIN_(cond); TEST_CHECK_(test_value_, STR(cond), message, ##__VA_ARGS__); TEST_END_
diff --git a/src/unit_test.cpp b/src/unit_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d21a935a31fdf5a413f18ca3506cc203f584d79
--- /dev/null
+++ b/src/unit_test.cpp
@@ -0,0 +1,34 @@
+#include "log.hpp"
+#include "simpletest.h"
+#include <iostream>
+#include <fstream>
+
+DEFINE_TEST_G(Log, LogTests) {
+    Log log;
+    bool success = log.create_log("./log.in");
+    TEST_MESSAGE(success == true, "Failed to open log!!!!");
+    std::cout << success << std::endl;
+
+    // Open the file before passing it to the next function
+    std::ifstream file("./log.in");
+    success = log.next(file); // Pass the file object to the next function
+    std::cout  << success << std::endl;
+    TEST_MESSAGE(success == true, "Failed to read log!!!!");
+
+    std::string line = log.line("[ERROR]: Invalid operation");
+    std::cout  << line << std::endl;
+    TEST_MESSAGE(line.compare(" Invalid operation") == 0, "Expecting Invalid operation!!!!");
+
+    std::string level = log.level("[ERROR]: Invalid operation");
+    std::cout << level << std::endl;
+    TEST_MESSAGE(level.compare("ERROR") == 0, "Expecting ERROR level!!!!");
+
+    std::string reformatted = log.reformat("[INFO]: This is a log message");
+    std::cout << reformatted << std::endl;
+    TEST_MESSAGE(reformatted.compare("This is a log message (INFO)") == 0, "Expecting reformatted message!!!!");
+}
+
+int main() {
+    TestFixture::ExecuteTestGroup("LogTests", TestFixture::Verbose);
+    return 0;
+}
\ No newline at end of file