summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2011-12-21 17:00:43 -0500
committerNat Goodspeed <nat@lindenlab.com>2011-12-21 17:00:43 -0500
commit2fd0bc8648e71aa2f141fde4b3a6a0165f7ef4d6 (patch)
tree614e27545421032fab7bef75e4e3c1d4f923a72f /indra/llcommon
parent7832d8eccb00d32b6122e5851238e962f65af1e8 (diff)
Change llprocesslauncher_test.cpp eyeballing to program verification.
That is, where before we just flung stuff to stdout with the expectation that a human user would verify, replace with assertions in the test code itself. Quiet previous noise on stdout. Introduce a temp script file that produces output on both stdout and stderr, with sleep() calls so we predictably have to wait for it. Track and then verify the history of our interaction with the child process, noting especially EWOULDBLOCK attempts.
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/tests/llprocesslauncher_test.cpp97
1 files changed, 85 insertions, 12 deletions
diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocesslauncher_test.cpp
index 4d14e1be53..ca06b3164e 100644
--- a/indra/llcommon/tests/llprocesslauncher_test.cpp
+++ b/indra/llcommon/tests/llprocesslauncher_test.cpp
@@ -17,6 +17,7 @@
// STL headers
#include <vector>
// std headers
+#include <errno.h>
// external library headers
#include "llapr.h"
#include "apr_thread_proc.h"
@@ -71,11 +72,53 @@ namespace tut
typedef llprocesslauncher_group::object object;
llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher");
+ struct Item
+ {
+ Item(): tries(0) {}
+ unsigned tries;
+ std::string which;
+ std::string what;
+ };
+
template<> template<>
void object::test<1>()
{
set_test_name("raw APR nonblocking I/O");
+ // Create a script file in a temporary place.
+ const char* tempdir = NULL;
+ aprchk(apr_temp_dir_get(&tempdir, apr.pool));
+
+ // Construct a temp filename template in that directory.
+ char *tempname = NULL;
+ aprchk(apr_filepath_merge(&tempname, tempdir, "testXXXXXX", 0, apr.pool));
+
+ // Create a temp file from that template.
+ apr_file_t* fp = NULL;
+ aprchk(apr_file_mktemp(&fp, tempname, APR_CREATE | APR_WRITE | APR_EXCL, apr.pool));
+
+ // Write it.
+ const char script[] =
+ "import sys\n"
+ "import time\n"
+ "\n"
+ "time.sleep(2)\n"
+ "print >>sys.stdout, \"stdout after wait\"\n"
+ "sys.stdout.flush()\n"
+ "time.sleep(2)\n"
+ "print >>sys.stderr, \"stderr after wait\"\n"
+ "sys.stderr.flush()\n"
+ ;
+ apr_size_t len(sizeof(script)-1);
+ aprchk(apr_file_write(fp, script, &len));
+ aprchk(apr_file_close(fp));
+
+ // Arrange to track the history of our interaction with child: what we
+ // fetched, which pipe it came from, how many tries it took before we
+ // got it.
+ std::vector<Item> history;
+ history.push_back(Item());
+
apr_procattr_t *procattr = NULL;
aprchk(apr_procattr_create(&procattr, apr.pool));
aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
@@ -84,8 +127,7 @@ namespace tut
std::vector<const char*> argv;
apr_proc_t child;
argv.push_back("python");
- argv.push_back("-c");
- argv.push_back("raise RuntimeError('Hello from Python!')");
+ argv.push_back(tempname);
argv.push_back(NULL);
aprchk(apr_proc_create(&child, argv[0],
@@ -110,24 +152,35 @@ namespace tut
apr_status_t rv = apr_file_gets(buf, sizeof(buf), iterfiles[i].second);
if (APR_STATUS_IS_EOF(rv))
{
- std::cout << "(EOF on " << iterfiles[i].first << ")\n";
+// std::cout << "(EOF on " << iterfiles[i].first << ")\n";
+ history.back().which = iterfiles[i].first;
+ history.back().what = "*eof*";
+ history.push_back(Item());
outfiles.erase(outfiles.begin() + i);
continue;
}
- if (rv != APR_SUCCESS)
+ if (rv == EWOULDBLOCK)
{
- std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
+// std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
+ ++history.back().tries;
continue;
}
+ ensure_equals(rv, APR_SUCCESS);
// Is it even possible to get APR_SUCCESS but read 0 bytes?
// Hope not, but defend against that anyway.
if (buf[0])
{
- std::cout << iterfiles[i].first << ": " << buf;
- // Just for pretty output... if we only read a partial
- // line, terminate it.
- if (buf[strlen(buf) - 1] != '\n')
- std::cout << "...\n";
+// std::cout << iterfiles[i].first << ": " << buf;
+ history.back().which = iterfiles[i].first;
+ history.back().what.append(buf);
+ if (buf[strlen(buf) - 1] == '\n')
+ history.push_back(Item());
+ else
+ {
+ // Just for pretty output... if we only read a partial
+ // line, terminate it.
+// std::cout << "...\n";
+ }
}
}
sleep(1);
@@ -141,9 +194,29 @@ namespace tut
apr_status_t rv;
while (! APR_STATUS_IS_CHILD_DONE(rv = apr_proc_wait(&child, &rc, &why, APR_NOWAIT)))
{
- std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n';
+// std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n';
sleep(0.5);
}
- std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+// std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+ ensure_equals(rv, APR_CHILD_DONE);
+ ensure_equals(why, APR_PROC_EXIT);
+ ensure_equals(rc, 0);
+
+ // Remove temp script file
+ aprchk(apr_file_remove(tempname, apr.pool));
+
+ // Beyond merely executing all the above successfully, verify that we
+ // obtained expected output -- and that we duly got control while
+ // waiting, proving the non-blocking nature of these pipes.
+ ensure("blocking I/O on child pipe (0)", history[0].tries);
+ ensure_equals(history[0].which, "out");
+ ensure_equals(history[0].what, "stdout after wait\n");
+ ensure("blocking I/O on child pipe (1)", history[1].tries);
+ ensure_equals(history[1].which, "out");
+ ensure_equals(history[1].what, "*eof*");
+ ensure_equals(history[2].which, "err");
+ ensure_equals(history[2].what, "stderr after wait\n");
+ ensure_equals(history[3].which, "err");
+ ensure_equals(history[3].what, "*eof*");
}
} // namespace tut