summaryrefslogtreecommitdiff
path: root/indra/llmessage/tests
diff options
context:
space:
mode:
authorAdam Moss <moss@lindenlab.com>2009-03-09 23:42:07 +0000
committerAdam Moss <moss@lindenlab.com>2009-03-09 23:42:07 +0000
commit7573288ab3ede23f97bff2f5caefcb622e7e9842 (patch)
tree1429e7d758e282e123d31a21246603d0b9814273 /indra/llmessage/tests
parent2d60d45aead0b49787fb8ad8b5a03614fe17c170 (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/tests')
-rw-r--r--indra/llmessage/tests/commtest.h60
-rw-r--r--indra/llmessage/tests/networkio.h93
-rw-r--r--indra/llmessage/tests/test_llsdmessage_peer.py130
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:]))