diff options
| author | Adam Moss <moss@lindenlab.com> | 2009-03-09 23:42:07 +0000 | 
|---|---|---|
| committer | Adam Moss <moss@lindenlab.com> | 2009-03-09 23:42:07 +0000 | 
| commit | 7573288ab3ede23f97bff2f5caefcb622e7e9842 (patch) | |
| tree | 1429e7d758e282e123d31a21246603d0b9814273 /indra/llmessage | |
| parent | 2d60d45aead0b49787fb8ad8b5a03614fe17c170 (diff) | |
svn merge -r113780:113785
svn+ssh://svn.lindenlab.com/svn/linden/branches/moss/gst3-t113732
QAR-1333 linux gstreamer compatibility improvements and ADD_BUILD_TEST
improvements - combo merge
Diffstat (limited to 'indra/llmessage')
| -rw-r--r-- | indra/llmessage/tests/commtest.h | 60 | ||||
| -rw-r--r-- | indra/llmessage/tests/networkio.h | 93 | ||||
| -rw-r--r-- | indra/llmessage/tests/test_llsdmessage_peer.py | 130 | 
3 files changed, 283 insertions, 0 deletions
| diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h new file mode 100644 index 0000000000..7360230451 --- /dev/null +++ b/indra/llmessage/tests/commtest.h @@ -0,0 +1,60 @@ +/** + * @file   commtest.h + * @author Nat Goodspeed + * @date   2009-01-09 + * @brief   + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_COMMTEST_H) +#define LL_COMMTEST_H + +#include "networkio.h" +#include "llevents.h" +#include "llsd.h" +#include "llhost.h" +#include "stringize.h" +#include <string> + +/** + * This struct is shared by a couple of standalone comm tests (ADD_COMM_BUILD_TEST). + */ +struct commtest_data +{ +    NetworkIO& netio; +    LLEventPumps& pumps; +    LLEventStream replyPump, errorPump; +    LLSD result; +    bool success; +    LLHost host; +    std::string server; + +    commtest_data(): +        netio(NetworkIO::instance()), +        pumps(LLEventPumps::instance()), +        replyPump("reply"), +        errorPump("error"), +        success(false), +        host("127.0.0.1", 8000), +        server(STRINGIZE("http://" << host.getString() << "/")) +    { +        replyPump.listen("self", boost::bind(&commtest_data::outcome, this, _1, true)); +        errorPump.listen("self", boost::bind(&commtest_data::outcome, this, _1, false)); +    } + +    bool outcome(const LLSD& _result, bool _success) +    { +//      std::cout << "commtest_data::outcome(" << _result << ", " << _success << ")\n"; +        result = _result; +        success = _success; +        // Break the wait loop in NetworkIO::pump(), otherwise devs get +        // irritated at making the big monolithic test executable take longer +        pumps.obtain("done").post(success); +        return false; +    } +}; + +#endif /* ! defined(LL_COMMTEST_H) */ diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h new file mode 100644 index 0000000000..11c5cc07fc --- /dev/null +++ b/indra/llmessage/tests/networkio.h @@ -0,0 +1,93 @@ +/** + * @file   networkio.h + * @author Nat Goodspeed + * @date   2009-01-09 + * @brief   + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_NETWORKIO_H) +#define LL_NETWORKIO_H + +#include "llmemory.h"               // LLSingleton +#include "llapr.h" +#include "llares.h" +#include "llpumpio.h" +#include "llhttpclient.h" + +/***************************************************************************** +*   NetworkIO +*****************************************************************************/ +// Doing this initialization in a class constructor makes sense. But we don't +// want to redo it for each different test. Nor do we want to do it at static- +// init time. Use the lazy, on-demand initialization we get from LLSingleton. +class NetworkIO: public LLSingleton<NetworkIO> +{ +public: +    NetworkIO(): +        mServicePump(NULL), +        mDone(false) +    { +        ll_init_apr(); +        if (! gAPRPoolp) +        { +            throw std::runtime_error("Can't initialize APR"); +        } + +        // Create IO Pump to use for HTTP Requests. +        mServicePump = new LLPumpIO(gAPRPoolp); +        LLHTTPClient::setPump(*mServicePump); +        if (ll_init_ares() == NULL || !gAres->isInitialized()) +        { +            throw std::runtime_error("Can't start DNS resolver"); +        } + +        // You can interrupt pump() without waiting the full timeout duration +        // by posting an event to the LLEventPump named "done". +        LLEventPumps::instance().obtain("done").listen("self", +                                                       boost::bind(&NetworkIO::done, this, _1)); +    } + +    bool pump(F32 timeout=10) +    { +        // Reset the done flag so we don't pop out prematurely +        mDone = false; +        // Evidently the IO structures underlying LLHTTPClient need to be +        // "pumped". Do some stuff normally performed in the viewer's main +        // loop. +        LLTimer timer; +        while (timer.getElapsedTimeF32() < timeout) +        { +            if (mDone) +            { +//              std::cout << "NetworkIO::pump(" << timeout << "): breaking loop after " +//                        << timer.getElapsedTimeF32() << " seconds\n"; +                return true; +            } +            pumpOnce(); +        } +        return false; +    } + +    void pumpOnce() +    { +        gAres->process(); +        mServicePump->pump(); +        mServicePump->callback(); +    } + +    bool done(const LLSD&) +    { +        mDone = true; +        return false; +    } + +private: +    LLPumpIO* mServicePump; +    bool mDone; +}; + +#endif /* ! defined(LL_NETWORKIO_H) */ diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py new file mode 100644 index 0000000000..e62f20912b --- /dev/null +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -0,0 +1,130 @@ +#!/usr/bin/python +"""\ +@file   test_llsdmessage_peer.py +@author Nat Goodspeed +@date   2008-10-09 +@brief  This script asynchronously runs the executable (with args) specified on +        the command line, returning its result code. While that executable is +        running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewergpl$ +Copyright (c) 2008, Linden Research, Inc. +$/LicenseInfo$ +""" + +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 + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): +    """This subclass of BaseHTTPRequestHandler is to receive and echo +    LLSD-flavored messages sent by the C++ LLHTTPClient. +    """ +    def read(self): +        # The following logic is adapted from the library module +        # SimpleXMLRPCServer.py. +        # Get arguments by reading body of request. +        # We read this in chunks to avoid straining +        # socket.read(); around the 10 or 15Mb mark, some platforms +        # begin to have problems (bug #792570). +        try: +            size_remaining = int(self.headers["content-length"]) +        except (KeyError, ValueError): +            return "" +        max_chunk_size = 10*1024*1024 +        L = [] +        while size_remaining: +            chunk_size = min(size_remaining, max_chunk_size) +            chunk = self.rfile.read(chunk_size) +            L.append(chunk) +            size_remaining -= len(chunk) +        return ''.join(L) +        # end of swiped read() logic + +    def read_xml(self): +        # This approach reads the entire POST data into memory first +        return llsd.parse(self.read()) +##         # This approach attempts to stream in the LLSD XML from self.rfile, +##         # assuming that the underlying XML parser reads its input file +##         # incrementally. Unfortunately I haven't been able to make it work. +##         tree = xml_parse(self.rfile) +##         debug("Finished raw parse\n") +##         debug("parsed XML tree %s\n" % tree) +##         debug("parsed root node %s\n" % tree.getroot()) +##         debug("root node tag %s\n" % tree.getroot().tag) +##         return llsd.to_python(tree.getroot()) + +    def do_GET(self): +        # Of course, don't attempt to read data. +        self.answer(dict(reply="success", status=500, +                         reason="Your GET operation requested failure")) + +    def do_POST(self): +        # Read the provided POST data. +        self.answer(self.read_xml()) + +    def answer(self, data): +        if "fail" not in self.path: +            response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) +            self.send_response(200) +            self.send_header("Content-type", "application/llsd+xml") +            self.send_header("Content-Length", str(len(response))) +            self.end_headers() +            self.wfile.write(response) +        else:                           # fail requested +            status = data.get("status", 500) +            reason = data.get("reason", +                               self.responses.get(status, +                                                  ("fail requested", +                                                   "Your request specified failure status %s " +                                                   "without providing a reason" % status))[1]) +            self.send_error(status, reason) + +    def log_request(self, code, size=None): +        # For present purposes, we don't want the request splattered onto +        # stderr, as it would upset devs watching the test run +        pass + +    def log_error(self, format, *args): +        # Suppress error output as well +        pass + +class TestHTTPServer(Thread): +    def run(self): +        httpd = HTTPServer(('127.0.0.1', 8000), TestHTTPRequestHandler) +        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:])) | 
