|
|
@@ -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;
|