diff --git a/.gitignore b/.gitignore index 6ca11bdb7bdfc811b8ff8e03fd4937696fe5974f..bba67c15c7d205245ebd9cdee200fe2cb8e44a28 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ area/area.exe area/test.exe .vscode/ msg.txt +lab07/build.bat diff --git a/lab-07/MockAnalysisTool.txt b/lab-07/MockAnalysisTool.txt new file mode 100644 index 0000000000000000000000000000000000000000..6f0453e4ccc026dbdae2684162d65cdd56285743 --- /dev/null +++ b/lab-07/MockAnalysisTool.txt @@ -0,0 +1,20 @@ +--filterNames()-- + +Susan Brewer + +Eugene McMannon + +Susan Brewer + +Eugene McMannon + +--sortNames()-- + +Eugene McMannon + +Eugene McMannon + +Susan Brewer + +Susan Brewer + diff --git a/lab-07/faculty.txt b/lab-07/faculty.txt new file mode 100644 index 0000000000000000000000000000000000000000..50ed0d77149cc7f69f1db4f6b4989009883eacf5 --- /dev/null +++ b/lab-07/faculty.txt @@ -0,0 +1,4 @@ +Foster, James +Aamodt, Larry +Duncan, Jonathan +Carman, Preston diff --git a/lab-07/lab7.exe b/lab-07/lab7.exe new file mode 100644 index 0000000000000000000000000000000000000000..b84542fe10c6be1cc0313dee9733e78e63a46cf0 Binary files /dev/null and b/lab-07/lab7.exe differ diff --git a/lab-07/main.cpp b/lab-07/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59c87a84ec2a39e48cec504d8f9e2740f6360bb6 --- /dev/null +++ b/lab-07/main.cpp @@ -0,0 +1,307 @@ +/* + * CPTR 245 Lab 07 + * + * 1. Use dependency injection to provide a StubbedSource for canned responses + * 2. Avoid the user interface by getting the user type from the command line + * 3. Use a mock test double for the application analysis + * 4. Use fake time + */ + +#include // std::sort +#include +#include +#include +#include +#include +#include // std::this_thread::sleep_for +#include + +using namespace std; +using namespace std::chrono; + +class Clock { +public: + virtual uint64_t msSinceEpoch() = 0; + virtual void sleepMs(int ms) = 0; +}; + +class SystemClock : public Clock { +public: + uint64_t msSinceEpoch() { + return duration_cast(system_clock::now().time_since_epoch()) + .count(); + } + void sleepMs(int ms) { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + } +}; + +// PROBLEM 4 +// The instructions were a little fuzzy on this one, personally, maybe I just didn't understand it. +// Rather than use whatever the system time was, I just had it count up by increments of ten. We can +// assume that it's counting in milliseconds, so "epoch" is in units of ms. +class FakeClock : public Clock { +public: + uint64_t epoch = 0; + uint64_t msSinceEpoch(){ + return epoch; + } + void sleepMs(int ms) { + epoch+=10; + } +}; + +class DataSource { +public: + virtual vector names(int userType) = 0; +}; + +class FileSource : public DataSource { +public: + vector names(int userType); +}; + +// PROBLEM 1 +// Creates an almost identical DataSource that has its own implementation of names(int userType) +class StubbedSource : public DataSource { +public: + vector names(int userType); +}; + +// PROBLEM 1 +// Rather than read from a file, simply pushes random names to the vector +vector StubbedSource::names(int userType) { + vector names; + switch(userType) { + case 1: + names.push_back("Bob Saget"); + names.push_back("Susan Brewer"); + names.push_back("Eugene McMannon"); + case 2: + names.push_back("Bob Saget"); + names.push_back("Susan Brewer"); + names.push_back("Eugene McMannon"); + } + return names; +} + + +vector FileSource::names(int userType) { + string filename; + vector names; + switch (userType) { + case 1: + filename = "students.txt"; + break; + case 2: + filename = "faculty.txt"; + break; + default: + cerr << "Invalid userType: " << userType << endl; + exit(1); + } + ifstream inFS; + inFS.open(filename); + if (!inFS.is_open()) { + cerr << "Could not open \"" << filename << '\"' << endl; + exit(1); + } + while (!inFS.eof()) { + string name; + getline(inFS, name); + if (name.size()) { + names.push_back(name); + } + } + inFS.close(); + return names; +} + +class AnalysisTool { +public: + virtual vector filterNames(vector names) = 0; + virtual vector sortNames(vector names) = 0; + virtual void printNames(vector names) = 0; +}; + +class RealAnalysisTool : public AnalysisTool { +public: + vector filterNames(vector names); + vector sortNames(vector names); + void printNames(vector names); +}; + +vector RealAnalysisTool::filterNames(vector names) { + vector result; + for (int i = 0; i < names.size(); ++i) { + if (names.at(i).at(0) % 2) { + result.push_back(names.at(i)); + } + } + return result; +} + +vector RealAnalysisTool::sortNames(vector names) { + vector result = names; + sort(result.begin(), result.end()); + return result; +} + +void RealAnalysisTool::printNames(vector names) { + for (int i = 0; i < names.size(); ++i) { + cout << i << ": " << names.at(i) << endl; + } +} + +// PROBLEM 3 +// This class runs the same operations that the RealAnalysisTool does, except that on every call, it pushes back the +// name of the function call and the results of the output. +class MockAnalysisTool : public AnalysisTool { +public: + + vector functionNames; + vector filterNames(vector names); + vector sortNames(vector names); + void printNames(vector names); +}; + +vector MockAnalysisTool::filterNames(vector names) { + functionNames.push_back("--filterNames()--"); + vector result; + for (int i = 0; i < names.size(); ++i) { + if (names.at(i).at(0) % 2) { + result.push_back(names.at(i)); + } + } + for (int i = 0; i < result.size(); i++){ + functionNames.push_back(result.at(i)); + } + return result; +} + +vector MockAnalysisTool::sortNames(vector names) { + functionNames.push_back("--sortNames()--"); + vector result = names; + sort(result.begin(), result.end()); + for (int i = 0; i < result.size(); i++){ + functionNames.push_back(result.at(i)); + } + return result; +} + +void MockAnalysisTool::printNames(vector names) { + string filename = "MockAnalysisTool.txt"; + ofstream onFS; + onFS.open(filename); + if(!onFS.is_open()) { + ofstream file(filename); + } + for (int i = 0; i < functionNames.size(); i++){ + onFS << functionNames.at(i) << "\n" << endl; + } + onFS.close(); +} + +class Application { +public: + Application(DataSource *dataSource) { this->dataSource = dataSource; } + int getUserInput(); + vector getNames(int userType); + +private: + DataSource *dataSource; +}; + +int Application::getUserInput() { + while (true) { + int choice; + cout << "Enter user type (1 = student; 2 = faculty): "; + cin >> choice; + if (cin && 1 <= choice && choice <= 2) { + return choice; + } + cerr << "Invalid response!" << endl; + cin.clear(); + cin.ignore(100, '\n'); + } +} + +vector Application::getNames(int userType) { + return dataSource->names(userType); +} + +/* + * Arguments (all optional): + * 1: DataSource subclass name (defaults to FileSource) + * 2: user type (defaults to asking user) + * 3: AnalysisTool subclass name (defaults to RealAnalysisTool) + * 4: Clock subclass name (defaults to SystemClock) + * + */ +int main(int argc, char *argv[]) { + // collect the arguments + vector args(10); + for (int i = 0; i < argc; ++i) { + args.at(i) = argv[i]; + } + + // select the data source + DataSource *dataSource; + if (args.at(1) == "" || args.at(1) == "FileSource") { + dataSource = new FileSource(); + } else if (args.at(1) == "StubbedSource"){ //Problem 1 code + dataSource = new StubbedSource(); + } else { + cerr << "unrecognized data source" << endl; + exit(1); + } + // Use dependency injection for the data source + Application app(dataSource); + + // Get the user type (avoid the user interface during testing) + // PROBLEM 2: Adjusted for command line testing + int userType; + if (args.at(2) == ""){ + userType = app.getUserInput(); + } else if (args.at(2) == "1"){ + userType = 1; + } else if (args.at(2) == "2"){ + userType = 2; + } else { + cerr << "Invalid user input" << endl; + } + + // read from the data source + vector names = app.getNames(userType); + + // do analysis of the names (use a mock for testing) + if (args.at(3) == "" || args.at(3) == "RealAnalysisTool"){ + AnalysisTool *tool; + tool = new RealAnalysisTool(); + names = tool->filterNames(names); + names = tool->sortNames(names); + tool->printNames(names); + } else { + AnalysisTool *tool; + tool = new MockAnalysisTool(); + names = tool->filterNames(names); + names = tool->sortNames(names); + tool->printNames(names); + } + + Clock *clock; + // Determining, based on the args, if FakeClock or System clock is to be used + if (args.at(4) == "" || args.at(4) == "SystemClock"){ + clock = new SystemClock(); + } else if (args.at(4) == "FakeClock"){ + clock = new FakeClock(); + } + // do long-running process (faking time for testing) + uint64_t start = clock->msSinceEpoch(); + cout << "start long-running process at " << start << endl; + while (clock->msSinceEpoch() < start + 2000) { + clock->sleepMs(10); + } + cout << " end long-running process at " << clock->msSinceEpoch() << endl; + return 0; +} diff --git a/lab-07/students.txt b/lab-07/students.txt new file mode 100644 index 0000000000000000000000000000000000000000..a9dbf449be1f66ebf08a7d15828185bbb7bdbaf0 --- /dev/null +++ b/lab-07/students.txt @@ -0,0 +1,18 @@ +Tinker, Hayden +Benko, Matija +Sukachevin, Kieran +Smith, Taylor +Herbel, Caleb +Hernandez, Samuel +Jones, Julian +Taylor, Evan +Thomsen, Catherine +DePaula, Stefan +Fairchild, Kaden +Hartman, Joel +Taylor, Adam +Calvo, Eliam +Price, Quinton +Reklai, Reece +Riggs, Jason +Moody, Garrett