diff options
Diffstat (limited to 'indra/llmessage')
| -rwxr-xr-x | indra/llmessage/tests/test_llsdmessage_peer.py | 27 | ||||
| -rwxr-xr-x | indra/llmessage/tests/testrunner.py | 87 | 
2 files changed, 80 insertions, 34 deletions
| diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index bac18fa374..9cd2959ea1 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -31,7 +31,6 @@ $/LicenseInfo$  import os  import sys -from threading import Thread  from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler  from llbase.fastest_elementtree import parse as xml_parse @@ -155,17 +154,23 @@ class Server(HTTPServer):      allow_reuse_address = False  if __name__ == "__main__": -    # Instantiate a Server(TestHTTPRequestHandler) on the first free port -    # in the specified port range. Doing this inline is better than in a -    # daemon thread: if it blows up here, we'll get a traceback. If it blew up -    # in some other thread, the traceback would get eaten and we'd run the -    # subject test program anyway. -    httpd, port = freeport(xrange(8000, 8020), -                           lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) +    # function to make a server with specified port +    make_server = lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler) + +    if not sys.platform.startswith("win"): +        # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the +        # runtime. +        httpd = make_server(0) +    else: +        # "Then there's Windows" +        # Instantiate a Server(TestHTTPRequestHandler) on the first free port +        # in the specified port range. +        httpd, port = freeport(xrange(8000, 8020), make_server) +      # Pass the selected port number to the subject test program via the      # environment. We don't want to impose requirements on the test program's      # command-line parsing -- and anyway, for C++ integration tests, that's      # performed in TUT code rather than our own. -    os.environ["PORT"] = str(port) -    debug("$PORT = %s", port) -    sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) +    os.environ["PORT"] = str(httpd.server_port) +    debug("$PORT = %s", httpd.server_port) +    sys.exit(run(server_inst=httpd, *sys.argv[1:])) diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 5b9beb359b..c25945067e 100755 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -27,13 +27,12 @@ Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA  $/LicenseInfo$  """ -from __future__ import with_statement -  import os  import sys  import re  import errno  import socket +import subprocess  VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet  # Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if @@ -47,6 +46,9 @@ if VERBOSE:  else:      debug = lambda *args: None +class Error(Exception): +    pass +  def freeport(portlist, expr):      """      Find a free server port to use. Specifically, evaluate 'expr' (a @@ -141,34 +143,73 @@ def freeport(portlist, expr):          raise  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. +    Run a specified command as a synchronous child process, optionally +    launching a server Thread during the run. + +    All positional arguments collectively form a command line. The first +    positional argument names the program file to execute. + +    Returns the termination code of the child process. + +    In addition, you may pass keyword-only arguments: + +    use_path=True: allow a simple filename as command and search PATH for that +    filename. (This argument is retained for backwards compatibility but is +    now the default behavior.) + +    server_inst: an instance of a subclass of SocketServer.BaseServer. + +    When you pass server_inst, run() calls its handle_request() method in a +    loop until the child process terminates. +    """ +    # server= keyword arg is discontinued      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. +        raise Error("Obsolete call to testrunner.run(): pass server_inst=, not server=") +      debug("Running %s...", " ".join(args)) -    rc = os.spawnv(os.P_WAIT, args[0], args) + +    try: +        server_inst = kwds.pop("server_inst") +    except KeyError: +        # Without server_inst, this is very simple: just run child process. +        rc = subprocess.call(args) +    else: +        # We're being asked to run a local server while the child process +        # runs. We used to launch a daemon thread calling +        # server_inst.serve_forever(), then eventually call sys.exit() with +        # the daemon thread still running -- but in recent versions of Python +        # 2, even when you call sys.exit(0), apparently killing the thread +        # causes the Python runtime to force the process termination code +        # nonzero. So now we avoid the extra thread altogether. + +        # SocketServer.BaseServer.handle_request() honors a 'timeout' +        # attribute, if it's set to something other than None. +        # We pick 0.5 seconds because that's the default poll timeout for +        # BaseServer.serve_forever(), which is what we used to use. +        server_inst.timeout = 0.5 + +        child = subprocess.Popen(args) +        while child.poll() is None: +            # Setting server_inst.timeout is what keeps this handle_request() +            # call from blocking "forever." Interestingly, looping over +            # handle_request() with a timeout is very like the implementation +            # of serve_forever(). We just check a different flag to break out. +            # It might be interesting if handle_request() returned an +            # indication of whether it in fact handled a request or timed out. +            # Oddly, it doesn't. We could discover that by overriding +            # handle_timeout(), whose default implementation does nothing -- +            # but in fact we really don't care. All that matters is that we +            # regularly poll both the child process and the server socket. +            server_inst.handle_request() +        # We don't bother to capture the rc returned by child.poll() because +        # poll() is already defined to capture that in its returncode attr. +        rc = child.returncode +      debug("%s returned %s", args[0], rc)      return rc | 
