| 
				
			 | 
			
			 | 
			@@ -53,6 +53,177 @@ bool result; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  << ", Fail:  " << nFail                       \
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  << ", Total:  " << nTotalTests << std::endl
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool doReadWrite(size_t bufSize,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                 size_t nominalAboveMin_ms,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                 size_t delta_ms,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                 size_t maxChar) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  try {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::vector<std::string> cmd;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    cmd.push_back(childName);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    CodeDweller::Child child(cmd,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             bufSize,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             nominalAboveMin_ms,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             delta_ms);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Characters for testing.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    char randomLetter[] = "abcdefghijklmnop#rstuvwxyz";
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Calculate how often to output a "." to indicate progress.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    int maxChunks = maxChar / bufSize;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    int nChunks = 0;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    int nDots = 50;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    int outputEveryChunk = maxChunks / nDots / 2;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (outputEveryChunk == 0) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      outputEveryChunk = 2;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Send and receive random-sized chunks of data.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    size_t nChar = 0;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    while (nChar < maxChar) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      nChunks++;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      if (nChunks % outputEveryChunk == 0) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        std::cout << ".";
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        std::cout.flush();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      int chunkSize;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::string sentChunk, expectedChunk;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      // Get a random chunk size for sending.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      chunkSize = std::rand() % (bufSize * 4);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      if (chunkSize + nChar > maxChar) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        chunkSize = maxChar - nChar;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      // Generate output to child.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      sentChunk.clear();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      for (size_t i = 0; i < chunkSize; i++) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        sentChunk.push_back(randomLetter[std::rand() % 26]);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      // Generated expected input from child.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      expectedChunk = sentChunk;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      for (int i = 0; i < expectedChunk.size(); i++) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        expectedChunk[i] = std::toupper(expectedChunk[i]);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      // Send the data.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      while (!sentChunk.empty()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (child.writeAndShrink(sentChunk) == 0) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          std::this_thread::sleep_for(std::chrono::milliseconds(1));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        // Read and compare while sending data.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        std::string chunkFromChild;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        size_t chunkFromChildSize;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        (void) child.read(chunkFromChild);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        chunkFromChildSize = chunkFromChild.size();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (!chunkFromChild.empty()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          if (chunkFromChild == expectedChunk.substr(0, chunkFromChildSize)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            expectedChunk.erase(0, chunkFromChildSize);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          } else {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::cout << "Expected:  \""
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << expectedChunk.substr(0, chunkFromChildSize)
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << "\", received \"" << chunkFromChild << "\"."
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::cout << "Remaining chunk:  \"" << expectedChunk << "\""
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            child.write("q");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            RETURN_FALSE("  comparison failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      // Read and compare after all data was sent.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      while (!expectedChunk.empty()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        std::string chunkFromChild;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        size_t chunkFromChildSize;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        (void) child.read(chunkFromChild);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        chunkFromChildSize = chunkFromChild.size();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if (0 == chunkFromChildSize) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          std::this_thread::sleep_for(std::chrono::milliseconds(1));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        } else {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          if (chunkFromChild == expectedChunk.substr(0, chunkFromChildSize)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            expectedChunk.erase(0, chunkFromChildSize);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          } else {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::cout << "Expected:  \""
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << expectedChunk.substr(0, chunkFromChildSize)
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << "\", received \"" << chunkFromChild << "\"."
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::cout << "Remaining chunk:  \"" << expectedChunk << "\""
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                      << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            child.write("q");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            RETURN_FALSE("  comparison failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			          }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      nChar += chunkSize;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Send exit message.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.write("q")) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Verify exit.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.isDone()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::cout << "  Failure in testChildNonblockingReadWrite2:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					<< "Child program did not exit."  << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      return false;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::string errorDescription;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.errorOccurred(errorDescription)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::ostringstream temp;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      temp << "  Failure in: testChildNonBlockingReadWrite2:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			           << errorDescription;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE(temp.str());
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    child.close();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  } catch (std::exception &e) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    EXCEPTION_TERM("Non-blocking reader test");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return false;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  return true;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			////////////////////////////////////////////////////////////////////////////////
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// ChildStream Tests ///////////////////////////////////////////////////////////
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			////////////////////////////////////////////////////////////////////////////////
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -858,8 +1029,14 @@ bool testChildClose() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  try {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    CodeDweller::Child child(cmd);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.write("q")) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    child.close();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  } catch (std::exception &e) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    EXCEPTION_TERM("close() after child exits");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return false;
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -956,7 +1133,7 @@ bool testChildReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    child.open(childName);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Write, read, put back, and reread each character.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Write, read test.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    int nLine = 0;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    for (std::string &line : expectedChildOutput) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1017,6 +1194,7 @@ bool testChildReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      for (int i = 0; i < nTries; i++) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        nCharRead = child.read(temp, nCharToRead);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        readLine += temp;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        nCharToRead -= nCharRead;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1078,6 +1256,83 @@ bool testChildReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool testChildIsFinishedWriting() {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  std::vector<std::string> cmd;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  cmd.push_back(childName);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  size_t bufSize = 16;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  std::uint16_t
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    nominalAboveMin_ms = 100,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    delta_ms = 0;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  try {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    CodeDweller::Child child(cmd,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             bufSize,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             nominalAboveMin_ms,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                             delta_ms);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.isFinishedWriting()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  isFinishedWriting() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.write("0123456789")) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Busy wait until the data is written.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    while (!child.isFinishedWriting())
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      ;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(50));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Writer thread should be pausing now.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.write("0123456789")) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.isFinishedWriting()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  isFinishedWriting() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(20));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.isFinishedWriting()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  isFinishedWriting() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(80));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.isFinishedWriting()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  isFinishedWriting() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Send exit message.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.write("q")) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Verify exit.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.isDone()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::cout << "  Failure in testIsFinishedWriting:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                << "Child program did not exit."
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      return false;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  } catch (std::exception &e) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    EXCEPTION_TERM("write()/isFinishedWriting()");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return false;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  return true;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool testChildRead() {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  try {
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1156,7 +1411,7 @@ bool testChildRead() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool testChildNonBlockingReadWrite() {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool testChildNonBlockingReadWrite1() {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  try {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::vector<std::string> cmd;
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1236,7 +1491,9 @@ bool testChildNonBlockingReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(25));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.read(readBuf) != bufSize) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    size_t nRead = child.read(readBuf);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (nRead != bufSize) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1244,6 +1501,9 @@ bool testChildNonBlockingReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read() failure)");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    // Wait for reader thread to fill input buffer.
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(25));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.read(readBuf) != expectedLeftOver) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1261,7 +1521,7 @@ bool testChildNonBlockingReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!child.isDone()) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::cout << "  Failure in testNonblockingReader:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::cout << "  Failure in testChildNonblockingReadWrite1:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					<< "Child program did not exit."  << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      return false;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1271,7 +1531,7 @@ bool testChildNonBlockingReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (child.errorOccurred(errorDescription)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      std::ostringstream temp;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      temp << "  Failure in: testChildNonBlockingReadWrite:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      temp << "  Failure in: testChildNonBlockingReadWrite1:  "
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			           << errorDescription;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE(temp.str());
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1287,13 +1547,48 @@ bool testChildNonBlockingReadWrite() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool testChildNonBlockingReadWrite2() {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  size_t bufSize, maxChar;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  std::uint16_t
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    nominalAboveMin_ms = 0,
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    delta_ms = 0;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    bufSize = 16;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    maxChar = 1000 * 100;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout << "\n    many small buffers";
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout.flush();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!doReadWrite(bufSize, nominalAboveMin_ms, delta_ms, maxChar)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read()/write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    bufSize = 128 * 1024;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    maxChar = 1000 * 10;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout << "\n    one big buffer";
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout.flush();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!doReadWrite(bufSize, nominalAboveMin_ms, delta_ms, maxChar)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read()/write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    bufSize = 128 * 1024;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    maxChar = 1000 * 1000 * 10;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout << "\n    many big buffers";
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    std::cout.flush();
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if (!doReadWrite(bufSize, nominalAboveMin_ms, delta_ms, maxChar)) {
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			      RETURN_FALSE("  read()/write() failure");
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    }
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  return true;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			////////////////////////////////////////////////////////////////////////////////
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// End of tests ////////////////////////////////////////////////////////////////
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			////////////////////////////////////////////////////////////////////////////////
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			int main()
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			{
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#if 0
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  std::cout << "\nCodeDweller::ChildStream unit tests\n" << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildStreamIsDone);
 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -1307,17 +1602,17 @@ int main() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  std::cout << "\nCodeDweller::Child unit tests\n" << std::endl;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildIsDone);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  //RUN_TEST(testChildIsDone);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildIsRunning);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildResult);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildClose);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildOpen);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildIsFinishedWriting);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildRead);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#if 1
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildReadWrite);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildNonBlockingReadWrite);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildNonBlockingReadWrite1);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  RUN_TEST(testChildNonBlockingReadWrite2);
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  SUMMARY;
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			  return 0;
 |