a gleam implementation of a CS assignment originally written in cpp

feat: add verse parser

-2
.gitignore
··· 1 - lab66 2 - lab66.output 3 1 build/ 4 2 .DS_Store
-64
COVERSHEET.md
··· 1 - --- 2 - Project: "#6" 3 - Professor: "Professor Knoerr" 4 - Class: "CS 1210 – Fall 2025" 5 - Author: "Kieran Klukas" 6 - --- 7 - 8 - # Requirements 9 - 10 - <!--Restate the problem specifications in your own words--> 11 - 12 - Search a provided old testament text file for arbitrary reference. It prompts the user for book, chapter, and verse and then searches for that verse. If book is not found then output `Book does not exist in the Old Testament`. If chapter isn't found then `Chapter # does not exist in Book`. Finally if the verse isn't found then output `Verse # does not exist in Book #`. Once the verse is found then append the verse to `verses.txt`. 13 - 14 - # Design 15 - 16 - <!--How did you attack the problem? What choices did you make in your design, and why?--> 17 - 18 - I started with outlining the basic input system [`532c6cf`](https://github.com/cu-cs1210/lab-6-kieranklukas/commit/5b2ced23798fc7cbce79eb445c9592cccf16d6a6). Then worked on setting up a while loop to read the file line by line. I spent a while puzzling over exactly how I wanted to implement this and I am not entirely satisfied with my current solution. It would be interesting to try this problem in a functional language like gleam or erlang and see how much more cleanly I can parse the file. I implemented each reference part sequentially and then went back and made it check for barrier strings to stay within the proper book and chapter. I ended up realizing that the book of Psalms is referenced differently and so made some logic to permit its idiosyncracies [`7c18fff`](https://github.com/cu-cs1210/lab-6-kieranklukas/commit/7c18fffa30ab3e43bcb05ad4dff44f275987cbb8). 19 - 20 - # Implementation 21 - 22 - <!--Outline any interesting implementation details.--> 23 - 24 - I used a single input variable to parse the file word by word until we get to the correct verse. I also check if the input book is listed as `Psalm` or `Psalms` and add or remove an `s` when doing checks or output as appropriate. The output file is also never opened until we find the verse and is immediately closed afterward. Also a minor note of intrest is that you can change the default paths that the program loads from and saves to by adding the paths as arguments (eg. `lab66 test/OT.txt build/verses.txt`). 25 - 26 - # Testing 27 - 28 - <!-- 29 - Explain how you tested your program, enumerating the tests if possible. 30 - Explain why your test set was sufficient to believe that the software is working properly, 31 - i.e., what were the range of possibilities of errors that you were testing for. 32 - --> 33 - 34 - I updated the `makefile` to add `make run` so I could compile and run it more easily and updated the test file to have the Zylabs test states as well as more granularly check a happy path and some adversarial paths. 35 - 36 - # Outside Help 37 - 38 - <!--Did you get help from anyone else on this project? Document their contribution to your learning.--> 39 - 40 - No outside help was used beyond minor googling for how to capitalize an entire string and the proper syntax for appending (not covered in zybooks as far as I can tell but I'm sure you will cover it in lectures next week). 41 - 42 - # Summary/Conclusion 43 - 44 - <!--Present your results. Did it work properly? Are there any limitations?--> 45 - 46 - The program works well and should cover most edgecases! As far as I can tell there isn't anything that should be able to break it beyond changing the `OT.txt` to be several GB in size. 47 - 48 - # AI Use 49 - 50 - <!--How did you use Generative AI in this project?--> 51 - 52 - I used claude to generate a more complex test file and then modified it to add the rest of the zybooks tests as I got closer to finishing the project. 53 - 54 - # Lessons Learned 55 - 56 - <!--List any lessons learned. What might you have done differently if you were going to attack this again.--> 57 - 58 - I would definetly want to try a different language for sure if I was doing this independantly but overal I think this implementation is fairly solid and I wouldn't change much. I did find out in this lab that it is always a good idea to check the whole input or you might run into weird bugs like how `PSALMS` is formatted. 59 - 60 - # Time Spent 61 - 62 - <!--Approximately how many hours did you spend on this project?--> 63 - 64 - ![hackatime badge](https://hackatime-badge.hackclub.com/U062UG485EE/lab-6)
+15 -63
README.md
··· 1 - # Lab 6 2 - 3 - ![screenshot of the code generated by freeze](docs/freeze.jpeg) 4 - 5 - For this lab the program must parse `OT.txt` (format below) to find specific references and produce scoped errors if the reference isn't found. 6 - 7 - ```text 8 - THE BOOK OF GENESIS 9 - 10 - CHAPTER 1 11 - 1 In the beginning God created the heaven and the earth. 12 - 2 And the earth was without form, and void; and darkness [was] upon the face of the deep. And the Spirit of God moved upon the face of the waters. 13 - 3 And God said, Let there be light: and there was light. 14 - 4 And God saw the light, that [it was] good: and God divided the light from the darkness. 1 + # bible_search_gleam 15 2 16 - THE BOOK OF PSALMS 3 + [![Package Version](https://img.shields.io/hexpm/v/bible_search_gleam)](https://hex.pm/packages/bible_search_gleam) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/bible_search_gleam/) 17 5 18 - PSALM 1 19 - 1 Blessed [is] the man that walketh not in the counsel of the ungodly, nor standeth in the way of sinners, nor sitteth in the seat of the scornful. 20 - 2 But his delight [is] in the law of the LORD; and in his law doth he meditate day and night. 21 - 3 And he shall be like a tree planted by the rivers of water, that bringeth forth his fruit in his season; his leaf also shall not wither; and whatsoever he doeth shall prosper. 6 + ```sh 7 + gleam add bible_search_gleam@1 22 8 ``` 23 - 24 - The project is layed out as follows: 25 - 26 - ```bash 27 - . 28 - ├── build # this directory isn't committed and is ephemeral 29 - │   └── lab66 30 - ├── COVERSHEET.md 31 - ├── LICENSE.md 32 - ├── makefile 33 - ├── README.md 34 - ├── src 35 - │   └── lab66.cpp 36 - └── test 37 - ├── OT.txt 38 - └── test.sh 9 + ```gleam 10 + import bible_search_gleam 39 11 40 - 4 directories, 8 files 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 41 15 ``` 42 16 43 - ### Build scripts 17 + Further documentation can be found at <https://hexdocs.pm/bible_search_gleam>. 44 18 45 - The preferred way to run the program is through make: 19 + ## Development 46 20 47 - ```bash 48 - make # compiles and runs tests 49 - make run # compiles and runs the program 50 - make clean # clean up the build dir 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 51 24 ``` 52 - 53 - it is however possible to still do things manually: 54 - 55 - ```bash 56 - mkdir build 57 - cd build 58 - g++ ../src/lab66.cpp -o lab66 59 - ./lab66 ../test/OT.txt verses.txt 60 - ``` 61 - 62 - <p align="center"> 63 - <img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/main/.github/images/line-break.svg" /> 64 - </p> 65 - 66 - <p align="center"> 67 - <i><code>&copy 2025-present <a href="https://github.com/taciturnaxololt">Kieran Klukas</a></code></i> 68 - </p> 69 - 70 - <p align="center"> 71 - <a href="https://github.com/cu-cs1210/lab-6-kieranklukas/blob/main/LICENSE.md"><img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&logoColor=d9e0ee&colorA=363a4f&colorB=b7bdf8"/></a> 72 - </p>
docs/freeze.jpeg

This is a binary file and will not be displayed.

+21
gleam.toml
··· 1 + name = "bible_search" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + simplifile = ">= 2.3.0 and < 3.0.0" 18 + input = ">= 1.0.1 and < 2.0.0" 19 + 20 + [dev-dependencies] 21 + gleeunit = ">= 1.0.0 and < 2.0.0"
-16
makefile
··· 1 - all: lab66 test 2 - 3 - lab66: src/lab66.cpp 4 - mkdir -p build && g++ src/lab66.cpp -Wall -o build/lab66 5 - 6 - test: lab66 7 - cd test && ./test.sh 8 - 9 - exec: 10 - ./build/lab66 test/OT.txt build/verses.txt 11 - 12 - run: lab66 exec 13 - 14 - clean: 15 - rm -rf build 16 -
+16
manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 6 + { name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" }, 7 + { name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" }, 8 + { name = "input", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "input", source = "hex", outer_checksum = "FE84CDADC78A1367E4AFD561A529825A8FEC88D165CBDF511FD3226CABCDEE6F" }, 9 + { name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" }, 10 + ] 11 + 12 + [requirements] 13 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 14 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 15 + input = { version = ">= 1.0.1 and < 2.0.0" } 16 + simplifile = { version = ">= 2.3.0 and < 3.0.0" }
+47
src/bible_search.gleam
··· 1 + import gleam/int 2 + import gleam/io 3 + import gleam/list 4 + import gleam/string 5 + import input.{input} 6 + import simplifile 7 + 8 + const filepath = "./OT.txt" 9 + 10 + pub type Reference { 11 + Reference(book: String, chapter: Int, verse: Int) 12 + } 13 + 14 + pub fn parse_reference(input: String) -> Result(Reference, String) { 15 + let parts = string.split(input, on: " ") 16 + 17 + case parts { 18 + [book, chapter_s, verse_s] -> 19 + case int.parse(chapter_s) { 20 + Ok(chapter) -> 21 + case int.parse(verse_s) { 22 + Ok(verse) -> 23 + Ok(Reference(book: string.uppercase(book), chapter:, verse:)) 24 + Error(_) -> Error("Invalid verse" <> verse_s) 25 + } 26 + Error(_) -> Error("Invalid chapter" <> chapter_s) 27 + } 28 + _ -> Error("invalid parse") 29 + } 30 + } 31 + 32 + pub fn main() -> Nil { 33 + let assert Ok(bible) = simplifile.read(from: filepath) 34 + 35 + let biblelines = string.split(bible, on: "\n") 36 + 37 + let assert Ok(search) = input(prompt: "> ") 38 + 39 + let assert Ok(reference) = parse_reference(search) 40 + 41 + list.each(biblelines, fn(line) { 42 + case line { 43 + "THE BOOK OF " <> book if book == reference.book -> io.println(book) 44 + _ -> io.print("") 45 + } 46 + }) 47 + }
-143
src/lab66.cpp
··· 1 - /********************************************************* 2 - * Summary: find verses from the old testament 3 - * appending them to file as found 4 - * 5 - * Author: Kieran Klukas 6 - * Created: October 2025 7 - * 8 - ********************************************************/ 9 - 10 - #include <iostream> 11 - #include <cstdlib> 12 - #include <fstream> 13 - 14 - using namespace std; 15 - 16 - int main(int argc, char **argv) { 17 - 18 - ifstream OT; 19 - ofstream OF; 20 - string inputFilename = "OT.txt"; 21 - string outputFilename = "verses.txt"; 22 - 23 - string book; 24 - string normalizedBook; 25 - int chapter; 26 - int verse; 27 - 28 - // get path to OT.txt if provided otherwise use default path 29 - // and then do the same for the verses.txt 30 - if (argc >= 2) inputFilename = argv[1]; 31 - if (argc >= 3) outputFilename = argv[2]; 32 - 33 - OT.open(inputFilename); 34 - 35 - if (!OT.is_open()) { 36 - cout << inputFilename << ": failed to open file"; 37 - return 1; 38 - } 39 - 40 - cout << "Please enter the reference of the verse you would like" << endl; 41 - 42 - cout << "the book: "; 43 - getline(cin, book); 44 - for (int i = 0; i < book.length(); i++) 45 - normalizedBook += toupper(book[i]); 46 - if (normalizedBook == "PSALMS") 47 - normalizedBook = "PSALM"; 48 - 49 - cout << "the chapter: "; 50 - cin >> chapter; 51 - 52 - cout << "the verse: "; 53 - cin >> verse; 54 - 55 - cout << endl; 56 - 57 - bool foundBook = false; 58 - bool foundChapter = false; 59 - bool foundVerse = false; 60 - string verseString; 61 - while (!OT.eof()) { 62 - string input; 63 - 64 - if (!foundBook) { 65 - OT >> input; // THE 66 - if (input != "THE") continue; 67 - OT >> input; // BOOK 68 - if (input != "BOOK") continue; 69 - OT >> input; // OF 70 - if (input != "OF") continue; 71 - 72 - OT.ignore(); // skip the single space 73 - 74 - getline(OT, input); // handle multi word books 75 - 76 - // second check is for the book of psalms 77 - if (input == normalizedBook || input == (normalizedBook + "S")) { 78 - foundBook = true; 79 - 80 - getline(OT, input); // discard empty line 81 - continue; 82 - } 83 - } else { 84 - if (!foundChapter) { 85 - OT >> input; 86 - if (input != "CHAPTER" && input != "PSALM") { 87 - if (input == "THE") break; 88 - 89 - continue; 90 - } 91 - 92 - OT >> input; 93 - if (input == to_string(chapter)) { 94 - foundChapter = true; 95 - continue; 96 - } 97 - } else { 98 - OT >> input; 99 - 100 - if (input == "CHAPTER" || input == "PSALM") break; 101 - 102 - if (input == to_string(verse)) { 103 - foundVerse = true; 104 - OT.ignore(); // ignore space 105 - getline(OT, verseString); 106 - break; 107 - } 108 - } 109 - } 110 - } 111 - 112 - if (foundVerse) { 113 - cout << verse << " " << verseString << endl; 114 - 115 - OF.open(outputFilename, ios_base::app); 116 - 117 - if (!OF.is_open()) { 118 - cout << outputFilename << ": error saving file" << endl; 119 - return 1; 120 - } 121 - 122 - OF << verse << " " << verseString << endl; 123 - 124 - OF.close(); 125 - } else { 126 - if (!foundBook) { 127 - cout << (normalizedBook == "PSALM" ? book += "s" : book) << " does not exist in the Old Testament"; 128 - } else { 129 - if (!foundChapter) { 130 - cout << (normalizedBook == "PSALM" ? "Psalm " : "Chapter ") << chapter; 131 - cout << " does not exist in " << (normalizedBook == "PSALM" ? book += "s" : book); 132 - } else if (!foundVerse) { 133 - cout << "Verse " << verse << " does not exist in " << book << " " << chapter; 134 - } 135 - } 136 - 137 - cout << endl; 138 - } 139 - 140 - OT.close(); 141 - 142 - return 0; 143 - }
test/OT.txt OT.txt
+13
test/bible_search_gleam_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }
-362
test/test.sh
··· 1 - #!/bin/bash 2 - 3 - # Test script with configurable timeout support 4 - # Usage: ./test.sh [timeout_seconds] 5 - # Example: ./test.sh 5 (uses 5 second timeout) 6 - # Default: 1 second timeout 7 - 8 - # Colors for output 9 - RED='\033[0;31m' 10 - GREEN='\033[0;32m' 11 - YELLOW='\033[1;33m' 12 - BLUE='\033[0;34m' 13 - NC='\033[0m' # No Color 14 - 15 - # Test configuration 16 - temp_file="lab66.output" 17 - program="../build/lab66" 18 - output_file="../build/verses.txt" 19 - test_timeout=${1:-1} 20 - 21 - # Test counters 22 - total_tests=0 23 - passed_tests=0 24 - 25 - # Cleanup function 26 - cleanup() { 27 - rm -f "$temp_file" "$output_file" 28 - } 29 - 30 - # Error trap 31 - trap cleanup EXIT ERR 32 - 33 - # Function to print test results 34 - print_result() { 35 - local test_name="$1" 36 - local result="$2" 37 - local message="$3" 38 - 39 - total_tests=$((total_tests + 1)) 40 - 41 - if [ "$result" = "PASS" ]; then 42 - echo -e "${GREEN}[PASS]${NC} $test_name: $message" 43 - passed_tests=$((passed_tests + 1)) 44 - elif [ "$result" = "TIMEOUT" ]; then 45 - echo -e "${YELLOW}[TIMEOUT]${NC} $test_name: $message" 46 - else 47 - echo -e "${RED}[FAIL]${NC} $test_name: $message" 48 - fi 49 - } 50 - 51 - # Function to print test section header 52 - print_section() { 53 - echo -e "\n${BLUE}=== $1 ===${NC}" 54 - } 55 - 56 - # Build program if needed 57 - if [ ! -f "$program" ]; then 58 - print_section "Building Program" 59 - cd .. && make lab66 > /dev/null 2>&1 60 - build_result=$? 61 - cd test 62 - if [ $build_result -ne 0 ]; then 63 - print_result "Program Build" "FAIL" "Could not build $program" 64 - exit 1 65 - fi 66 - print_result "Program Build" "PASS" "Program built successfully" 67 - fi 68 - 69 - # Test 1: Program compilation/existence 70 - if [ ! -f "$program" ]; then 71 - print_section "Test 1: Program Availability" 72 - print_result "Program Existence" "FAIL" "Program $program not found" 73 - exit 1 74 - fi 75 - 76 - # Test 2: Basic Bible Verse Lookup Test (Genesis 1:1) 77 - print_section "Basic Bible Verse Lookup Test" 78 - 79 - # Test with known valid verse 80 - basic_input="Genesis\n1\n1" 81 - resp=$(timeout "$test_timeout" bash -c "echo -e '$basic_input' | '$program' OT.txt '$output_file'" 2>&1) 82 - exit_code=$? 83 - 84 - # Print the output 85 - if [ -n "$resp" ]; then 86 - echo "$resp" 87 - else 88 - echo "(No output produced)" 89 - fi 90 - echo 91 - 92 - if [ $exit_code -eq 124 ]; then 93 - print_result "Basic Execution" "TIMEOUT" "Program timed out after ${test_timeout}s" 94 - exit 1 95 - elif [ $exit_code -ne 0 ]; then 96 - print_result "Basic Execution" "FAIL" "Program exited with code $exit_code" 97 - exit 1 98 - else 99 - print_result "Basic Execution" "PASS" "Program executed successfully" 100 - fi 101 - 102 - # Check if output contains required prompts 103 - if [[ "$resp" == *"Please enter the reference of the verse you would like"* ]]; then 104 - print_result "Initial Prompt" "PASS" "Program shows initial prompt" 105 - else 106 - print_result "Initial Prompt" "FAIL" "Program missing initial prompt" 107 - fi 108 - 109 - if [[ "$resp" == *"the book:"* ]]; then 110 - print_result "Book Prompt" "PASS" "Program prompts for book" 111 - else 112 - print_result "Book Prompt" "FAIL" "Program missing book prompt" 113 - fi 114 - 115 - if [[ "$resp" == *"the chapter:"* ]]; then 116 - print_result "Chapter Prompt" "PASS" "Program prompts for chapter" 117 - else 118 - print_result "Chapter Prompt" "FAIL" "Program missing chapter prompt" 119 - fi 120 - 121 - if [[ "$resp" == *"the verse:"* ]]; then 122 - print_result "Verse Prompt" "PASS" "Program prompts for verse" 123 - else 124 - print_result "Verse Prompt" "FAIL" "Program missing verse prompt" 125 - fi 126 - 127 - # Check for verse output format 128 - if [[ "$resp" == *"1 In the beginning God created the heaven and the earth"* ]]; then 129 - print_result "Verse Format" "PASS" "Program outputs verse in correct format" 130 - else 131 - print_result "Verse Format" "FAIL" "Program missing or incorrect verse format" 132 - fi 133 - 134 - # Check if test/verses.txt file was created 135 - if [ -f "$output_file" ]; then 136 - verses_content=$(cat "$output_file") 137 - if [[ "$verses_content" == *"In the beginning God created the heaven and the earth"* ]]; then 138 - print_result "File Output" "PASS" "verses.txt created with correct content in build directory" 139 - else 140 - print_result "File Output" "FAIL" "verses.txt has incorrect content" 141 - fi 142 - else 143 - print_result "File Output" "FAIL" "verses.txt file not created in build directory" 144 - fi 145 - 146 - # Function to run verse lookup test 147 - run_verse_test() { 148 - local test_name="$1" 149 - local book="$2" 150 - local chapter="$3" 151 - local verse="$4" 152 - local expected_pattern="$5" 153 - local should_create_file="$6" 154 - 155 - # Clean up previous test 156 - rm -f "$output_file" 157 - 158 - input="$book\n$chapter\n$verse" 159 - output=$(timeout "$test_timeout" bash -c "echo -e '$input' | '$program' OT.txt '$output_file'" 2>&1) 160 - exit_code=$? 161 - 162 - if [ $exit_code -eq 124 ]; then 163 - print_result "$test_name" "TIMEOUT" "Program timed out after ${test_timeout}s" 164 - return 165 - elif [ $exit_code -ne 0 ]; then 166 - print_result "$test_name" "FAIL" "Program crashed (exit code $exit_code)" 167 - return 168 - fi 169 - 170 - # Check if expected pattern is found in output 171 - pattern_found=false 172 - if [[ "$output" == *"$expected_pattern"* ]]; then 173 - pattern_found=true 174 - fi 175 - 176 - # For tests expecting specific output, pattern must be found 177 - if [ -n "$expected_pattern" ] && [ "$pattern_found" = "false" ]; then 178 - print_result "$test_name" "FAIL" "Expected '$expected_pattern' not found in output" 179 - return 180 - fi 181 - 182 - # Check file creation expectations 183 - if [ "$should_create_file" = "true" ]; then 184 - if [ -f "$output_file" ] && [ "$pattern_found" = "true" ]; then 185 - print_result "$test_name" "PASS" "Expected output found and file created" 186 - elif [ ! -f "$output_file" ]; then 187 - print_result "$test_name" "FAIL" "Expected file verses.txt not created in build directory" 188 - else 189 - print_result "$test_name" "FAIL" "File created but expected output not found" 190 - fi 191 - else 192 - if [ ! -f "$output_file" ] && [ "$pattern_found" = "true" ]; then 193 - print_result "$test_name" "PASS" "Expected error output and no file created" 194 - elif [ -f "$output_file" ]; then 195 - print_result "$test_name" "FAIL" "verses.txt file should not be created for error cases" 196 - else 197 - print_result "$test_name" "FAIL" "Expected error message not found" 198 - fi 199 - fi 200 - } 201 - 202 - # Valid Verse Tests 203 - print_section "Valid Verse Tests" 204 - 205 - run_verse_test "Exodus 20:3" "Exodus" "20" "3" "3 Thou shalt have no other gods before me" true 206 - run_verse_test "Psalm 23:1" "Psalm" "23" "1" "1 The LORD [is] my shepherd; I shall not want." true 207 - 208 - # Error Case Tests 209 - print_section "Error Case Tests" 210 - 211 - run_verse_test "Invalid Book" "Matthew" "1" "1" "Matthew does not exist in the Old Testament" false 212 - run_verse_test "Invalid Chapter" "Esther" "18" "3" "Chapter 18 does not exist in Esther" false 213 - run_verse_test "Invalid Verse" "Psalm" "117" "5" "Verse 5 does not exist in Psalm 117" false 214 - 215 - # Adversarial Testing Section 216 - print_section "Adversarial Tests" 217 - 218 - # Test edge cases 219 - run_verse_test "Empty Book Name" "" "1" "1" "Please enter the reference" false 220 - run_verse_test "Non-numeric Chapter" "Genesis" "abc" "1" "Please enter the reference" false 221 - run_verse_test "Non-numeric Verse" "Genesis" "1" "xyz" "Please enter the reference" false 222 - run_verse_test "Zero Chapter" "Genesis" "0" "1" "Please enter the reference" false 223 - run_verse_test "Zero Verse" "Genesis" "1" "0" "Please enter the reference" false 224 - run_verse_test "Negative Chapter" "Genesis" "-1" "1" "Please enter the reference" false 225 - run_verse_test "Negative Verse" "Genesis" "1" "-1" "Please enter the reference" false 226 - run_verse_test "Very Large Chapter" "Genesis" "999999" "1" "Please enter the reference" false 227 - run_verse_test "Very Large Verse" "Genesis" "1" "999999" "Please enter the reference" false 228 - 229 - # Test case sensitivity 230 - run_verse_test "Lowercase Book" "genesis" "1" "1" "1 In the beginning God created the heaven and the earth." true 231 - run_verse_test "Mixed Case Book" "GEnesis" "1" "1" "1 In the beginning God created the heaven and the earth." true 232 - 233 - # Test two-word books 234 - run_verse_test "First Samuel" "First Samuel" "1" "1" "1 Now there was a certain man of Ramathaim-zophim, of mount Ephraim, and his name [was] Elkanah, the son of Jeroham, the son of Elihu, the son of Tohu, the son of Zuph, an Ephrathite:" true 235 - run_verse_test "Second Kings" "Second Kings" "1" "1" "1 Then Moab rebelled against Israel after the death of Ahab" true 236 - 237 - # Appending Tests 238 - print_section "Appending & File Path Tests" 239 - 240 - # Function to run appending test 241 - run_appending_test() { 242 - local test_name="$1" 243 - 244 - # Clean up previous test 245 - rm -f "$output_file" 246 - 247 - # First verse lookup 248 - input1="Genesis\n1\n1" 249 - output1=$(timeout "$test_timeout" bash -c "echo -e '$input1' | '$program' OT.txt '$output_file'" 2>&1) 250 - exit_code1=$? 251 - 252 - if [ $exit_code1 -ne 0 ]; then 253 - print_result "$test_name - First Lookup" "FAIL" "First verse lookup failed" 254 - return 255 - fi 256 - 257 - # Check first verse was written 258 - if [ ! -f "$output_file" ]; then 259 - print_result "$test_name - First File" "FAIL" "First verse file not created" 260 - return 261 - fi 262 - 263 - first_content=$(cat "$output_file") 264 - 265 - # Second verse lookup 266 - input2="Genesis\n1\n2" 267 - output2=$(timeout "$test_timeout" bash -c "echo -e '$input2' | '$program' OT.txt '$output_file'" 2>&1) 268 - exit_code2=$? 269 - 270 - if [ $exit_code2 -ne 0 ]; then 271 - print_result "$test_name - Second Lookup" "FAIL" "Second verse lookup failed" 272 - return 273 - fi 274 - 275 - # Check if file still exists and has content 276 - if [ ! -f "$output_file" ]; then 277 - print_result "$test_name - Second File" "FAIL" "File disappeared after second lookup" 278 - return 279 - fi 280 - 281 - second_content=$(cat "$output_file") 282 - 283 - # Check if both verses are in the file (appending behavior) 284 - if [[ "$second_content" == *"1 In the beginning God created the heaven and the earth"* ]] && [[ "$second_content" == *"2 And the earth was without form, and void"* ]]; then 285 - print_result "$test_name" "PASS" "Both verses found in file (appending works)" 286 - elif [[ "$second_content" == *"2 And the earth was without form, and void"* ]] && [[ "$second_content" != *"1 In the beginning God created the heaven and the earth"* ]]; then 287 - print_result "$test_name" "FAIL" "Only second verse found (overwriting instead of appending)" 288 - else 289 - print_result "$test_name" "FAIL" "Unexpected file content after second lookup" 290 - fi 291 - } 292 - 293 - run_appending_test "Multiple Verse Appending" 294 - 295 - # Function to run file path test 296 - run_file_path_test() { 297 - local test_name="$1" 298 - local file_path="$2" 299 - local expected_pattern="$3" 300 - local should_succeed="$4" 301 - 302 - # Clean up previous test 303 - rm -f "$output_file" 304 - 305 - # Provide verse lookup input for file path tests 306 - input="Genesis\n1\n1" 307 - 308 - if [ -n "$file_path" ]; then 309 - output=$(timeout "$test_timeout" bash -c "echo -e '$input' | '$program' '$file_path' '$output_file'" 2>&1) 310 - else 311 - output=$(timeout "$test_timeout" bash -c "echo -e '$input' | '$program' OT.txt '$output_file'" 2>&1) 312 - fi 313 - exit_code=$? 314 - 315 - if [ $exit_code -eq 124 ]; then 316 - print_result "$test_name" "TIMEOUT" "Program timed out after ${test_timeout}s" 317 - return 318 - fi 319 - 320 - if [ "$should_succeed" = "true" ]; then 321 - if [ $exit_code -eq 0 ]; then 322 - print_result "$test_name" "PASS" "Program accepted file path successfully" 323 - else 324 - print_result "$test_name" "FAIL" "Program failed with exit code $exit_code: $output" 325 - fi 326 - else 327 - if [ $exit_code -ne 0 ] && [[ "$output" == *"$expected_pattern"* ]]; then 328 - print_result "$test_name" "PASS" "Program correctly rejected invalid file path" 329 - elif [ $exit_code -eq 0 ]; then 330 - print_result "$test_name" "FAIL" "Program should have failed with invalid file path" 331 - else 332 - print_result "$test_name" "FAIL" "Expected error message '$expected_pattern' not found: $output" 333 - fi 334 - fi 335 - } 336 - 337 - # Test with existing file (absolute path) 338 - run_file_path_test "Existing File (Absolute)" "/Users/kierank/code/school/lab-6/test/OT.txt" "" true 339 - 340 - # Test with existing file (relative path) 341 - run_file_path_test "Existing File (Relative)" "OT.txt" "" true 342 - 343 - # Test with non-existing file 344 - run_file_path_test "Non-existing File" "nonexistent.txt" "failed to open file" false 345 - 346 - # Test with invalid path 347 - run_file_path_test "Invalid Path" "/invalid/path/file.txt" "failed to open file" false 348 - 349 - # Test with empty path 350 - run_file_path_test "Empty Path" "" "" true 351 - 352 - # Test summary 353 - print_section "Test Summary" 354 - echo -e "${BLUE}Tests passed: $passed_tests/$total_tests${NC}" 355 - 356 - if [ $passed_tests -eq $total_tests ]; then 357 - echo -e "${GREEN}All tests passed!${NC}" 358 - exit 0 359 - else 360 - echo -e "${RED}Some tests failed!${NC}" 361 - exit 1 362 - fi