summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2011-12-21 10:06:26 -0500
committerNat Goodspeed <nat@lindenlab.com>2011-12-21 10:06:26 -0500
commit14c09c3ce597e47f5c41bb45246e89a1f31d89b0 (patch)
tree7273b95f5c7aaa4d787134795cd052321d21d28b /indra
parentb022ebf13c9a227f87a112419e237894a1231c8c (diff)
Add unit-test module for LLProcessLauncher.
As always with llcommon, this is expressed as an "integration test" to sidestep a circular dependency: the llcommon build depends on its unit tests, but all our unit tests depend on llcommon. Initial test code is more for human verification than automated verification: does APR's child-process management in fact support nonblocking operations?
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/CMakeLists.txt1
-rw-r--r--indra/llcommon/tests/llprocesslauncher_test.cpp144
2 files changed, 145 insertions, 0 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 0a3eaec5c5..c8e1827584 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -326,6 +326,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llprocesslauncher "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)
diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocesslauncher_test.cpp
new file mode 100644
index 0000000000..3b5602f620
--- /dev/null
+++ b/indra/llcommon/tests/llprocesslauncher_test.cpp
@@ -0,0 +1,144 @@
+/**
+ * @file llprocesslauncher_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-12-19
+ * @brief Test for llprocesslauncher.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llprocesslauncher.h"
+// STL headers
+#include <vector>
+// std headers
+// external library headers
+#include "llapr.h"
+#include "apr_thread_proc.h"
+#include "apr_file_io.h"
+// other Linden headers
+#include "../test/lltut.h"
+
+class APR
+{
+public:
+ APR():
+ pool(NULL)
+ {
+ apr_initialize();
+ apr_pool_create(&pool, NULL);
+ }
+
+ ~APR()
+ {
+ apr_terminate();
+ }
+
+ std::string strerror(apr_status_t rv)
+ {
+ char errbuf[256];
+ apr_strerror(rv, errbuf, sizeof(errbuf));
+ return errbuf;
+ }
+
+ apr_pool_t *pool;
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llprocesslauncher_data
+ {
+ void aprchk(apr_status_t rv)
+ {
+ ensure_equals(apr.strerror(rv), rv, APR_SUCCESS);
+ }
+
+ APR apr;
+ };
+ typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
+ typedef llprocesslauncher_group::object object;
+ llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("raw APR nonblocking I/O");
+
+ 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));
+ aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+
+ 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(NULL);
+
+ aprchk(apr_proc_create(&child, argv[0],
+ static_cast<const char* const*>(&argv[0]),
+ NULL, // if we wanted to pass explicit environment
+ procattr,
+ apr.pool));
+
+ typedef std::pair<std::string, apr_file_t*> DescFile;
+ typedef std::vector<DescFile> DescFileVec;
+ DescFileVec outfiles;
+ outfiles.push_back(DescFile("out", child.out));
+ outfiles.push_back(DescFile("err", child.err));
+
+ while (! outfiles.empty())
+ {
+ DescFileVec iterfiles(outfiles);
+ for (size_t i = 0; i < iterfiles.size(); ++i)
+ {
+ char buf[4096];
+
+ 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";
+ outfiles.erase(outfiles.begin() + i);
+ continue;
+ }
+ if (rv != APR_SUCCESS)
+ {
+ std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
+ continue;
+ }
+ // 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";
+ }
+ }
+ sleep(1);
+ }
+ apr_file_close(child.in);
+ apr_file_close(child.out);
+ apr_file_close(child.err);
+
+ int rc = 0;
+ apr_exit_why_e why;
+ 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';
+ sleep(0.5);
+ }
+ std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+ }
+} // namespace tut