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
parent
c0731c1c05
commit
74fbd31813
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue