Add first couple of LLProcessLauncher tests.

Run INTEGRATION_TEST_llprocesslauncher using setpython.py so we can find the
Python interpreter of interest.
Introduce python() function to run a Python script specified using
NamedTempFile conventions.
Introduce a convention by which we can read output from a Python script using
only the limited pre-January-2012 LLProcessLauncher API. Introduce
python_out() function to leverage that convention.
Exercise a couple of LLProcessLauncher methods using all the above.
meow-7.2.2
Nat Goodspeed 2012-01-17 19:03:17 -05:00
parent c0731c1c05
commit 74fbd31813
2 changed files with 148 additions and 1 deletions

View File

@ -328,7 +328,8 @@ 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}")
LL_ADD_INTEGRATION_TEST(llprocesslauncher "" "${test_libs}"
"${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.

View File

@ -18,10 +18,14 @@
#include <vector>
#include <list>
// std headers
#include <fstream>
// external library headers
#include "llapr.h"
#include "apr_thread_proc.h"
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "../test/manageapr.h"
@ -36,6 +40,8 @@
#include <sys/wait.h>
#endif
namespace lambda = boost::lambda;
// static instance of this manages APR init/cleanup
static ManageAPR manager;
@ -56,6 +62,116 @@ namespace tut
rv, expected);
}
/**
* Run a Python script using LLProcessLauncher.
* @param desc Arbitrary description for error messages
* @param script Python script, any form acceptable to NamedTempFile,
* typically either a std::string or an expression of the form
* (lambda::_1 << "script content with " << variable_data)
* @param arg If specified, will be passed to script as its
* sys.argv[1]
* @param tweak "Do something" to LLProcessLauncher object before
* calling its launch() method. This program is to test
* LLProcessLauncher, but many such tests are "just like" this
* python() function but for one or two extra method calls before
* launch(). This avoids us having to clone & edit this function for
* such tests.
*/
template <typename CONTENT>
void python(const std::string& desc, const CONTENT& script, const std::string& arg="",
const boost::function<void (LLProcessLauncher&)> tweak=lambda::_1)
{
const char* PYTHON(getenv("PYTHON"));
ensure("Set $PYTHON to the Python interpreter", PYTHON);
NamedTempFile scriptfile("py", script);
LLProcessLauncher py;
py.setExecutable(PYTHON);
py.addArgument(scriptfile.getName());
if (! arg.empty())
{
py.addArgument(arg);
}
tweak(py);
ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0);
// One of the irritating things about LLProcessLauncher is that
// there's no API to wait for the child to terminate -- but given
// its use in our graphics-intensive interactive viewer, it's
// understandable.
while (py.isRunning())
{
sleep(1);
}
}
/**
* Run a Python script using LLProcessLauncher, expecting that it will
* write to the file passed as its sys.argv[1]. Retrieve that output.
*
* Until January 2012, LLProcessLauncher provided distressingly few
* mechanisms for a child process to communicate back to its caller --
* not even its return code. We've introduced a convention by which we
* create an empty temp file, pass the name of that file to our child
* as sys.argv[1] and expect the script to write its output to that
* file. This function implements the C++ (parent process) side of
* that convention.
*
* @param desc as for python()
* @param script as for python()
* @param tweak as for python()
*/
template <typename CONTENT>
std::string python_out(const std::string& desc, const CONTENT& script,
const boost::function<void (LLProcessLauncher&)> tweak=lambda::_1)
{
NamedTempFile out("out", ""); // placeholder
// pass name of this temporary file to the script
python(desc, script, out.getName(), tweak);
// assuming the script wrote a line to that file, read it
std::string output;
{
std::ifstream inf(out.getName().c_str());
ensure(STRINGIZE("No output from " << desc << " script"),
std::getline(inf, output));
std::string more;
while (std::getline(inf, more))
{
output += '\n' + more;
}
} // important to close inf BEFORE removing NamedTempFile
return output;
}
class NamedTempDir
{
public:
// Use python() function to create a temp directory: I've found
// nothing in either Boost.Filesystem or APR quite like Python's
// tempfile.mkdtemp().
// Special extra bonus: on Mac, mkdtemp() reports a pathname
// starting with /var/folders/something, whereas that's really a
// symlink to /private/var/folders/something. Have to use
// realpath() to compare properly.
NamedTempDir(llprocesslauncher_data* ths):
mThis(ths),
mPath(ths->python_out("mkdtemp()",
"import os.path, sys, tempfile\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write(os.path.realpath(tempfile.mkdtemp()))\n"))
{}
~NamedTempDir()
{
mThis->aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
}
std::string getName() const { return mPath; }
private:
llprocesslauncher_data* mThis;
std::string mPath;
};
LLAPRPool pool;
};
typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
@ -349,4 +465,34 @@ namespace tut
throw;
}
}
template<> template<>
void object::test<2>()
{
set_test_name("set/getExecutable()");
LLProcessLauncher child;
child.setExecutable("nonsense string");
ensure_equals("setExecutable() 0", child.getExecutable(), "nonsense string");
child.setExecutable("python");
ensure_equals("setExecutable() 1", child.getExecutable(), "python");
}
template<> template<>
void object::test<3>()
{
set_test_name("setWorkingDirectory()");
// We want to test setWorkingDirectory(). But what directory is
// guaranteed to exist on every machine, under every OS? Have to
// create one.
NamedTempDir tempdir(this);
std::string cwd(python_out("getcwd()",
"import os, sys\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write(os.getcwd())\n",
// Before LLProcessLauncher::launch(), call setWorkingDirectory()
lambda::bind(&LLProcessLauncher::setWorkingDirectory,
lambda::_1,
tempdir.getName())));
ensure_equals("os.getcwd()", cwd, tempdir.getName());
}
} // namespace tut