diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocesslauncher_test.cpp
index 4d8f850d9291724fdf5385d73d17832e5a8cea4f..3935c64a94b84adf0e8618d4d23db2220631f1d4 100644
--- a/indra/llcommon/tests/llprocesslauncher_test.cpp
+++ b/indra/llcommon/tests/llprocesslauncher_test.cpp
@@ -18,14 +18,14 @@
 #include <vector>
 #include <list>
 // std headers
-#include <errno.h>
 // external library headers
 #include "llapr.h"
 #include "apr_thread_proc.h"
-#include "apr_file_io.h"
 #include <boost/foreach.hpp>
 // other Linden headers
 #include "../test/lltut.h"
+#include "../test/manageapr.h"
+#include "../test/namedtempfile.h"
 #include "stringize.h"
 
 #if defined(LL_WINDOWS)
@@ -36,30 +36,8 @@
 #include <sys/wait.h>
 #endif
 
-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;
-};
+// static instance of this manages APR init/cleanup
+static ManageAPR manager;
 
 #define ensure_equals_(left, right) \
         ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
@@ -74,11 +52,11 @@ namespace tut
     {
         void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
         {
-            ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr.strerror(rv)),
+            ensure_equals(STRINGIZE(call << " => " << rv << ": " << manager.strerror(rv)),
                           rv, expected);
         }
 
-        APR apr;
+        LLAPRPool pool;
     };
     typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
     typedef llprocesslauncher_group::object object;
@@ -186,19 +164,7 @@ namespace tut
         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[] =
+        NamedTempFile script("py",
             "import sys" EOL
             "import time" EOL
             EOL
@@ -208,10 +174,7 @@ namespace tut
             "time.sleep(2)" EOL
             "print >>sys.stderr, 'stderr after wait'" EOL
             "sys.stderr.flush()" EOL
-            ;
-        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
@@ -221,30 +184,33 @@ namespace tut
 
         // Run the child process.
         apr_procattr_t *procattr = NULL;
-        aprchk(apr_procattr_create(&procattr, apr.pool));
+        aprchk(apr_procattr_create(&procattr, pool.getAPRPool()));
         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(tempname);
+        // Have to have a named copy of this std::string so its c_str() value
+        // will persist.
+        std::string scriptname(script.getName());
+        argv.push_back(scriptname.c_str());
         argv.push_back(NULL);
 
         aprchk(apr_proc_create(&child, argv[0],
                                &argv[0],
                                NULL, // if we wanted to pass explicit environment
                                procattr,
-                               apr.pool));
+                               pool.getAPRPool()));
 
         // We do not want this child process to outlive our APR pool. On
         // destruction of the pool, forcibly kill the process. Tell APR to try
         // SIGTERM and wait 3 seconds. If that didn't work, use SIGKILL.
-        apr_pool_note_subprocess(apr.pool, &child, APR_KILL_AFTER_TIMEOUT);
+        apr_pool_note_subprocess(pool.getAPRPool(), &child, APR_KILL_AFTER_TIMEOUT);
 
         // arrange to call child_status_callback()
         WaitInfo wi(&child);
-        apr_proc_other_child_register(&child, child_status_callback, &wi, child.in, apr.pool);
+        apr_proc_other_child_register(&child, child_status_callback, &wi, child.in, pool.getAPRPool());
 
         // TODO:
         // Stuff child.in until it (would) block to verify EWOULDBLOCK/EAGAIN.
@@ -289,7 +255,7 @@ namespace tut
                 }
                 if (rv == EWOULDBLOCK || rv == EAGAIN)
                 {
-//                  std::cout << "(waiting; apr_file_gets(" << dfli->first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
+//                  std::cout << "(waiting; apr_file_gets(" << dfli->first << ") => " << rv << ": " << manager.strerror(rv) << ")\n";
                     ++history.back().tries;
                     continue;
                 }
@@ -334,14 +300,11 @@ namespace tut
             std::cout << "child_status_callback(APR_OC_REASON_DEATH) wasn't called" << std::endl;
             wi.rv = apr_proc_wait(wi.child, &wi.rc, &wi.why, APR_NOWAIT);
         }
-//      std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+//      std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
         aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE);
         ensure_equals_(wi.why, APR_PROC_EXIT);
         ensure_equals_(wi.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.
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 72322c3b727a46b9f24a9527badfa76657eb2229..4359e9afb9a6256fd081230425d6507f96d69c8b 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -43,38 +43,12 @@ typedef U32 uint32_t;
 #include "llprocesslauncher.h"
 #endif
 
-#include <sstream>
-
-/*==========================================================================*|
-// Whoops, seems Linden's Boost package and the viewer are built with
-// different settings of VC's /Zc:wchar_t switch! Using Boost.Filesystem
-// pathname operations produces Windows link errors:
-// unresolved external symbol "private: static class std::codecvt<unsigned short,
-// char,int> const * & __cdecl boost::filesystem3::path::wchar_t_codecvt_facet()"
-// unresolved external symbol "void __cdecl boost::filesystem3::path_traits::convert()"
-// See:
-// http://boost.2283326.n4.nabble.com/filesystem-v3-unicode-and-std-codecvt-linker-error-td3455549.html
-// which points to:
-// http://msdn.microsoft.com/en-us/library/dh8che7s%28v=VS.100%29.aspx
-
-// As we're not trying to preserve compatibility with old Boost.Filesystem
-// code, but rather writing brand-new code, use the newest available
-// Filesystem API.
-#define BOOST_FILESYSTEM_VERSION 3
-#include "boost/filesystem.hpp"
-#include "boost/filesystem/v3/fstream.hpp"
-|*==========================================================================*/
 #include "boost/range.hpp"
 #include "boost/foreach.hpp"
 #include "boost/function.hpp"
 #include "boost/lambda/lambda.hpp"
 #include "boost/lambda/bind.hpp"
 namespace lambda = boost::lambda;
-/*==========================================================================*|
-// Aaaarrgh, Linden's Boost package doesn't even include Boost.Iostreams!
-#include "boost/iostreams/stream.hpp"
-#include "boost/iostreams/device/file_descriptor.hpp"
-|*==========================================================================*/
 
 #include "../llsd.h"
 #include "../llsdserialize.h"
@@ -82,236 +56,17 @@ namespace lambda = boost::lambda;
 #include "../llformat.h"
 
 #include "../test/lltut.h"
+#include "../test/manageapr.h"
+#include "../test/namedtempfile.h"
 #include "stringize.h"
 
+static ManageAPR manager;
+
 std::vector<U8> string_to_vector(const std::string& str)
 {
 	return std::vector<U8>(str.begin(), str.end());
 }
 
-#if ! LL_WINDOWS
-// We want to call strerror_r(), but alarmingly, there are two different
-// variants. The one that returns int always populates the passed buffer
-// (except in case of error), whereas the other one always returns a valid
-// char* but might or might not populate the passed buffer. How do we know
-// which one we're getting? Define adapters for each and let the compiler
-// select the applicable adapter.
-
-// strerror_r() returns char*
-std::string message_from(int /*orig_errno*/, const char* /*buffer*/, const char* strerror_ret)
-{
-    return strerror_ret;
-}
-
-// strerror_r() returns int
-std::string message_from(int orig_errno, const char* buffer, int strerror_ret)
-{
-    if (strerror_ret == 0)
-    {
-        return buffer;
-    }
-    // Here strerror_r() has set errno. Since strerror_r() has already failed,
-    // seems like a poor bet to call it again to diagnose its own error...
-    int stre_errno = errno;
-    if (stre_errno == ERANGE)
-    {
-        return STRINGIZE("strerror_r() can't explain errno " << orig_errno
-                         << " (buffer too small)");
-    }
-    if (stre_errno == EINVAL)
-    {
-        return STRINGIZE("unknown errno " << orig_errno);
-    }
-    // Here we don't even understand the errno from strerror_r()!
-    return STRINGIZE("strerror_r() can't explain errno " << orig_errno
-                     << " (error " << stre_errno << ')');
-}
-#endif  // ! LL_WINDOWS
-
-// boost::filesystem::temp_directory_path() isn't yet in Boost 1.45! :-(
-std::string temp_directory_path()
-{
-#if LL_WINDOWS
-    char buffer[4096];
-    GetTempPathA(sizeof(buffer), buffer);
-    return buffer;
-
-#else  // LL_DARWIN, LL_LINUX
-    static const char* vars[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR" };
-    BOOST_FOREACH(const char* var, vars)
-    {
-        const char* found = getenv(var);
-        if (found)
-            return found;
-    }
-    return "/tmp";
-#endif // LL_DARWIN, LL_LINUX
-}
-
-// Windows presents a kinda sorta compatibility layer. Code to the yucky
-// Windows names because they're less likely than the Posix names to collide
-// with any other names in this source.
-#if LL_WINDOWS
-#define _remove   DeleteFileA
-#else  // ! LL_WINDOWS
-#define _open     open
-#define _write    write
-#define _close    close
-#define _remove   remove
-#endif  // ! LL_WINDOWS
-
-// Create a text file with specified content "somewhere in the
-// filesystem," cleaning up when it goes out of scope.
-class NamedTempFile
-{
-public:
-    // Function that accepts an ostream ref and (presumably) writes stuff to
-    // it, e.g.:
-    // (lambda::_1 << "the value is " << 17 << '\n')
-    typedef boost::function<void(std::ostream&)> Streamer;
-
-    NamedTempFile(const std::string& ext, const std::string& content):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, lambda::_1 << content);
-    }
-
-    // Disambiguate when passing string literal
-    NamedTempFile(const std::string& ext, const char* content):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, lambda::_1 << content);
-    }
-
-    NamedTempFile(const std::string& ext, const Streamer& func):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, func);
-    }
-
-    ~NamedTempFile()
-    {
-        _remove(mPath.c_str());
-    }
-
-    std::string getName() const { return mPath; }
-
-private:
-    void createFile(const std::string& ext, const Streamer& func)
-    {
-        // Silly maybe, but use 'ext' as the name prefix. Strip off a leading
-        // '.' if present.
-        int pfx_offset = ((! ext.empty()) && ext[0] == '.')? 1 : 0;
-
-#if ! LL_WINDOWS
-        // Make sure mPath ends with a directory separator, if it doesn't already.
-        if (mPath.empty() ||
-            ! (mPath[mPath.length() - 1] == '\\' || mPath[mPath.length() - 1] == '/'))
-        {
-            mPath.append("/");
-        }
-
-        // mkstemp() accepts and modifies a char* template string. Generate
-        // the template string, then copy to modifiable storage.
-        // mkstemp() requires its template string to end in six X's.
-        mPath += ext.substr(pfx_offset) + "XXXXXX";
-        // Copy to vector<char>
-        std::vector<char> pathtemplate(mPath.begin(), mPath.end());
-        // append a nul byte for classic-C semantics
-        pathtemplate.push_back('\0');
-        // std::vector promises that a pointer to the 0th element is the same
-        // as a pointer to a contiguous classic-C array
-        int fd(mkstemp(&pathtemplate[0]));
-        if (fd == -1)
-        {
-            // The documented errno values (http://linux.die.net/man/3/mkstemp)
-            // are used in a somewhat unusual way, so provide context-specific
-            // errors.
-            if (errno == EEXIST)
-            {
-                LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath
-                                         << "\") could not create unique file " << LL_ENDL;
-            }
-            if (errno == EINVAL)
-            {
-                LL_ERRS("NamedTempFile") << "bad mkstemp() file path template '"
-                                         << mPath << "'" << LL_ENDL;
-            }
-            // Shrug, something else
-            int mkst_errno = errno;
-            char buffer[256];
-            LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath << "\") failed: "
-                                     << message_from(mkst_errno, buffer,
-                                                     strerror_r(mkst_errno, buffer, sizeof(buffer)))
-                                     << LL_ENDL;
-        }
-        // mkstemp() seems to have worked! Capture the modified filename.
-        // Avoid the nul byte we appended.
-        mPath.assign(pathtemplate.begin(), (pathtemplate.end()-1));
-
-/*==========================================================================*|
-        // Define an ostream on the open fd. Tell it to close fd on destruction.
-        boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
-            out(fd, boost::iostreams::close_handle);
-|*==========================================================================*/
-
-        // Write desired content.
-        std::ostringstream out;
-        // Stream stuff to it.
-        func(out);
-
-        std::string data(out.str());
-        int written(_write(fd, data.c_str(), data.length()));
-        int closed(_close(fd));
-        llassert_always(written == data.length() && closed == 0);
-
-#else // LL_WINDOWS
-        // GetTempFileName() is documented to require a MAX_PATH buffer.
-        char tempname[MAX_PATH];
-        // Use 'ext' as filename prefix, but skip leading '.' if any.
-        // The 0 param is very important: requests iterating until we get a
-        // unique name.
-        if (0 == GetTempFileNameA(mPath.c_str(), ext.c_str() + pfx_offset, 0, tempname))
-        {
-            // I always have to look up this call...  :-P
-            LPSTR msgptr;
-            FormatMessageA(
-                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-                FORMAT_MESSAGE_FROM_SYSTEM |
-                FORMAT_MESSAGE_IGNORE_INSERTS,
-                NULL,
-                GetLastError(),
-                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                LPSTR(&msgptr),     // have to cast (char**) to (char*)
-                0, NULL );
-            LL_ERRS("NamedTempFile") << "GetTempFileName(\"" << mPath << "\", \""
-                                     << (ext.c_str() + pfx_offset) << "\") failed: "
-                                     << msgptr << LL_ENDL;
-            LocalFree(msgptr);
-        }
-        // GetTempFileName() appears to have worked! Capture the actual
-        // filename.
-        mPath = tempname;
-        // Open the file and stream content to it. Destructor will close.
-        std::ofstream out(tempname);
-        func(out);
-
-#endif  // LL_WINDOWS
-    }
-
-    void peep()
-    {
-        std::cout << "File '" << mPath << "' contains:\n";
-        std::ifstream reader(mPath.c_str());
-        std::string line;
-        while (std::getline(reader, line))
-            std::cout << line << '\n';
-        std::cout << "---\n";
-    }
-
-    std::string mPath;
-};
-
 namespace tut
 {
 	struct sd_xml_data
@@ -1783,7 +1538,7 @@ namespace tut
             const char* PYTHON(getenv("PYTHON"));
             ensure("Set $PYTHON to the Python interpreter", PYTHON);
 
-            NamedTempFile scriptfile(".py", script);
+            NamedTempFile scriptfile("py", script);
 
 #if LL_WINDOWS
             std::string q("\"");
@@ -1888,12 +1643,12 @@ namespace tut
             "    else:\n"
             "        assert False, 'Too many data items'\n";
 
-        // Create a something.llsd file containing 'data' serialized to
+        // Create an llsdXXXXXX file containing 'data' serialized to
         // notation. It's important to separate with newlines because Python's
         // llsd module doesn't support parsing from a file stream, only from a
         // string, so we have to know how much of the file to read into a
         // string.
-        NamedTempFile file(".llsd",
+        NamedTempFile file("llsd",
                            // NamedTempFile's boost::function constructor
                            // takes a callable. To this callable it passes the
                            // std::ostream with which it's writing the
@@ -1926,7 +1681,7 @@ namespace tut
         // Create an empty data file. This is just a placeholder for our
         // script to write into. Create it to establish a unique name that
         // we know.
-        NamedTempFile file(".llsd", "");
+        NamedTempFile file("llsd", "");
 
         python("write Python notation",
                lambda::_1 <<
diff --git a/indra/test/manageapr.h b/indra/test/manageapr.h
new file mode 100644
index 0000000000000000000000000000000000000000..0c1ca7b7be14162a42f23d9316c6af44749036c9
--- /dev/null
+++ b/indra/test/manageapr.h
@@ -0,0 +1,45 @@
+/**
+ * @file   manageapr.h
+ * @author Nat Goodspeed
+ * @date   2012-01-13
+ * @brief  ManageAPR class for simple test programs
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_MANAGEAPR_H)
+#define LL_MANAGEAPR_H
+
+#include "llapr.h"
+
+/**
+ * Declare a static instance of this class for dead-simple ll_init_apr() at
+ * program startup, ll_cleanup_apr() at termination. This is recommended for
+ * use only with simple test programs. Once you start introducing static
+ * instances of other classes that depend on APR already being initialized,
+ * the indeterminate static-constructor-order problem rears its ugly head.
+ */
+class ManageAPR
+{
+public:
+    ManageAPR()
+    {
+        ll_init_apr();
+    }
+
+    ~ManageAPR()
+    {
+        ll_cleanup_apr();
+    }
+
+    static std::string strerror(apr_status_t rv)
+    {
+        char errbuf[256];
+        apr_strerror(rv, errbuf, sizeof(errbuf));
+        return errbuf;
+    }
+};
+
+#endif /* ! defined(LL_MANAGEAPR_H) */
diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..9670d4db53f38c2b43f8eb188b6d39b324273e2d
--- /dev/null
+++ b/indra/test/namedtempfile.h
@@ -0,0 +1,113 @@
+/**
+ * @file   namedtempfile.h
+ * @author Nat Goodspeed
+ * @date   2012-01-13
+ * @brief  NamedTempFile class for tests that need disk files as fixtures.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_NAMEDTEMPFILE_H)
+#define LL_NAMEDTEMPFILE_H
+
+#include "llapr.h"
+#include "apr_file_io.h"
+#include <string>
+#include <boost/function.hpp>
+#include "boost/lambda/lambda.hpp"
+#include "boost/lambda/bind.hpp"
+#include <iostream>
+#include <sstream>
+
+/**
+ * Create a text file with specified content "somewhere in the
+ * filesystem," cleaning up when it goes out of scope.
+ */
+class NamedTempFile
+{
+public:
+    NamedTempFile(const std::string& pfx, const std::string& content, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, boost::lambda::_1 << content);
+    }
+
+    // Disambiguate when passing string literal
+    NamedTempFile(const std::string& pfx, const char* content, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, boost::lambda::_1 << content);
+    }
+
+    // Function that accepts an ostream ref and (presumably) writes stuff to
+    // it, e.g.:
+    // (boost::lambda::_1 << "the value is " << 17 << '\n')
+    typedef boost::function<void(std::ostream&)> Streamer;
+
+    NamedTempFile(const std::string& pfx, const Streamer& func, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, func);
+    }
+
+    ~NamedTempFile()
+    {
+        ll_apr_assert_status(apr_file_remove(mPath.c_str(), mPool));
+    }
+
+    std::string getName() const { return mPath; }
+
+private:
+    void createFile(const std::string& pfx, const Streamer& func)
+    {
+        // Create file in a temporary place.
+        const char* tempdir = NULL;
+        ll_apr_assert_status(apr_temp_dir_get(&tempdir, mPool));
+
+        // Construct a temp filename template in that directory.
+        char *tempname = NULL;
+        ll_apr_assert_status(apr_filepath_merge(&tempname,
+                                                tempdir,
+                                                (pfx + "XXXXXX").c_str(),
+                                                0,
+                                                mPool));
+
+        // Create a temp file from that template.
+        apr_file_t* fp = NULL;
+        ll_apr_assert_status(apr_file_mktemp(&fp,
+                                             tempname,
+                                             APR_CREATE | APR_WRITE | APR_EXCL,
+                                             mPool));
+        // apr_file_mktemp() alters tempname with the actual name. Not until
+        // now is it valid to capture as our mPath.
+        mPath = tempname;
+
+        // Write desired content.
+        std::ostringstream out;
+        // Stream stuff to it.
+        func(out);
+
+        std::string data(out.str());
+        apr_size_t writelen(data.length());
+        ll_apr_assert_status(apr_file_write(fp, data.c_str(), &writelen));
+        ll_apr_assert_status(apr_file_close(fp));
+        llassert_always(writelen == data.length());
+    }
+
+    void peep()
+    {
+        std::cout << "File '" << mPath << "' contains:\n";
+        std::ifstream reader(mPath.c_str());
+        std::string line;
+        while (std::getline(reader, line))
+            std::cout << line << '\n';
+        std::cout << "---\n";
+    }
+
+    std::string mPath;
+    apr_pool_t* mPool;
+};
+
+#endif /* ! defined(LL_NAMEDTEMPFILE_H) */