diff options
Diffstat (limited to 'indra/llcommon')
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocesslauncher_test.cpp | 146 | 
2 files changed, 148 insertions, 1 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 334f78cbff..2c376bb016 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -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. diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocesslauncher_test.cpp index 3935c64a94..aebd280c2e 100644 --- a/indra/llcommon/tests/llprocesslauncher_test.cpp +++ b/indra/llcommon/tests/llprocesslauncher_test.cpp @@ -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 | 
