#include #include #include #include #include #include "CodeDweller/child.hpp" //////////////////////////////////////////////////////////////////////////////// // Configuration /////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// Child program name. const std::string childName("./childProgram"); //////////////////////////////////////////////////////////////////////////////// // End of configuration //////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int nTotalTests = 0; int nPass = 0; int nFail = 0; bool result; #define NO_EXCEPTION_TERM(msg) \ std::cout \ << msg << " failed to throw exception at line " \ << __LINE__ << "." << std::endl #define EXCEPTION_TERM(msg) \ std::cout \ << msg << " threw unexpected exception at line " \ << __LINE__ << ": " << e.what() << std::endl #define RETURN_FALSE(msg) \ std::cout \ << msg << " at line " << __LINE__ << std::endl; \ return false; #define RUN_TEST(test) \ std::cout << " " #test ": "; \ std::cout.flush(); \ result = test(); \ std::cout << (result ? "ok" : "fail") << std::endl; \ nTotalTests++; \ if (result) nPass++; else nFail++; #define SUMMARY \ std::cout \ << "\nPass: " << nPass \ << ", Fail: " << nFail \ << ", Total: " << nTotalTests << std::endl //////////////////////////////////////////////////////////////////////////////// // Tests /////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// bool testIsDone() { try { CodeDweller::Child child(childName); // Test exception if called out-of-order. try { child.isDone(); NO_EXCEPTION_TERM("isDone() called without run()"); return false; } catch (std::exception &e) { } child.run(); if (child.isDone()) { std::cout << "isDone() failure; returned true." << std::endl; return false; } // Command the child to exit. child.writer << 'q'; child.writer.flush(); // Sleep to let the child exit. std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (!child.isDone()) { std::cout << "isDone() failure; returned false." << std::endl; return false; } } catch (std::exception &e) { EXCEPTION_TERM("isDone()"); return false; } return true; } bool testResult() { try { std::vector cmd; cmd.push_back(childName); cmd.push_back("quit"); CodeDweller::Child child(cmd); // Test exception if called out-of-order. try { (void) child.result(); NO_EXCEPTION_TERM(" result() called without run()"); return false; } catch (std::exception &e) { } child.run(); // Test exception if called while child is running. try { (void) child.result(); NO_EXCEPTION_TERM(" result() called before child exited"); return false; } catch (std::exception &e) { } // Wait for the child to exit. std::this_thread::sleep_for(std::chrono::milliseconds(100)); int32_t result = child.result(); if (25 != result) { std::cout << "result() failure; returned " << result << " instead of 25." << std::endl; return false; } } catch (std::exception &e) { EXCEPTION_TERM("result()"); return false; } return true; } bool testTerminate() { // Test with no waiting. try { CodeDweller::Child child(childName); child.run(); child.terminate(); } catch (std::exception &e) { EXCEPTION_TERM("terminate() with no waiting"); return false; } // Test with waiting. try { CodeDweller::Child child(childName); child.run(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); child.terminate(); } catch (std::exception &e) { EXCEPTION_TERM("terminate() with 100 ms waiting"); return false; } // Test after the child exits. std::vector cmd; cmd.push_back(childName); cmd.push_back("quit"); try { CodeDweller::Child child(cmd); child.run(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); child.terminate(); } catch (std::exception &e) { EXCEPTION_TERM("terminate() after child exits"); return false; } // Test exception thrown for out-of-order calling. try { CodeDweller::Child child(cmd); child.terminate(); NO_EXCEPTION_TERM("terminate() called without run()"); return false; } catch (std::exception &e) { } return true; } bool testReaderWriter() { try { size_t bufSize = 15; CodeDweller::Child child(childName, bufSize); std::ostringstream childOutput; std::vector expectedChildOutput; char readChar; // Generate input. char randomLetter[] = "abcdefghijklmnop#rstuvwxyz"; int nChar = bufSize - 1; int nLines = 2; for (int iLine = 0; iLine < nLines; iLine++) { std::string line; line.erase(); for (int iChar = 0; iChar < nChar; iChar++) { line.push_back(randomLetter[std::rand() % 26]); } expectedChildOutput.push_back(line); } // Test exception. try { child.writer << bufSize; child.writer.flush(); NO_EXCEPTION_TERM(" writer called without run()"); return false; } catch (std::exception &e) { } // Clear the writer stream. try { child.writer.clear(); } catch (std::exception &e) { } child.run(); // Write, read, put back, and reread each character. for (std::string line : expectedChildOutput) { // Write one line. const char *ptr; ptr = line.data(); for (std::string::size_type i = 0; i < line.length(); i++) { child.writer << ptr[i]; if (!child.writer) { RETURN_FALSE(" Failure in testReaderWriter: writer stream is bad"); } } child.writer.flush(); if (!child.writer) { RETURN_FALSE(" Failure in testReaderWriter: writer stream is bad"); } // Read one line. std::string readLine; readLine.erase(); for (std::string::size_type i = 0; i < line.length(); i++) { child.reader >> readChar; if (!child.reader) { RETURN_FALSE(" Failure in testReaderWriter: reader stream is bad"); } readLine.push_back(readChar); } // Convert to upper case. std::string expectedLine; expectedLine = line; for (auto &c : expectedLine) { c = toupper(c); } // Compare. if (expectedLine != readLine) { std::cout << " Failure in testReaderWriter." << std::endl; std::cout << " Expected: '" << expectedLine << "'\n Received: '" << readLine << "'" << std::endl; return false; } } // Send exit message. child.writer << 'q'; child.writer.flush(); if (!child.writer) { RETURN_FALSE(" Failure in testReaderWriter: writer stream is bad"); } // Verify exit. std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (!child.isDone()) { std::cout << " Failure in testReaderWriter: " << "Child program did not exit." << std::endl; return false; } } catch (std::exception &e) { EXCEPTION_TERM("reader()/writer()"); return false; } return true; } bool testReader() { try { std::vector cmd; cmd.push_back(childName); cmd.push_back("write"); size_t bufSize = 32; CodeDweller::Child child(cmd, bufSize); // Test exception. try { int temp; child.reader >> temp; NO_EXCEPTION_TERM(" reader called without run()"); return false; } catch (std::exception &e) { } child.reader.clear(); child.writer.clear(); std::ostringstream childOutput; std::string expectedChildOutput("This is a test"); char readChar; child.run(); child.reader.exceptions(std::istream::badbit); child.reader >> std::noskipws; if (!child.reader) { RETURN_FALSE(" Failure in testReader: reader stream is bad"); } while (child.reader >> readChar) { if (!child.reader) { RETURN_FALSE(" Failure in testReader: reader stream is bad"); } child.reader.putback(readChar); if (!child.reader) { RETURN_FALSE(" Failure in testReader: reader stream is bad"); } child.reader >> readChar; if (!child.reader) { RETURN_FALSE(" Failure in testReader: reader stream is bad"); } childOutput << readChar; } if (!child.reader.eof()) { RETURN_FALSE(" Failure in testReader: Error occured before " "EOF was reached while reading"); } // Check. if (childOutput.str() != expectedChildOutput) { std::cout << " reader() failure in testReader." << std::endl; std::cout << " Expected: '" << expectedChildOutput << "'\n Received: '" << childOutput.str() << "'" << std::endl; return false; } if (!child.isDone()) { std::cout << "isDone() failure in testReader." << std::endl; return false; } } catch (std::exception &e) { EXCEPTION_TERM("reader()"); return false; } return true; } //////////////////////////////////////////////////////////////////////////////// // End of tests //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int main() { std::cout << "CodeDweller::Child unit tests" << std::endl << std::endl; CodeDweller::Child child(childName); RUN_TEST(testIsDone); RUN_TEST(testResult); RUN_TEST(testTerminate); RUN_TEST(testReader); RUN_TEST(testReaderWriter); SUMMARY; return 0; }