#pragma once ///////////////////////////////////////////////////////////////////// // Proj1Reqs_Tests.h - Demonstrate Project #1 Requirements // // ver 1.3 using the single-user TestHarness // // // // Jim Fawcett, CSE687-Object Oriented Design, Fall 2018 // ///////////////////////////////////////////////////////////////////// /* * Package Operations: * ------------------- * Proj1Reqs_Tests provides eleven tests, one for each requirement or, * in some cases, part of a requirement. These run in the single-user * TestHarness, which Project #1 develops. * * All but the CodeUtility tests are defined in this Proj1Reqs_Tests.h. * They are executed in the main function in Proj1_TestDriver.cpp. * * Notes: * - Each test contains, in comments, a Test Description and Test * Procedure, intended to be a sample of how tests are documented. * - This package uses the single-user TestHarness, defined in * TestUtilities, to test itself. * - Uses Singleton Logger interface and object factory, and so, are not * bound to its implementation details. * * Required Files: * --------------- * Proj1Reqs_Tests.h, Proj1_TestDriver.cpp * TestUtilities.h * CodeUtilities.h * StringUtilities.h * FileUtilities.h * SingletonLogger.h * FileSystem.h, FileSystem.cpp * * Maintenance History: * -------------------- * ver 1.3 : 02 Oct 2018 * - minor changes to comments * ver 1.2 : 23 Sep 2018 * - completed reqs testing, including tests for CodeUtils * - refactored tests into this file and CodeUtil_Tests.h * ver 1.1 : 04 Sep 2018 * - added additional tests for Req #5 * ver 1.0 : 03 Sep 2018 * - first release */ #include #include #include #include #include "../TestUtilities/TestUtilitiesDemo.h" #include "../CodeUtilities/CodeUtilities.h" #include "../StringUtilities/StringUtilities.h" #include "../FileUtilities/FileUtilities.h" //#include "../SingletonLogger/SingletonLogger/SingletonLogger.h" #include "../SingletonLogger/SingletonLogger/ISingletonLogger.h" #include "../SingletonLogger/SingletonLogger/SingletonLoggerFactory.h" namespace UtilitiesTest { using Path = std::string; using Message = std::string; using Line = size_t; using File = std::string; using Files = std::vector; using ILogger = CodeUtilities::ILogger<1, CodeUtilities::NoLock>; using LoggerFactory = CodeUtilities::SingletonLoggerFactory<1, CodeUtilities::NoLock>; using Pattern = std::string; using Patterns = std::vector; class TestReq1 : public Utilities::ITest { /* * Test Description: * Demonstrates use of C++ * Test Procedure: * - Displays C++ files in project directory */ private: const std::string path_; public: TestReq1(const std::string& path) : path_(path) {} ~TestReq1() { std::cout << "\n deleting TestReq1"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #1 - Use C++"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Demonstrate by viewing files in project directory"); Path path = path_ + "/TestUtilities"; Patterns pats = { "*.h", "*.cpp" }; return Utilities::showDirContents(path, "", pats); } }; class TestReq2a : public Utilities::ITest { /* * Test Description: * Demonstrates use of C++ streams * Test Procedure: * - Displays lines of project source code that use iostream. * - To insure that std streams are used for ALL output * requires code inspection. */ private: const std::string path_; public: TestReq2a(const std::string& path) : path_(path) {} ~TestReq2a() { std::cout << "\n deleting TestReq2a"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #2a - use std::streams"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("demonstrate by viewing code - see line 16"); Path fileSpec = path_ + "/TestUtilities/TestUtilitiesDemo.cpp"; return Utilities::showFileLines(fileSpec, 1, 20); } }; class TestReq2b : public Utilities::ITest { /* * Test Description: * Demonstrates use of C++ new and delete operators * Test Procedure: * - Displays lines of project source code that use new * and std::shared-ptr which will use delete when out * of scope. * - To insure no use of malloc or free requires code * inspection or more advanced test tool with text * finder capability. */ private: const std::string path_; public: TestReq2b(const std::string& path) : path_(path) {} ~TestReq2b() { std::cout << "\n deleting TestReq2b"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #2b - use new and delete"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Demonstrate by viewing project source code"); pLog->write("Use of std::shared_ptr demonstrates delete"); Path fileSpec = path_ + "/TestUtilities/TestUtilitiesDemo.cpp"; return Utilities::showFileLines(fileSpec, 104, 113); } }; class TestReq3 : public Utilities::ITest { /* * Test Description: * Demonstrates that tests implement ITest interface * Test Procedure: * - Displays lines of project source code where tests * are defined. */ private: const std::string path_; public: TestReq3(const std::string& path) : path_(path) {} ~TestReq3() { std::cout << "\n deleting TestReq3"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #3 - tests implement ITest interface"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Demonstrate by viewing project source code"); Path fileSpec = path_ + "/TestUtilities/TestUtilitiesDemo.cpp"; return Utilities::showFileLines(fileSpec, 26, 33); } }; class TestReq4a : public Utilities::ITest { /* * Test Description: * Demonstrates use of test container in test framework * Test Procedure: * - Displays lines of project source code where container * is defined. */ private: const std::string path_; public: TestReq4a(const std::string& path) : path_(path) {} ~TestReq4a() { std::cout << "\n deleting TestReq4a"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #4a - provides test container"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Demonstrate by viewing project code - see line 125 and line 132"); Path fileSpec2 = path_ + "/TestUtilities/TestUtilitiesDemo.h"; return Utilities::showFileLines(fileSpec2, 118, 134); } }; class TestReq4b : public Utilities::ITest { /* * Test Description: * Demonstrates use of test container in test framework * Test Procedure: * - Displays lines of this demo where tests are registered * to be placed in test container. */ private: const std::string path_; public: TestReq4b(const std::string& path) : path_(path) {} ~TestReq4b() { std::cout << "\n deleting TestReq4b"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #4b - provides test container"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Source code shows where this demo's tests are registered"); Path fileSpec = path_ + "/DemoReqs/Proj1_TestDriver.cpp"; return Utilities::showFileLines(fileSpec, 64, 75); } }; class TestPasses : public Utilities::ITest { public: bool test() { return true; } virtual ~TestPasses() { std::cout << "\n -- deleting TestPasses"; } }; class TestFails : public Utilities::ITest { public: bool test() { return false; } ~TestFails() { std::cout << "\n -- deleting TestFails"; } }; class TestThrows : public Utilities::ITest { public: bool test() { std::exception ex("\n -- this test always throws -- "); throw ex; } ~TestThrows() { std::cout << "\n -- deleting TestThrows"; } }; class TestReq5 : public Utilities::ITest { /* * Test Description: * Demonstrates execution of tests in try-catch block * Test Procedure: * - Displays lines of project source code where tests are * run inside try catch block. * - runs tests that: * 1. always passes - show that tests that succeed are correctly reported * 2. always fails - show that tests that fail are correctly reported * 3. always throws - show that exception causes failure report without * without stopping succeeding test executions * * Note: * - This test uses a TestHarness instance to run tests in a test * that will be run by the TestHarness. Mind-bending :-) */ private: const std::string path_; public: TestReq5(const std::string& path) : path_(path) {} ~TestReq5() { std::cout << "\n deleting TestReq5"; } bool test() { Utilities::Cosmetic cos_; // show code with try-catch block ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #5 - function that executes tests within try-catch block"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Demonstrate by viewing function in project source code"); Path fileSpec = path_ + "/TestUtilities/TestUtilitiesDemo.h"; bool t1 = Utilities::showFileLines(fileSpec, 87, 103); pLog->write("\n Continue demonstration by running several small tests:"); // use that code to run self tests pLog->write("Create instance of TestHarness to run specific tests"); Utilities::TestExecutive te; using SPtr = std::shared_ptr; pLog->write("Using test framework to run test that always passes"); SPtr pTt2(new TestPasses); te.registerTest(pTt2, "TestPasses"); bool t2 = te.doTests(); te.clearTests(); pLog->write(" Using test framework to run test that always fails"); SPtr pTt3(new TestFails); te.registerTest(pTt3, "TestFails"); bool t3 = te.doTests(); te.clearTests(); pLog->write(" Using test framework to run test that always throws"); SPtr pTt4(new TestThrows); te.registerTest(pTt4, "TestThrows"); bool t4 = te.doTests(); te.clearTests(); return (t1 == true && t2 == true && t3 == false && t4 == false); } }; class TestReq6 : public Utilities::ITest { /* * Test Description: * Demonstrates use of logger * Test Procedure: * - Logs Head, Body, and Tail. */ private: const std::string path_; public: TestReq6(const std::string& path) : path_(path) {} ~TestReq6() { std::cout << "\n deleting TestReq6"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #6 - Use logging facility"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->setAuthor("Jim Fawcett"); pLog->write(msg); pLog->writeHead("This is a Log Header"); pLog->write("This is a log item"); pLog->writeTail("This is the end of log\n"); return true; } }; class TestReq7 : public Utilities::ITest { /* * Test Description: * Demonstrates use of logger with multiple streams * Test Procedure: * - open file ofstream * - add to logger * - write a message * - show that output goes to both console and file * - do that by writing, closing file stream, opening for reading * and send file contents to console to compare with console log. */ private: const std::string path_; public: TestReq7(const std::string& path) : path_(path) {} ~TestReq7() { std::cout << "\n deleting TestReq7"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #7 - Use logging facility"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); std::string fileSpec = "Test.txt"; pLog->write("Adding FileStream for \"" + fileSpec + "\""); std::ofstream ofs(fileSpec); if (ofs.good()) { pLog->write("-- opened file \"" + fileSpec + "\""); pLog->addStream(&ofs); pLog->write(">> writing to multiple streams"); pLog->removeStream(&ofs); ofs.close(); std::ifstream ifs(fileSpec); if (ifs.good()) { std::ostringstream oss; oss << ifs.rdbuf(); std::string fileContents = oss.str(); fileContents = Utilities::trimNewLines(fileContents); pLog->write("contents of \"" + fileSpec + "\" are:"); pLog->write(fileContents); pLog->write("\n you see two identical messages, so test passed"); return true; } return false; } else { pLog->write("-- failed to open file \"" + fileSpec + "\""); return false; } return true; } }; class TestReq8 : public Utilities::ITest { /* * Test Description: * Demonstrates typical logger messages * Test Procedure: * - Log Head, Body, and Tail and observer output */ private: const std::string path_; public: TestReq8(const std::string& path) : path_(path) {} ~TestReq8() { std::cout << "\n deleting TestReq8"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #8 - Message information: Test, Author, Execution date"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg + "\n -- repeating test #6"); pLog->writeHead("This is a Log Header"); pLog->write("This is a log item"); pLog->writeTail("This is the end of log\n"); return true; } }; // Tests for Requirement #9 are all included in CodeUtil_Tests.h class TestReq10 : public Utilities::ITest { /* * Test Description: * Demonstrates use of Automated Testing * Test Procedure: * - Observe all of the other output. */ private: const std::string path_; public: TestReq10(const std::string& path) : path_(path) {} ~TestReq10() { std::cout << "\n deleting TestReq10"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #10 - Automated Regression Test Suite"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); msg = "All of this output is generated by tests run in the TestHarness"; pLog->write(msg); pLog->writeTail(" "); return true; } }; class TestReq11 : public Utilities::ITest { /* * Test Description: * Demonstrates that tests have descriptions and procedures. * Test Procedure: * - Displays lines of test code containing test description and * test procedure comments. */ private: const std::string path_; public: TestReq11(const std::string& path) : path_(path) {} ~TestReq11() { std::cout << "\n deleting TestReq11"; } bool test() { ILogger* pLog = LoggerFactory::getInstance(); Message msg = "\n Req #11 - provide test description and test procedure"; pLog->write(msg); msg = "------------------------------------------------------------------------"; pLog->write(msg); pLog->write("Show test code with description and procedure"); Path fileSpec = path_ + "/DemoReqs/Proj1Reqs_Tests.h"; return Utilities::showFileLines(fileSpec, 473, 488); } }; }