diff --git a/3-StudentDB/.debug/launch.json b/3-StudentDB/.debug/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c0e3d562a9de1dc3580eea333d09ca1f2dd612f
--- /dev/null
+++ b/3-StudentDB/.debug/launch.json
@@ -0,0 +1,29 @@
+{
+    "configurations": [
+        {
+            "name": "(gdb) 3-ShellP1",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/3-ShellP1/starter/dsh",
+            "args": [""],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}/3-ShellP1/starter",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "description": "Set Disassembly Flavor to Intel",
+                    "text": "-gdb-set disassembly-flavor intel",
+                    "ignoreFailures": true
+                }
+            ],
+            "preLaunchTask": "Build 3-ShellP1"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/3-StudentDB/.debug/tasks.json b/3-StudentDB/.debug/tasks.json
new file mode 100644
index 0000000000000000000000000000000000000000..bfa4a30202ee10d0f7a07a392358ded10a1a2e35
--- /dev/null
+++ b/3-StudentDB/.debug/tasks.json
@@ -0,0 +1,20 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+      {
+        "label": "Build 3-ShellP1",
+        "type": "shell",
+        "command": "make",
+        "group": {
+          "kind": "build",
+          "isDefault": true
+        },
+        "options": {
+          "cwd": "${workspaceFolder}/3-ShellP1/starter"
+      },
+        "problemMatcher": ["$gcc"],
+        "detail": "Runs the 'make' command to build the project."
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/3-StudentDB/dsh_cli.c b/3-StudentDB/dsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..9bb79da00b0b391f516eb19b273c407d20532336
--- /dev/null
+++ b/3-StudentDB/dsh_cli.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dshlib.h"
+#define DRAGON_ASCII "\
+                                                                        @%%%%                       \n\
+                                                                     %%%%%%                         \n\
+                                                                    %%%%%%                          \n\
+                                                                 % %%%%%%%           @              \n\
+                                                                %%%%%%%%%%        %%%%%%%           \n\
+                                       %%%%%%%  %%%%@         %%%%%%%%%%%%@    %%%%%%  @%%%%        \n\
+                                  %%%%%%%%%%%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%%%%%%%%%%%%          \n\
+                                %%%%%%%%%%%%%%%%%%%%%%%%%%   %%%%%%%%%%%% %%%%%%%%%%%%%%%           \n\
+                               %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%     %%%            \n\
+                             %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%%        %%            \n\
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%                \n\
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              \n\
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@              \n\
+      %%%%%%%%@           %%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%      %%                \n\
+    %%%%%%%%%%%%%         %%@%%%%%%%%%%%%           %%%%%%%%%%% %%%%%%%%%%%%      @%                \n\
+  %%%%%%%%%%   %%%        %%%%%%%%%%%%%%            %%%%%%%%%%%%%%%%%%%%%%%%                        \n\
+ %%%%%%%%%       %         %%%%%%%%%%%%%             %%%%%%%%%%%%@%%%%%%%%%%%                       \n\
+%%%%%%%%%@                % %%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%                     \n\
+%%%%%%%%@                 %%@%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  \n\
+%%%%%%%@                   %%%%%%%%%%%%%%%           %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              \n\
+%%%%%%%%%%                  %%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%      %%%%  \n\
+%%%%%%%%%@                   @%%%%%%%%%%%%%%         %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%%   %%%%%%%%\n\
+%%%%%%%%%%                  %%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%% %%%%%%%%%\n\
+%%%%%%%%%@%%@                %%%%%%%%%%%%%%%%@       %%%%%%%%%%%%%%     %%%%%%%%%%%%%%%%%%%%%%%%  %%\n\
+ %%%%%%%%%%                  % %%%%%%%%%%%%%%@        %%%%%%%%%%%%%%   %%%%%%%%%%%%%%%%%%%%%%%%%% %%\n\
+  %%%%%%%%%%%%  @           %%%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%% \n\
+   %%%%%%%%%%%%% %%  %  %@ %%%%%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%% \n\
+    %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%           @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%%%%%% \n\
+     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              %%%%%%%%%%%%%%%%%%%%%%%%%%%%        %%%   \n\
+      @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  %%%%%%%%%%%%%%%%%%%%%%%%%               \n\
+        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%%%%%%  %%%%%%%          \n\
+           %%%%%%%%%%%%%%%%%%%%%%%%%%                           %%%%%%%%%%%%%%%  @%%%%%%%%%         \n\
+              %%%%%%%%%%%%%%%%%%%%           @%@%                  @%%%%%%%%%%%%%%%%%%   %%%        \n\
+                  %%%%%%%%%%%%%%%        %%%%%%%%%%                    %%%%%%%%%%%%%%%    %         \n\
+                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%            \n\
+                %%%%%%%%%%%%%%%%%%%%%%%%%%  %%%% %%%                      %%%%%%%%%%  %%%@          \n\
+                     %%%%%%%%%%%%%%%%%%% %%%%%% %%                          %%%%%%%%%%%%%@          \n\
+                                                                                 %%%%%%%@        \n"
+/*
+ * Implement your main function by building a loop that prompts the
+ * user for input.  Use the SH_PROMPT constant from dshlib.h and then
+ * use fgets to accept user input.  Since we want fgets to also handle
+ * end of file so we can run this headless for testing we need to check
+ * the return code of fgets.  I have provided an example below of how
+ * to do this assuming you are storing user input inside of the cmd_buff
+ * variable.
+ *
+ *      while(1){
+ *        printf("%s", SH_PROMPT);
+ *        if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){
+ *           printf("\n");
+ *           break;
+ *        }
+ *        //remove the trailing \n from cmd_buff
+ *        cmd_buff[strcspn(cmd_buff,"\n")] = '\0';
+ *
+ *        //IMPLEMENT THE REST OF THE REQUIREMENTS
+ *      }
+ *
+ *   Also, use the constants in the dshlib.h in this code.
+ *      SH_CMD_MAX              maximum buffer size for user input
+ *      EXIT_CMD                constant that terminates the dsh program
+ *      SH_PROMPT               the shell prompt
+ *      OK                      the command was parsed properly
+ *      WARN_NO_CMDS            the user command was empty
+ *      ERR_TOO_MANY_COMMANDS   too many pipes used
+ *
+ *   Expected output:
+ *
+ *      CMD_OK_HEADER      if the command parses properly. You will
+ *                         follow this by the command details
+ *
+ *      CMD_WARN_NO_CMD    if the user entered a blank command
+ *      CMD_ERR_PIPE_LIMIT if the user entered too many commands using
+ *                         the pipe feature, e.g., cmd1 | cmd2 | ... |
+ *
+ *  See the provided test cases for output expectations.
+ */
+    // Structure definitions
+
+int main() 
+{
+    char *cmd_buff = malloc(SH_CMD_MAX);
+    int rc;
+    command_list_t clist;
+
+    while (1) {
+        printf("%s", SH_PROMPT);
+        if (!fgets(cmd_buff, SH_CMD_MAX, stdin)) 
+        {
+            printf("\n");
+            break;
+        }
+        cmd_buff[strcspn(cmd_buff, "\n")] = '\0';
+        if (strcmp(cmd_buff, "dragon") == 0) 
+        {
+            puts(DRAGON_ASCII);
+            continue;
+        }
+        if (*cmd_buff == '\0') 
+        {
+            printf("%s\n", CMD_WARN_NO_CMD);
+            continue;
+        }
+        if (strcmp(cmd_buff, EXIT_CMD) == 0) 
+        {
+            break;
+        }
+        rc = build_cmd_list(cmd_buff, &clist);
+        if (rc == ERR_TOO_MANY_COMMANDS) 
+        {
+            printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
+            continue;
+        }
+        printf("PARSED COMMAND LINE - TOTAL COMMANDS %d\n", clist.num);
+        for (int i = 0; i < clist.num; i++) {
+            printf("<%d>%s", i + 1, clist.commands[i].exe);
+            
+            if (clist.commands[i].args[0] != '\0') {
+                printf("[%s]", clist.commands[i].args);
+            }
+            
+            printf("\n");
+        }
+    }
+    
+    free(cmd_buff);
+    return 0;
+}
+    
\ No newline at end of file
diff --git a/3-StudentDB/dshlib.c b/3-StudentDB/dshlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b16bb96ab0991504c161ae23a90ec5583d51dc6
--- /dev/null
+++ b/3-StudentDB/dshlib.c
@@ -0,0 +1,108 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "dshlib.h"
+
+/*
+ *  build_cmd_list
+ *    cmd_line:     the command line from the user
+ *    clist *:      pointer to clist structure to be populated
+ *
+ *  This function builds the command_list_t structure passed by the caller
+ *  It does this by first splitting the cmd_line into commands by spltting
+ *  the string based on any pipe characters '|'.  It then traverses each
+ *  command.  For each command (a substring of cmd_line), it then parses
+ *  that command by taking the first token as the executable name, and
+ *  then the remaining tokens as the arguments.
+ *
+ *  NOTE your implementation should be able to handle properly removing
+ *  leading and trailing spaces!
+ *
+ *  errors returned:
+ *
+ *    OK:                      No Error
+ *    ERR_TOO_MANY_COMMANDS:   There is a limit of CMD_MAX (see dshlib.h)
+ *                             commands.
+ *    ERR_CMD_OR_ARGS_TOO_BIG: One of the commands provided by the user
+ *                             was larger than allowed, either the
+ *                             executable name, or the arg string.
+ *
+ *  Standard Library Functions You Might Want To Consider Using
+ *      memset(), strcmp(), strcpy(), strtok(), strlen(), strchr()
+ */
+int build_cmd_list(char *cmd_line, command_list_t *clist) 
+{
+    if (!cmd_line || !*cmd_line || !clist) {
+        return WARN_NO_CMDS;
+    }
+    clist->num = 0;
+    char *cmd_copy = strdup(cmd_line);
+    if (!cmd_copy) {
+        return 0;
+    }
+    char *pipe_ptr = cmd_copy;
+    size_t pipe_count = 0;
+    while ((pipe_ptr = strchr(pipe_ptr, PIPE_CHAR))) {
+        pipe_count++;
+        pipe_ptr++;
+    }
+    if (pipe_count >= CMD_MAX) {
+        free(cmd_copy);
+        return ERR_TOO_MANY_COMMANDS;
+    }
+    char *curr_pos = cmd_copy;
+    char *next_pipe;
+    while (curr_pos && *curr_pos && clist->num < CMD_MAX) {
+        next_pipe = strchr(curr_pos, PIPE_CHAR);
+        if (next_pipe) {
+            *next_pipe = '\0';
+        }
+        while (*curr_pos && isspace((unsigned char)*curr_pos)) {
+            curr_pos++;
+        }
+        if (!*curr_pos) {
+            if (next_pipe) {
+                curr_pos = next_pipe + 1;
+                continue;
+            }
+            break;
+        }
+        char *end = curr_pos + strlen(curr_pos) - 1;
+        while (end > curr_pos && isspace((unsigned char)*end)) {
+            *end-- = '\0';
+        }
+        char *space_pos = strchr(curr_pos, SPACE_CHAR);
+        char *cmd = curr_pos;
+        char *args = NULL;
+        if (space_pos) {
+            *space_pos = '\0';
+            args = space_pos + 1;
+            
+            while (*args && isspace((unsigned char)*args)) {
+                args++;
+            }
+        }
+        if (strlen(cmd) >= EXE_MAX || (args && strlen(args) >= ARG_MAX)) {
+            free(cmd_copy);
+            return ERR_CMD_OR_ARGS_TOO_BIG;
+        }
+        
+        command_t *current_cmd = &clist->commands[clist->num];
+        
+        strncpy(current_cmd->exe, cmd, EXE_MAX - 1);
+        current_cmd->exe[EXE_MAX - 1] = '\0';
+        
+        if (args) {
+            strncpy(current_cmd->args, args, ARG_MAX - 1);
+            current_cmd->args[ARG_MAX - 1] = '\0';
+        } else {
+            current_cmd->args[0] = '\0';
+        }
+        clist->num++;
+        curr_pos = next_pipe ? (next_pipe + 1) : NULL;
+    }
+    free(cmd_copy);
+    return clist->num > 0 ? OK : WARN_NO_CMDS;
+}
\ No newline at end of file
diff --git a/3-StudentDB/dshlib.h b/3-StudentDB/dshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..1353bbd3ba39a4099cdf58ab8f8827e2d3e4e5aa
--- /dev/null
+++ b/3-StudentDB/dshlib.h
@@ -0,0 +1,50 @@
+#ifndef __DSHLIB_H__
+#define __DSHLIB_H__
+
+// Constants for command structure sizes
+#define EXE_MAX 64
+#define ARG_MAX 256
+#define CMD_MAX 8
+// Longest command that can be read from the shell
+#define SH_CMD_MAX EXE_MAX + ARG_MAX
+
+typedef struct command
+{
+    char exe[EXE_MAX];
+    char args[ARG_MAX];
+} command_t;
+
+typedef struct command_list
+{
+    int num;
+    command_t commands[CMD_MAX];
+} command_list_t;
+
+// Special character #defines
+#define SPACE_CHAR ' '
+#define PIPE_CHAR '|'
+#define PIPE_STRING "|"
+
+#define SH_PROMPT "dsh> "
+#define EXIT_CMD "exit"
+
+// Standard Return Codes
+#define OK 0
+#define WARN_NO_CMDS -1
+#define ERR_TOO_MANY_COMMANDS -2
+#define ERR_CMD_OR_ARGS_TOO_BIG -3
+
+// starter code
+#define M_NOT_IMPL "The requested operation is not implemented yet!\n"
+#define EXIT_NOT_IMPL 3
+#define NOT_IMPLEMENTED_YET 0
+
+// prototypes
+int build_cmd_list(char *cmd_line, command_list_t *clist);
+
+// output constants
+#define CMD_OK_HEADER "PARSED COMMAND LINE - TOTAL COMMANDS %d\n"
+#define CMD_WARN_NO_CMD "warning: no commands provided\n"
+#define CMD_ERR_PIPE_LIMIT "error: piping limited to %d commands\n"
+
+#endif
\ No newline at end of file
diff --git a/3-StudentDB/makefile b/3-StudentDB/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..9d20e5b5c474c8217fcc26b24b9a795b4b1a8127
--- /dev/null
+++ b/3-StudentDB/makefile
@@ -0,0 +1,27 @@
+# Compiler settings
+CC = gcc
+CFLAGS = -Wall -Wextra -g
+
+# Target executable name
+TARGET = dsh
+
+# Find all source and header files
+SRCS = $(wildcard *.c)
+HDRS = $(wildcard *.h)
+
+# Default target
+all: $(TARGET)
+
+# Compile source to executable
+$(TARGET): $(SRCS) $(HDRS)
+	$(CC) $(CFLAGS) -o $(TARGET) $(SRCS)
+
+# Clean up build files
+clean:
+	rm -f $(TARGET)
+
+test:
+	./test.sh
+
+# Phony targets
+.PHONY: all clean
\ No newline at end of file
diff --git a/3-StudentDB/questions.md b/3-StudentDB/questions.md
new file mode 100644
index 0000000000000000000000000000000000000000..b2096fe432d00c8ede87829de6893c416eef36d6
--- /dev/null
+++ b/3-StudentDB/questions.md
@@ -0,0 +1,30 @@
+1. In this assignment I suggested you use `fgets()` to get user input in the main while loop. Why is `fgets()` a good choice for this application?
+
+    > **Answer**:  Because it takes an argument for maximum length and prevents buffer overflow, 'fgets()` is a solid solution for this application. Additionally, because it reads the entire line, it is convenient and behaves like a command shell (input ends when the user hits enter).
+
+2. You needed to use `malloc()` to allocte memory for `cmd_buff` in `dsh_cli.c`. Can you explain why you needed to do that, instead of allocating a fixed-size array?
+
+    > **Answer**:  For `cmd_buff`, using `malloc()` offers flexibility, guards against stack overflows, and guarantees persistence during execution. It facilitates simpler changes and maintains consistency in memory management with other allocations. Dynamic allocation, as opposed to fixed arrays, prevents wasteful stack utilisation and accommodates possible future resizing. Memory leaks can be avoided by properly deallocating using `free(cmd_buff);`.
+
+
+3. In `dshlib.c`, the function `build_cmd_list(`)` must trim leading and trailing spaces from each command before storing it. Why is this necessary? If we didn't trim spaces, what kind of issues might arise when executing commands in our shell?
+
+    > **Answer**:  Commands are appropriately interpreted and performed error-free when leading and trailing spaces are trimmed. If left untrimmed, trailing spaces may obstruct parameter parsing, while leading spaces may result in erroneous command names. Unexpected behaviour could result from this, including not recognising built-in commands like "exit" or "dragon." Untrimmed spaces can also result in inconsistent command comparisons, which reduces the shell's dependability. Trimming ensures clean input, which keeps things functioning properly and avoids execution mistakes.
+
+4. For this question you need to do some research on STDIN, STDOUT, and STDERR in Linux. We've learned this week that shells are "robust brokers of input and output". Google _"linux shell stdin stdout stderr explained"_ to get started.
+
+- One topic you should have found information on is "redirection". Please provide at least 3 redirection examples that we should implement in our custom shell, and explain what challenges we might have implementing them.
+
+    > **Answer**:  In a Linux shell, redirection gives you control over the input and output streams. Important instances include input redirection (`<`), which reads from a file rather than the keyboard, error redirection (`2>`), which redirects STDERR to a file, and output redirection (`>` and `>>`), which sends STDOUT to a file (overwriting or appending). Managing file permissions, appropriately altering file descriptors with system calls (`dup2()`), and making sure outputs are merged in the correct order (e.g., `2>&1`) are some of the implementation challenges. A strong custom shell requires effective file management and proper error handling.
+
+- You should have also learned about "pipes". Redirection and piping both involve controlling input and output in the shell, but they serve different purposes. Explain the key differences between redirection and piping.
+
+    > **Answer**:  While pipe (|) joins the output of one command to another directly, redirection manages input and output between commands and files. While pipes allow for real-time command communication without the need for intermediate files, redirection stores or retrieves data from files. For instance, ls | grep txt dynamically filters results, whereas ls > output.txt writes output to a file. Implementing piping is more difficult and calls for inter-process communication through the use of pipe() and fork().
+
+- STDERR is often used for error messages, while STDOUT is for regular output. Why is it important to keep these separate in a shell?
+
+    > **Answer**:  Clarity, debugging, and appropriate command chaining all depend on keeping STDOUT and STDERR distinct in a shell. Regular output is handled by STDOUT, and error messages are handled by STDERR, which makes sure that errors don't affect the desired outcomes. For instance, the error warning displays independently of the valid output when ls nonexistentfile is executed. Because of this division, users can filter or reroute problems without compromising regular output. Additionally, it guarantees that pipelines (|) function properly by preventing errors from being inadvertently handled by the subsequent command. Without separation, it would be challenging to discern between errors and correct output, which would reduce the reliability of automation and troubleshooting.
+
+- How should our custom shell handle errors from commands that fail? Consider cases where a command outputs both STDOUT and STDERR. Should we provide a way to merge them, and if so, how?
+
+    > **Answer**:  In order to handle command errors, our custom shell should appropriately capture STDERR and offer insightful feedback without interfering with regular output. The shell should show the error message and let the user continue running the command if it fails. When a command outputs both STDOUT and STDERR, we should provide a means to use redirection to combine the two when necessary. For logging or additional processing via pipelines, this enables users to merge error and standard output into a single stream. To put this into practice, dup2() must be used to manage file descriptors correctly, guaranteeing that commands execute without hiccups and providing users with control over how failures are handled.
\ No newline at end of file
diff --git a/3-StudentDB/test.sh b/3-StudentDB/test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6d586dbabea1cf80d3862450f1c62c8664db13d4
--- /dev/null
+++ b/3-StudentDB/test.sh
@@ -0,0 +1,212 @@
+#!/usr/bin/env bats
+
+@test "Simple Command" {
+    run ./dsh <<EOF                
+test_command
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>test_commanddsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "Simple Command with Args" {
+    run ./dsh <<EOF                
+cmd -a1 -a2
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output 
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd[-a1-a2]dsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+
+@test "No command provided" {
+    run ./dsh <<EOF                
+
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>warning:nocommandsprovideddsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "Two commands" {
+    run ./dsh <<EOF                
+command_one | command_two
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>command_one<2>command_twodsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "three commands with args" {
+    run ./dsh <<EOF                
+cmd1 a1 a2 a3 | cmd2 a4 a5 a6 | cmd3 a7 a8 a9
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS3<1>cmd1[a1a2a3]<2>cmd2[a4a5a6]<3>cmd3[a7a8a9]dsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "try max (8) commands" {
+    run ./dsh <<EOF                
+cmd1 | cmd2 | cmd3 | cmd4 | cmd5 | cmd6 | cmd7 | cmd8
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS8<1>cmd1<2>cmd2<3>cmd3<4>cmd4<5>cmd5<6>cmd6<7>cmd7<8>cmd8dsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "try too many commands" {
+    run ./dsh <<EOF                
+cmd1 | cmd2 | cmd3 | cmd4 | cmd5 | cmd6 | cmd7 | cmd8 | cmd9
+exit
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>error:pipinglimitedto8commandsdsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
+
+@test "kitchen sink - multiple commands" {
+    run ./dsh <<EOF                
+cmd1
+cmd2 arg arg2
+p1 | p2
+p3 p3a1 p3a2 | p4 p4a1 p4a2
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd1dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd2[argarg2]dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>p1<2>p2dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>p3[p3a1p3a2]<2>p4[p4a1p4a2]dsh>"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+
+}
\ No newline at end of file