summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
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