diff options
| author | brad kittenbrink <brad@lindenlab.com> | 2009-10-07 17:51:28 -0700 | 
|---|---|---|
| committer | brad kittenbrink <brad@lindenlab.com> | 2009-10-07 17:51:28 -0700 | 
| commit | 50247533f99f374be71728cc5ca00f0444189a9f (patch) | |
| tree | c1505ec3ec9c054447fb98968c9299e502857000 /indra/llmessage | |
| parent | 146e084af3344ada737340115c5fa063c0aab6b3 (diff) | |
| parent | d885db79f94ee3d6a2b4ed7febe7beb69d0c7d51 (diff) | |
Merged latest viewer/login-api with latest viewer/viewer-20
Diffstat (limited to 'indra/llmessage')
| -rw-r--r-- | indra/llmessage/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/llmessage/llares.cpp | 8 | ||||
| -rw-r--r-- | indra/llmessage/llares.h | 12 | ||||
| -rw-r--r-- | indra/llmessage/llareslistener.cpp | 75 | ||||
| -rw-r--r-- | indra/llmessage/llareslistener.h | 37 | ||||
| -rw-r--r-- | indra/llmessage/llcachename.h | 6 | ||||
| -rw-r--r-- | indra/llmessage/llinstantmessage.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/llpartdata.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/llregionpresenceverifier.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/llsdmessage.cpp | 7 | ||||
| -rw-r--r-- | indra/llmessage/llsdmessage.h | 3 | ||||
| -rwxr-xr-x | indra/llmessage/llsdmessagebuilder.cpp | 1 | ||||
| -rwxr-xr-x | indra/llmessage/llsdmessagereader.cpp | 1 | ||||
| -rw-r--r-- | indra/llmessage/tests/llareslistener_test.cpp | 200 | ||||
| -rw-r--r-- | indra/llmessage/tests/llregionpresenceverifier_test.cpp | 2 | ||||
| -rw-r--r-- | indra/llmessage/tests/test_llsdmessage_peer.py | 30 | ||||
| -rw-r--r-- | indra/llmessage/tests/testrunner.py | 53 | 
17 files changed, 409 insertions, 35 deletions
| diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 453286b83d..3eceda7901 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -23,6 +23,7 @@ include_directories(  set(llmessage_SOURCE_FILES      llares.cpp +    llareslistener.cpp      llassetstorage.cpp      llblowfishcipher.cpp      llbuffer.cpp @@ -105,6 +106,7 @@ set(llmessage_HEADER_FILES      CMakeLists.txt      llares.h +    llareslistener.h      llassetstorage.h      llblowfishcipher.h      llbuffer.h @@ -224,6 +226,7 @@ IF (NOT LINUX AND VIEWER)        lltemplatemessagedispatcher.cpp        llregionpresenceverifier.cpp        ) +  #    set(TEST_DEBUG on)      set(test_libs        ${LLMESSAGE_LIBRARIES} diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index fe37fe8142..acbf51d75c 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -33,6 +33,7 @@   */  #include "linden_common.h" +#include "llares.h"  #include <ares_dns.h>  #include <ares_version.h> @@ -42,9 +43,10 @@  #include "apr_poll.h"  #include "llapr.h" -#include "llares.h" +#include "llareslistener.h"  #if defined(LL_WINDOWS) +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally  # define ns_c_in 1  # define NS_HFIXEDSZ     12      /* #/bytes of fixed data in header */  # define NS_QFIXEDSZ     4       /* #/bytes of fixed data in query */ @@ -102,7 +104,9 @@ void LLAres::QueryResponder::queryError(int code)  }  LLAres::LLAres() : -chan_(NULL), mInitSuccess(false) +    chan_(NULL), +    mInitSuccess(false), +    mListener(new LLAresListener("LLAres", this))  {  	if (ares_init(&chan_) != ARES_SUCCESS)  	{ diff --git a/indra/llmessage/llares.h b/indra/llmessage/llares.h index c709a08499..78febcd560 100644 --- a/indra/llmessage/llares.h +++ b/indra/llmessage/llares.h @@ -36,7 +36,13 @@  #define LL_LLARES_H  #ifdef LL_WINDOWS +// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h +// we need to include them first to work around it, but the headers issue warnings +# pragma warning(push) +# pragma warning(disable:4996) +# include <winsock2.h>  # include <ws2tcpip.h> +# pragma warning(pop)  #endif  #ifdef LL_STANDALONE @@ -49,7 +55,10 @@  #include "llrefcount.h"  #include "lluri.h" +#include <boost/shared_ptr.hpp> +  class LLQueryResponder; +class LLAresListener;  /**   * @brief Supported DNS RR types. @@ -444,6 +453,9 @@ public:  protected:  	ares_channel chan_;  	bool mInitSuccess; +    // boost::scoped_ptr would actually fit the requirement better, but it +    // can't handle incomplete types as boost::shared_ptr can. +    boost::shared_ptr<LLAresListener> mListener;  };  /** diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp new file mode 100644 index 0000000000..a8beb8cbde --- /dev/null +++ b/indra/llmessage/llareslistener.cpp @@ -0,0 +1,75 @@ +/** + * @file   llareslistener.cpp + * @author Nat Goodspeed + * @date   2009-03-18 + * @brief  Implementation for llareslistener. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llareslistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llares.h" +#include "llerror.h" +#include "llevents.h" +#include "llsdutil.h" + +LLAresListener::LLAresListener(const std::string& pumpname, LLAres* llares): +    LLDispatchListener(pumpname, "op"), +    mAres(llares) +{ +    // add() every method we want to be able to invoke via this event API. +    // Optional third parameter validates expected LLSD request structure. +    add("rewriteURI", &LLAresListener::rewriteURI, +        LLSD().insert("uri", LLSD()).insert("reply", LLSD())); +} + +/// This UriRewriteResponder subclass packages returned URIs as an LLSD +/// array to send back to the requester. +class UriRewriteResponder: public LLAres::UriRewriteResponder +{ +public: +    /** +     * Specify the request, containing the event pump name on which to send +     * the reply. +     */ +    UriRewriteResponder(const LLSD& request): +        mReqID(request), +        mPumpName(request["reply"]) +    {} + +    /// Called by base class with results. This is called in both the +    /// success and error cases. On error, the calling logic passes the +    /// original URI. +    virtual void rewriteResult(const std::vector<std::string>& uris) +    { +        LLSD result; +        for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end()); +             ui != uend; ++ui) +        { +            result.append(*ui); +        } +        // This call knows enough to avoid trying to insert a map key into an +        // LLSD array. It's there so that if, for any reason, we ever decide +        // to change the response from array to map, it will Just Start Working. +        mReqID.stamp(result); +        LLEventPumps::instance().obtain(mPumpName).post(result); +    } + +private: +    LLReqID mReqID; +    const std::string mPumpName; +}; + +void LLAresListener::rewriteURI(const LLSD& data) +{ +    mAres->rewriteURI(data["uri"], new UriRewriteResponder(data)); +} diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h new file mode 100644 index 0000000000..bf093b3d3d --- /dev/null +++ b/indra/llmessage/llareslistener.h @@ -0,0 +1,37 @@ +/** + * @file   llareslistener.h + * @author Nat Goodspeed + * @date   2009-03-18 + * @brief  LLEventPump API for LLAres. This header doesn't actually define the + *         API; the API is defined by the pump name on which this class + *         listens, and by the expected content of LLSD it receives. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLARESLISTENER_H) +#define LL_LLARESLISTENER_H + +#include "lleventdispatcher.h" + +class LLAres; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAres request events. +class LLAresListener: public LLDispatchListener +{ +public: +    /// Specify the pump name on which to listen, and bind the LLAres instance +    /// to use (e.g. gAres) +    LLAresListener(const std::string& pumpname, LLAres* llares); + +private: +    /// command["op"] == "rewriteURI"  +    void rewriteURI(const LLSD& data); + +    LLAres* mAres; +}; + +#endif /* ! defined(LL_LLARESLISTENER_H) */ diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index c044b3d80d..8641437d86 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -42,9 +42,9 @@ class LLUUID;  typedef boost::signals2::signal<void (const LLUUID& id, -							const std::string& first_name, -							const std::string& last_name, -							BOOL is_group)> LLCacheNameSignal; +                                      const std::string& first_name, +                                      const std::string& last_name, +                                      BOOL is_group)> LLCacheNameSignal;  typedef LLCacheNameSignal::slot_type LLCacheNameCallback;  // Old callback with user data for compatability diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp index 7c63625004..3da41939fa 100644 --- a/indra/llmessage/llinstantmessage.cpp +++ b/indra/llmessage/llinstantmessage.cpp @@ -40,7 +40,7 @@  #include "lluuid.h"  #include "llsd.h"  #include "llsdserialize.h" -#include "llsdutil.h" +#include "llsdutil_math.h"  #include "llpointer.h"  #include "message.h" diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index 485bc6aa44..9376cde7b5 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -39,6 +39,8 @@  #include "v4coloru.h"  #include "llsdutil.h" +#include "llsdutil_math.h" +  const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18 diff --git a/indra/llmessage/llregionpresenceverifier.cpp b/indra/llmessage/llregionpresenceverifier.cpp index e02c735ce7..b1868e6a00 100644 --- a/indra/llmessage/llregionpresenceverifier.cpp +++ b/indra/llmessage/llregionpresenceverifier.cpp @@ -30,6 +30,8 @@   * $/LicenseInfo$   */ +#include "linden_common.h" +  #include "llregionpresenceverifier.h"  #include "llhttpclientinterface.h"  #include <sstream> diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index 9967a6197f..2cb742e261 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -68,6 +68,7 @@ bool LLSDMessage::httpListener(const LLSD& request)      }      LLHTTPClient::post(url, payload,                         new LLSDMessage::EventResponder(LLEventPumps::instance(), +                                                       request,                                                         url, "POST", reply, error),                         LLSD(),      // headers                         timeout); @@ -81,7 +82,9 @@ void LLSDMessage::EventResponder::result(const LLSD& data)      // to the pump whose name is "".      if (! mReplyPump.empty())      { -        mPumps.obtain(mReplyPump).post(data); +        LLSD response(data); +        mReqID.stamp(response); +        mPumps.obtain(mReplyPump).post(response);      }      else                            // default success handling      { @@ -98,7 +101,7 @@ void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string      // explicit pump name.      if (! mErrorPump.empty())      { -        LLSD info; +        LLSD info(mReqID.makeResponse());          info["target"]  = mTarget;          info["message"] = mMessage;          info["status"]  = LLSD::Integer(status); diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h index 65503756a8..6ee00fd41d 100644 --- a/indra/llmessage/llsdmessage.h +++ b/indra/llmessage/llsdmessage.h @@ -121,9 +121,11 @@ private:           * (e.g. "POST") as @a message.           */          EventResponder(LLEventPumps& pumps, +                       const LLSD& request,                         const std::string& target, const std::string& message,                         const std::string& replyPump, const std::string& errorPump):              mPumps(pumps), +            mReqID(request),              mTarget(target),              mMessage(message),              mReplyPump(replyPump), @@ -135,6 +137,7 @@ private:      private:          LLEventPumps& mPumps; +        LLReqID mReqID;          const std::string mTarget, mMessage, mReplyPump, mErrorPump;      }; diff --git a/indra/llmessage/llsdmessagebuilder.cpp b/indra/llmessage/llsdmessagebuilder.cpp index 21937f022f..6e41b03895 100755 --- a/indra/llmessage/llsdmessagebuilder.cpp +++ b/indra/llmessage/llsdmessagebuilder.cpp @@ -37,6 +37,7 @@  #include "llmessagetemplate.h"  #include "llquaternion.h"  #include "llsdutil.h" +#include "llsdutil_math.h"  #include "llsdserialize.h"  #include "u64.h"  #include "v3dmath.h" diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index e699ec9e28..845a12d23b 100755 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -38,6 +38,7 @@  #include "llsdmessagebuilder.h"  #include "llsdutil.h" +#include "llsdutil_math.h"  #include "v3math.h"  #include "v4math.h"  #include "v3dmath.h" diff --git a/indra/llmessage/tests/llareslistener_test.cpp b/indra/llmessage/tests/llareslistener_test.cpp new file mode 100644 index 0000000000..ac4886ccf4 --- /dev/null +++ b/indra/llmessage/tests/llareslistener_test.cpp @@ -0,0 +1,200 @@ +/** + * @file   llareslistener_test.cpp + * @author Mark Palange + * @date   2009-02-26 + * @brief  Tests of llareslistener.h. + *  + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +// Precompiled header +#include "linden_common.h" +// associated header +#include "../llareslistener.h" +// STL headers +#include <iostream> +// std headers +// external library headers +#include <boost/bind.hpp> + +// other Linden headers +#include "llsd.h" +#include "llares.h" +#include "../test/lltut.h" +#include "llevents.h" +#include "tests/wrapllerrs.h" + +/***************************************************************************** +*   Dummy stuff +*****************************************************************************/ +LLAres::LLAres(): +    // Simulate this much of the real LLAres constructor: we need an +    // LLAresListener instance. +    mListener(new LLAresListener("LLAres", this)) +{} +LLAres::~LLAres() {} +void LLAres::rewriteURI(const std::string &uri, +					LLAres::UriRewriteResponder *resp) +{ +	// This is the only LLAres method I chose to implement. +	// The effect is that LLAres returns immediately with +	// a result that is equal to the input uri. +	std::vector<std::string> result; +	result.push_back(uri); +	resp->rewriteResult(result); +} + +LLAres::QueryResponder::~QueryResponder() {} +void LLAres::QueryResponder::queryError(int) {} +void LLAres::QueryResponder::queryResult(char const*, size_t) {} +LLQueryResponder::LLQueryResponder() {} +void LLQueryResponder::queryResult(char const*, size_t) {} +void LLQueryResponder::querySuccess() {} +void LLAres::UriRewriteResponder::queryError(int) {} +void LLAres::UriRewriteResponder::querySuccess() {} +void LLAres::UriRewriteResponder::rewriteResult(const std::vector<std::string>& uris) {} + +/***************************************************************************** +*   TUT +*****************************************************************************/ +namespace tut +{ +    struct data +    { +        LLAres dummyAres; +    }; +    typedef test_group<data> llareslistener_group; +    typedef llareslistener_group::object object; +    llareslistener_group llareslistenergrp("llareslistener"); + +	struct ResponseCallback +	{ +		std::vector<std::string> mURIs; +		bool operator()(const LLSD& response) +		{ +            mURIs.clear(); +            for (LLSD::array_const_iterator ri(response.beginArray()), rend(response.endArray()); +                 ri != rend; ++ri) +            { +                mURIs.push_back(*ri); +            } +            return false; +		} +	}; + +    template<> template<> +    void object::test<1>() +    { +        set_test_name("test event"); +		// Tests the success and failure cases, since they both use  +		// the same code paths in the LLAres responder. +		ResponseCallback response; +        std::string pumpname("trigger"); +        // Since we're asking LLEventPumps to obtain() the pump by the desired +        // name, it will persist beyond the current scope, so ensure we +        // disconnect from it when 'response' goes away. +        LLTempBoundListener temp( +            LLEventPumps::instance().obtain(pumpname).listen("rewriteURIresponse", +                                                             boost::bind(&ResponseCallback::operator(), &response, _1))); +        // Now build an LLSD request that will direct its response events to +        // that pump. +		const std::string testURI("login.bar.com"); +        LLSD request; +        request["op"] = "rewriteURI"; +        request["uri"] = testURI; +        request["reply"] = pumpname; +        LLEventPumps::instance().obtain("LLAres").post(request); +		ensure_equals(response.mURIs.size(), 1); +		ensure_equals(response.mURIs.front(), testURI);  +	} + +    template<> template<> +    void object::test<2>() +    { +        set_test_name("bad op"); +        WrapLL_ERRS capture; +        LLSD request; +        request["op"] = "foo"; +        std::string threw; +        try +        { +            LLEventPumps::instance().obtain("LLAres").post(request); +        } +        catch (const WrapLL_ERRS::FatalException& e) +        { +            threw = e.what(); +        } +        ensure_contains("LLAresListener bad op", threw, "bad"); +    } + +    template<> template<> +    void object::test<3>() +    { +        set_test_name("bad rewriteURI request"); +        WrapLL_ERRS capture; +        LLSD request; +        request["op"] = "rewriteURI"; +        std::string threw; +        try +        { +            LLEventPumps::instance().obtain("LLAres").post(request); +        } +        catch (const WrapLL_ERRS::FatalException& e) +        { +            threw = e.what(); +        } +        ensure_contains("LLAresListener bad req", threw, "missing"); +        ensure_contains("LLAresListener bad req", threw, "reply"); +        ensure_contains("LLAresListener bad req", threw, "uri"); +    } + +    template<> template<> +    void object::test<4>() +    { +        set_test_name("bad rewriteURI request"); +        WrapLL_ERRS capture; +        LLSD request; +        request["op"] = "rewriteURI"; +        request["reply"] = "nonexistent"; +        std::string threw; +        try +        { +            LLEventPumps::instance().obtain("LLAres").post(request); +        } +        catch (const WrapLL_ERRS::FatalException& e) +        { +            threw = e.what(); +        } +        ensure_contains("LLAresListener bad req", threw, "missing"); +        ensure_contains("LLAresListener bad req", threw, "uri"); +        ensure_does_not_contain("LLAresListener bad req", threw, "reply"); +    } + +    template<> template<> +    void object::test<5>() +    { +        set_test_name("bad rewriteURI request"); +        WrapLL_ERRS capture; +        LLSD request; +        request["op"] = "rewriteURI"; +        request["uri"] = "foo.bar.com"; +        std::string threw; +        try +        { +            LLEventPumps::instance().obtain("LLAres").post(request); +        } +        catch (const WrapLL_ERRS::FatalException& e) +        { +            threw = e.what(); +        } +        ensure_contains("LLAresListener bad req", threw, "missing"); +        ensure_contains("LLAresListener bad req", threw, "reply"); +        ensure_does_not_contain("LLAresListener bad req", threw, "uri"); +    } +} diff --git a/indra/llmessage/tests/llregionpresenceverifier_test.cpp b/indra/llmessage/tests/llregionpresenceverifier_test.cpp index b7602ef15c..c86126406e 100644 --- a/indra/llmessage/tests/llregionpresenceverifier_test.cpp +++ b/indra/llmessage/tests/llregionpresenceverifier_test.cpp @@ -29,6 +29,8 @@   * $/LicenseInfo$   */ +#include "linden_common.h" +  #include "../test/lltut.h"  #include "llregionpresenceverifier.h"  #include "llcurl_stub.cpp" diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index e62f20912b..86d5761b1b 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -16,16 +16,12 @@ import os  import sys  from threading import Thread  from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +  mydir = os.path.dirname(__file__)       # expected to be .../indra/llmessage/tests/  sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))  from indra.util.fastest_elementtree import parse as xml_parse  from indra.base import llsd - -def debug(*args): -    sys.stdout.writelines(args) -    sys.stdout.flush() -# comment out the line below to enable debug output -debug = lambda *args: None +from testrunner import run, debug  class TestHTTPRequestHandler(BaseHTTPRequestHandler):      """This subclass of BaseHTTPRequestHandler is to receive and echo @@ -106,25 +102,5 @@ class TestHTTPServer(Thread):          debug("Starting HTTP server...\n")          httpd.serve_forever() -def main(*args): -    # Start HTTP server thread. Note that this and all other comm server -    # threads should be daemon threads: we'll let them run "forever," -    # confident that the whole process will terminate when the main thread -    # terminates, which will be when the test executable child process -    # terminates. -    httpThread = TestHTTPServer(name="httpd") -    httpThread.setDaemon(True) -    httpThread.start() -    # choice of os.spawnv(): -    # - [v vs. l] pass a list of args vs. individual arguments, -    # - [no p] don't use the PATH because we specifically want to invoke the -    #   executable passed as our first arg, -    # - [no e] child should inherit this process's environment. -    debug("Running %s...\n" % (" ".join(args))) -    sys.stdout.flush() -    rc = os.spawnv(os.P_WAIT, args[0], args) -    debug("%s returned %s\n" % (args[0], rc)) -    return rc -  if __name__ == "__main__": -    sys.exit(main(*sys.argv[1:])) +    sys.exit(run(server=TestHTTPServer(name="httpd"), *sys.argv[1:])) diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py new file mode 100644 index 0000000000..3b9c3a7a19 --- /dev/null +++ b/indra/llmessage/tests/testrunner.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +"""\ +@file   testrunner.py +@author Nat Goodspeed +@date   2009-03-20 +@brief  Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&license=viewergpl$ +Copyright (c) 2009, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys + +def debug(*args): +    sys.stdout.writelines(args) +    sys.stdout.flush() +# comment out the line below to enable debug output +debug = lambda *args: None + +def run(*args, **kwds): +    """All positional arguments collectively form a command line, executed as +    a synchronous child process. +    In addition, pass server=new_thread_instance as an explicit keyword (to +    differentiate it from an additional command-line argument). +    new_thread_instance should be an instantiated but not yet started Thread +    subclass instance, e.g.: +    run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) +    """ +    # If there's no server= keyword arg, don't start a server thread: simply +    # run a child process. +    try: +        thread = kwds.pop("server") +    except KeyError: +        pass +    else: +        # Start server thread. Note that this and all other comm server +        # threads should be daemon threads: we'll let them run "forever," +        # confident that the whole process will terminate when the main thread +        # terminates, which will be when the child process terminates. +        thread.setDaemon(True) +        thread.start() +    # choice of os.spawnv(): +    # - [v vs. l] pass a list of args vs. individual arguments, +    # - [no p] don't use the PATH because we specifically want to invoke the +    #   executable passed as our first arg, +    # - [no e] child should inherit this process's environment. +    debug("Running %s...\n" % (" ".join(args))) +    sys.stdout.flush() +    rc = os.spawnv(os.P_WAIT, args[0], args) +    debug("%s returned %s\n" % (args[0], rc)) +    return rc | 
