diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e42470..f608572 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -55,5 +55,8 @@ "C_Cpp_Runner.useLeakSanitizer": false, "C_Cpp_Runner.showCompilationTime": false, "C_Cpp_Runner.useLinkTimeOptimization": false, - "C_Cpp_Runner.msvcSecureNoWarnings": false + "C_Cpp_Runner.msvcSecureNoWarnings": false, + "clang-tidy.compilerArgs": [ + "-std=c++17" + ] } \ No newline at end of file diff --git a/L3B b/L3B new file mode 100755 index 0000000..3e1d990 Binary files /dev/null and b/L3B differ diff --git a/Makefile b/Makefile index 68509bc..2b7045c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -TARGET = CHANGEME -SRC_FILES = main.cpp +TARGET = L3B +SRC_FILES = main.cpp string_functions.cpp test_suite.cpp # Tyler's custom makefile extensions for CSCI200 (anyone can use these if they want) .DEFAULT_GOAL := all # Necessary so `make` doesn't run the "pack" target, as it is declared before "all" @@ -80,4 +80,6 @@ depend: .PHONY: all clean depend # DEPENDENCIES -main.o: main.cpp +main.o: main.cpp test_suite.h +string_functions.o: string_functions.cpp string_functions.h +test_suite.o: test_suite.cpp test_suite.h string_functions.h diff --git a/main.cpp b/main.cpp index 7bde5e8..d434ccd 100644 --- a/main.cpp +++ b/main.cpp @@ -1,13 +1,23 @@ /** * @file main.cpp * @author Tyler Beckman (tyler_beckman@mines.edu) - * @brief A program template for CSCI200 + * @brief CSCI200 L3B - A program to test different string modification APIs in C++ * @version 1 * @date 2024-09-21 */ #include -int main(void) { - std::cout << "Hello World" << std::endl; -} +#include "test_suite.h" + +int main() { + + std::cout << "Testing your functions..." << std::endl << std::endl; + if( run_all_tests() ) { + std::cout << "ALL TESTS PASSED!" << std::endl; + } else { + std::cout << "Not all tests are passing, errors remain..." << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/main.o b/main.o new file mode 100644 index 0000000..5526e69 Binary files /dev/null and b/main.o differ diff --git a/string_functions.cpp b/string_functions.cpp new file mode 100644 index 0000000..7716abb --- /dev/null +++ b/string_functions.cpp @@ -0,0 +1,144 @@ +#include "string_functions.h" + +#include + +using namespace std; + +unsigned long string_length(const string STR) { + unsigned long result = -1; + result = STR.length(); // set result to the length of the string + return result; +} + +char string_char_at(const string STR, const int IDX) { + char result = '\0'; + result = STR.at(IDX); + std::cout << "TODO: implement string_char_at(\"" << STR << "\", " << IDX << ")" << std::endl; + return result; +} + +string string_append(const string LEFT, const string RIGHT) { + string result = LEFT; + result = LEFT + RIGHT; + std::cout << "TODO: implement string_append(\"" << LEFT << "\", \"" << RIGHT << "\")" << std::endl; + return result; +} + +string string_insert(const string STR, const string TO_INSERT, const int IDX) { + string result = STR; + result.insert(IDX, TO_INSERT); + std::cout << "TODO: implement string_insert(\"" << STR << "\", \"" << TO_INSERT << "\", " << IDX << ")" << std::endl; + return result; +} + +size_t string_find(const string STR, const char C) { + size_t result = 0; + result = STR.find_first_of(C); + std::cout << "TODO: implement string_find(\"" << STR << "\", '" << C << "')" << std::endl; + return result; +} + +string string_substring(const string STR, const int IDX, const int LEN) { + string result = STR; + result = STR.substr(IDX, LEN); + std::cout << "TODO: implement string_substring(\"" << STR << "\", " << IDX << ", " << LEN << ")" << std::endl; + return result; +} + +string string_replace(const string STR, const string TEXT_TO_REPLACE, const string REPLACE_WITH) { + string result = STR; + auto index = STR.find(TEXT_TO_REPLACE); + if (index != -1ul) { + result.replace(STR.find(TEXT_TO_REPLACE), TEXT_TO_REPLACE.length(), REPLACE_WITH); + } + std::cout << "TODO: implement string_replace(\"" << STR << "\", \"" << TEXT_TO_REPLACE << "\", \"" << REPLACE_WITH << ")\"" << std::endl; + return result; +} + +string string_first_word(const string STR) { + string result = STR; + result = STR.substr(0, STR.find_first_of(' ')); + std::cout << "TODO: implement string_first_word(\"" << STR << "\")" << std::endl; + return result; +} + +string string_remove_first_word(const string STR) { + string result = STR; + // TODO 08: set result to be the string with the first word removed + std::cout << "TODO: implement string_remove_first_word(\"" << STR << ")\"" << std::endl; + return result; +} + +string string_second_word(const string STR) { + string result = STR; + // TODO 09: set result to be the second word from the string + std::cout << "TODO: implement string_second_word(\"" << STR << "\")" << std::endl; + return result; +} + +string string_third_word(const string STR) { + string result = STR; + // TODO 10: set result to be the third word from the string + std::cout << "TODO: implement string_third_word(\"" << STR << "\")" << std::endl; + return result; +} + +string string_nth_word(const string STR, const int N) { + string result = STR; + // TODO 11: set result to be the nth word from the string + std::cout << "TODO: implement string_nth_word(\"" << STR << "\", " << N << ")" << std::endl; + return result; +} + +vector string_tokenize(const string STR, const char DELIMINATOR) { + vector result; + // TODO 12: split the string by the given deliminator + std::cout << "TODO: implement string_tokenize(\"" << STR << "\", '" << DELIMINATOR << "')" << std::endl; + return result; +} + +string string_substitute(const string STR, const char TARGET, const char REPLACEMENT) { + string result = STR; + // TODO 13: set result to be the string with all instances of TARGET replaced + std::cout << "TODO: implement string_substitute(\"" << STR << "\", '" << TARGET << "', '" << REPLACEMENT << "')" << std::endl; + return result; +} + +string string_to_lower(const string STR) { + string result = STR; + for (unsigned int i = 0; i < STR.length(); i++) { + char newChar = STR[i]; + if (newChar >= 'A' && newChar <= 'Z') { + newChar = (char) (newChar + 32); + } + result[i] = newChar; + } + std::cout << "TODO: implement string_to_lower(\"" << STR << "\")" << std::endl; + return result; +} + +string string_to_upper(const string STR) { + string result = STR; + for (unsigned int i = 0; i < STR.length(); i++) { + char newChar = STR[i]; + if (newChar >= 'a' && newChar <= 'z') { + newChar = (char) (newChar - 32); + } + result[i] = newChar; + } + std::cout << "TODO: implement string_to_upper(\"" << STR << "\")" << std::endl; + return result; +} + +int string_compare(const string LHS, const string RHS) { + int result = 0; + if (LHS > RHS) { + result = 1; + } else { + if (LHS < RHS) { + result = -1; + } + } + std::cout << "TODO: implement string_compare(\"" << LHS << "\", \"" << RHS << "\")" << std::endl; + return result; +} \ No newline at end of file diff --git a/string_functions.h b/string_functions.h new file mode 100644 index 0000000..fb0e298 --- /dev/null +++ b/string_functions.h @@ -0,0 +1,145 @@ +#ifndef STRING_FUNCTIONS_H +#define STRING_FUNCTIONS_H + +#include +#include + +/** + * @brief Returns the length of a string + * @param STR string to return the length of + * @return length of the input string + */ +unsigned long string_length(const std::string STR); + +/** + * @brief Returns the character of a string at a given index + * @param STR full string + * @param IDX index to access string at + * @return character from STR at index IDX + */ +char string_char_at(const std::string STR, const int IDX); + +/** + * @brief Returns a concatenation of strings left and right + * @param LEFT string to be appended to + * @param RIGHT string to be appended + * @return concatenated string LEFTRIGHT + */ +std::string string_append(const std::string LEFT, const std::string RIGHT); + +/** + * @brief Returns the result of inserting a string into another at a given position + * @param STR original string + * @param TO_INSERT string to be inserted into STR + * @param IDX location within STR to insert TO_INSERT + * @return a new string with TO_INSERT inserted at index IDX of STR + */ +std::string string_insert(const std::string STR, const std::string TO_INSERT, const int IDX); + +/** + * @brief Returns the first index of a character in a string + * @param STR string to search within (the haystack) + * @param C character to search for (the needle) + * @return if found, first position within STR that C is located. otherwise returns string::npos + */ +size_t string_find(const std::string STR, const char C); + +/** + * @brief Returns part of a string + * @param STR original full string + * @param IDX starting position to return a substring from + * @param LEN length of substring to return + * @return a substring of length LEN from STR + */ +std::string string_substring(const std::string STR, const int IDX, const int LEN); + +/** + * @brief Replaces part of a string + * @param STR original full string (the haystack) + * @param TEXT_TO_REPLACE the existing text to replace (the needle) + * @param REPLACE_WITH the new text to insert + * @return modified string (if text found), otherwise the original string + */ +std::string string_replace(const std::string STR, const std::string TEXT_TO_REPLACE, const std::string REPLACE_WITH); + +/** + * @brief Returns the first word, given a sentence + * @param STR original full string + * @return substring up to the first whitespace + */ +std::string string_first_word(const std::string STR); + +/** + * @brief Returns the string with the first word removed + * @param STR original full string + * @return substring after the first whitespace + */ +std::string string_remove_first_word(const std::string STR); + +/** + * @brief Returns the second word, given a sentence + * @param STR original full string + * @return substring after the first whitespace up to the second whitespace + */ +std::string string_second_word(const std::string STR); + +/** + * @brief Returns the third word, given a sentence + * @param STR original full string + * @return substring after the second whitespace up to the third whitespace + */ +std::string string_third_word(const std::string STR); + +/** + * @brief Returns the nth word, given a sentence + * @param STR original full string + * @param N word within STR to return (beginning at 1) + * @return corresponding word from STR + */ +std::string string_nth_word(const std::string STR, const int N); + +/** + * @brief Splits a string into a list of tokens deliminated by a given character + * + * @param STR string to tokenize + * @param DELIMINATOR character to split on + * @return vector list of all tokens in the order present in the original string + */ +std::vector string_tokenize(const std::string STR, const char DELIMINATOR); + +/** + * @brief Returns a string substituting target character with replacement character + * @param STR original full string (the haystack) + * @param TARGET character to replace (the needle) + * @param REPLACEMENT character to substitute with + * @return string with all instance of TARGET replaced with REPLACEMENT + */ +std::string string_substitute(const std::string STR, const char TARGET, const char REPLACEMENT); + +/** + * @brief Returns a string with all uppercase characters converted to lowercase characters + * + * @param STR original string + * @return string with lowercase characters + */ +std::string string_to_lower(const std::string STR); + +/** + * @brief Returns a string with all lowercase characters converted to uppercase characters + * + * @param STR original string + * @return string with uppercase characters + */ +std::string string_to_upper(const std::string STR); + +/** + * @brief Compares two strings to determine their ordering or equality. Returns -1 if + * LHS < RHS. Returns 0 if LHS == RHS. Returns 1 if LHS > RHS + * + * @param LHS left hand string + * @param RHS right hand string + * @return int representing relationship of LHS to RHS + */ +int string_compare(const std::string LHS, const std::string RHS); + +#endif \ No newline at end of file diff --git a/string_functions.o b/string_functions.o new file mode 100644 index 0000000..84faf86 Binary files /dev/null and b/string_functions.o differ diff --git a/test_suite.cpp b/test_suite.cpp new file mode 100644 index 0000000..98196d2 --- /dev/null +++ b/test_suite.cpp @@ -0,0 +1,256 @@ +#include "test_suite.h" +#include "string_functions.h" + +#include +#include + +using namespace std; + +const int MESSAGE_WIDTH = 45; + +int test_int(int& testNum, const string MESSAGE, const int LHS, const int RHS) { + cout << "Test #" << setw(3) << right << ++testNum; + cout << setw(MESSAGE_WIDTH) << right; + cout << MESSAGE << ": "; + cout << left; + if (LHS == RHS) + cout << " PASSED \n"; + else + cout << " !!>FAILEDFAILEDFAILEDFAILED& LHS, const vector& RHS) { + cout << "Test #" << setw(3) << right << ++testNum; + cout << setw(MESSAGE_WIDTH) << right; + cout << MESSAGE << ": "; + cout << left; + + bool vectorsMatch = (LHS.size() == RHS.size()); + if (vectorsMatch) { + for(unsigned int i = 0; i < LHS.size(); i++) { + if(LHS.at(i) != RHS.at(i)) { + vectorsMatch = false; + break; + } + } + } + if (vectorsMatch) { + cout << " PASSED \n"; + } else { + cout << " !!>FAILED tokens1 = {"The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"}; + totalPassed += test_vector_string( totalNumTests, "Testing string_tokenize()", string_tokenize("The quick brown fox jumped over the lazy dog", ' '), tokens1 ); + totalPassed += test_vector_string( totalNumTests, "Testing string_tokenize()", string_tokenize("The@quick@brown@fox@jumped@over@the@lazy@dog", '@'), tokens1 ); + + vector tokens2 = {"The quick brown fox jumped over the lazy dog"}; + totalPassed += test_vector_string( totalNumTests, "Testing string_tokenize()", string_tokenize("The quick brown fox jumped over the lazy dog", '*'), tokens2 ); + + vector tokens3 = {""}; + totalPassed += test_vector_string( totalNumTests, "Testing string_tokenize()", string_tokenize("", '*'), tokens3 ); + cout << endl; + + totalPassed += test_string( totalNumTests, "Testing string_substitute()", string_substitute("The Gxxgle", 'x', 'o') , "The Google" ); + totalPassed += test_string( totalNumTests, "Testing string_substitute()", string_substitute("$chool of Mine$", '$', 's') , "school of Mines" ); + totalPassed += test_string( totalNumTests, "Testing string_substitute()", string_substitute("$chool of Mine$", '+', '*') , "$chool of Mine$" ); + totalPassed += test_string( totalNumTests, "Testing string_substitute() twice", string_substitute(string_substitute("D--", '-', '+'), 'D', 'C') , "C++" ); + cout << endl; + + totalPassed += test_string( totalNumTests, "Testing string_to_lower()", string_to_lower("This SHOULD be LOWER case") , "this should be lower case" ); + totalPassed += test_string( totalNumTests, "Testing string_to_lower()", string_to_lower("MNASDF874792L[]//.;[\t],") , "mnasdf874792l[]//.;[\t]," ); + totalPassed += test_string( totalNumTests, "Testing string_to_lower()", string_to_lower("C++") , "c++" ); + totalPassed += test_string( totalNumTests, "Testing string_to_lower()", string_to_lower("this is already lower case") , "this is already lower case" ); + totalPassed += test_string( totalNumTests, "Testing string_to_lower()", string_to_lower("1234567890,./;'[]") , "1234567890,./;'[]" ); + cout << endl; + + totalPassed += test_string( totalNumTests, "Testing string_to_upper()", string_to_upper("This SHOULD be upper case") , "THIS SHOULD BE UPPER CASE" ); + totalPassed += test_string( totalNumTests, "Testing string_to_upper()", string_to_upper("mnasdf874792l[]//.;[\t],") , "MNASDF874792L[]//.;[\t]," ); + totalPassed += test_string( totalNumTests, "Testing string_to_upper()", string_to_upper("c++") , "C++" ); + totalPassed += test_string( totalNumTests, "Testing string_to_upper()", string_to_upper("THIS IS ALREADY UPPER CASE") , "THIS IS ALREADY UPPER CASE" ); + totalPassed += test_string( totalNumTests, "Testing string_to_upper()", string_to_upper("1234567890,./;'[]") , "1234567890,./;'[]" ); + cout << endl; + + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("C++", "c++") , -1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("C++", "C++") , 0 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("c++", "c++") , 0 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("c++", "C++") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("short", "shorter") , -1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("longer", "long") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("after", "later") , -1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("later", "after") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("G", "g") , -1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("g", "G") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("GoOse", "Bye") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("Equal", "Equal") , 0 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("Not Empty", "") , 1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("", "Not Empty") , -1 ); + totalPassed += test_int( totalNumTests, "Testing string_compare()", string_compare("", "") , 0 ); + cout << endl; + + cout << "Tests Passed: " << setw(3) << right << totalPassed << " / " << setw(3) << totalNumTests << " (" << setprecision(0) << fixed << totalPassed * 100.0f / totalNumTests << "%)" << endl << endl; + + return (totalPassed == totalNumTests); +} \ No newline at end of file diff --git a/test_suite.h b/test_suite.h new file mode 100644 index 0000000..9d0695a --- /dev/null +++ b/test_suite.h @@ -0,0 +1,73 @@ +#ifndef TEST_SUITE_H +#define TEST_SUITE_H + +#include +#include + +/** + * @brief Test suite. Runs all tests. + * @return true if all tests pass, false otherwise + */ +bool run_all_tests(); + +/** + * @brief A generic test function, that simply prints "PASSED" if LHS equals RHS + * and otherwise prints FAILED. Do not modify this function. + * + * @param testNum Number of the test. Value will be incremented upon completion + * @param MESSAGE description of test + * @param LHS computed result + * @param RHS expected result + * @return 1 if test passed, 0 otherwise + */ +int test_int(int& testNum, const std::string MESSAGE, const int LHS, const int RHS); + +/** + * @brief A generic test function, that simply prints "PASSED" if LHS equals RHS + * and otherwise prints FAILED. Do not modify this function. + * + * @param testNum Number of the test. Value will be incremented upon completion + * @param MESSAGE description of test + * @param LHS computed result + * @param RHS expected result + * @return 1 if test passed, 0 otherwise + */ +int test_unsigned_long(int& testNum, const std::string MESSAGE, const unsigned long LHS, const unsigned long RHS); + +/** + * @brief A generic test function, that simply prints "PASSED" if LHS equals RHS + * and otherwise prints FAILED. Do not modify this function. + * + * @param testNum Number of the test. Value will be incremented upon completion + * @param MESSAGE description of test + * @param LHS computed result + * @param RHS expected result + * @return 1 if test passed, 0 otherwise + */ +int test_char(int& testNum, const std::string MESSAGE, const char LHS, const char RHS); + +/** + * @brief A generic test function, that simply prints "PASSED" if LHS equals RHS + * and otherwise prints FAILED. Do not modify this function. + * + * @param testNum Number of the test. Value will be incremented upon completion + * @param MESSAGE description of test + * @param LHS computed result + * @param RHS expected result + * @return 1 if test passed, 0 otherwise + */ +int test_string(int& testNum, const std::string MESSAGE, const std::string LHS, const std::string RHS); + +/** + * @brief A generic test function, that simply prints "PASSED" if LHS equals RHS + * and otherwise prints FAILED. Do not modify this function. + * + * @param testNum Number of the test. Value will be incremented upon completion + * @param MESSAGE description of test + * @param LHS computed result + * @param RHS expected result + * @return 1 if test passed, 0 otherwise + */ +int test_vector_string(int& testNum, const std::string MESSAGE, const std::vector& LHS, const std::vector& RHS); + +#endif \ No newline at end of file diff --git a/test_suite.o b/test_suite.o new file mode 100644 index 0000000..ed0c86f Binary files /dev/null and b/test_suite.o differ