From 8948c4f001b77f1a5755716a353f2295bf246322 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Dec 2016 14:22:42 -0500 Subject: DRTVWR-418: Skip the llcorehttp test that breaks test machinery. Loath though I am to skip testing, this consistent failure is not a failure in the software being tested (llcorehttp) but rather in the dummy server with which we're testing it. --- indra/llcorehttp/tests/test_httprequest.hpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 463e55dd7e..a51b1fd32d 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1509,6 +1509,10 @@ void HttpRequestTestObjectType::test<14>() ensure("Request executed in reasonable time", count < limit); ensure("One handler invocation for request", mHandlerCalls == 1); +#if LL_WINDOWS + skip("This test causes our dummy server test_llcorehttp_peer.py to fail"); +#endif + // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); handle = req->requestStopThread(handlerp); -- cgit v1.2.3 From 4c89e6dea0d539c9177bff7bc45e8d411efe6e5c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Dec 2016 15:38:31 -0500 Subject: DRTVWR-418: Skip the whole of the failing llcorehttp test function. --- indra/llcorehttp/tests/test_httprequest.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index a51b1fd32d..0aad3d1835 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1455,6 +1455,10 @@ void HttpRequestTestObjectType::test<14>() set_test_name("HttpRequest GET timeout"); +#if LL_WINDOWS + skip("This test causes our dummy server test_llcorehttp_peer.py to fail"); +#endif + // Handler can be stack-allocated *if* there are no dangling // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. @@ -1509,10 +1513,6 @@ void HttpRequestTestObjectType::test<14>() ensure("Request executed in reasonable time", count < limit); ensure("One handler invocation for request", mHandlerCalls == 1); -#if LL_WINDOWS - skip("This test causes our dummy server test_llcorehttp_peer.py to fail"); -#endif - // Okay, request a shutdown of the servicing thread mStatus = HttpStatus(); handle = req->requestStopThread(handlerp); -- cgit v1.2.3 From e47b178fb9d40655faa837ca32f72a78753f63fb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Dec 2016 16:09:26 -0500 Subject: Try even harder to ignore errors in llcorehttp's dummy server. --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 6c5f37d407..a4783c42f2 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -280,10 +280,16 @@ class Server(ThreadingMixIn, HTTPServer): # default behavior which *shouldn't* cause the program to return # a failure status. def handle_error(self, request, client_address): - print '-'*40 - print 'Ignoring exception during processing of request from', - print client_address - print '-'*40 + print >>sys.stderr, '-'*40 + print >>sys.stderr, 'Ignoring exception during processing of request from', client_address + print >>sys.stderr, '-'*40 + + def shutdown_request(self, *args, **kwds): + try: + # just forward to base-class method, but wrap in try/except + HTTPServer.shutdown_request(*args, **kwds) + except Exception as err: + print >>sys.stderr, "Once more ignoring: %s" % err if __name__ == "__main__": do_valgrind = False -- cgit v1.2.3 From 43c9a7d706d3cdf72f861f89e9968342513d55b8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Dec 2016 16:19:32 -0500 Subject: Fix minor error in forwarding shutdown_request() call. --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index a4783c42f2..59acfc39c4 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -287,7 +287,7 @@ class Server(ThreadingMixIn, HTTPServer): def shutdown_request(self, *args, **kwds): try: # just forward to base-class method, but wrap in try/except - HTTPServer.shutdown_request(*args, **kwds) + HTTPServer.shutdown_request(self, *args, **kwds) except Exception as err: print >>sys.stderr, "Once more ignoring: %s" % err -- cgit v1.2.3 From e1b0317c04124b4fc72f14dee1c2125cf970b0e0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Dec 2016 19:44:57 -0500 Subject: DRTVWR-418: Remove duplicate testrunner.py --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 22 +- indra/llcorehttp/tests/testrunner.py | 265 ------------------------- indra/llmessage/tests/testrunner.py | 5 +- 3 files changed, 14 insertions(+), 278 deletions(-) delete mode 100755 indra/llcorehttp/tests/testrunner.py (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 59acfc39c4..caa204b519 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -40,10 +40,14 @@ try: except ImportError: from StringIO import StringIO from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler -from SocketServer import ThreadingMixIn from llbase.fastest_elementtree import parse as xml_parse from llbase import llsd + +# we're in llcorehttp/tests ; testrunner.py is found in llmessage/tests +sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, + "llmessage", "tests")) + from testrunner import freeport, run, debug, VERBOSE class TestHTTPRequestHandler(BaseHTTPRequestHandler): @@ -269,7 +273,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): # Suppress error output as well pass -class Server(ThreadingMixIn, HTTPServer): +class Server(HTTPServer): # This pernicious flag is on by default in HTTPServer. But proper # operation of freeport() absolutely depends on it being off. allow_reuse_address = False @@ -280,16 +284,10 @@ class Server(ThreadingMixIn, HTTPServer): # default behavior which *shouldn't* cause the program to return # a failure status. def handle_error(self, request, client_address): - print >>sys.stderr, '-'*40 - print >>sys.stderr, 'Ignoring exception during processing of request from', client_address - print >>sys.stderr, '-'*40 - - def shutdown_request(self, *args, **kwds): - try: - # just forward to base-class method, but wrap in try/except - HTTPServer.shutdown_request(self, *args, **kwds) - except Exception as err: - print >>sys.stderr, "Once more ignoring: %s" % err + print '-'*40 + print 'Ignoring exception during processing of request from', + print client_address + print '-'*40 if __name__ == "__main__": do_valgrind = False diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py deleted file mode 100755 index 9a2de71142..0000000000 --- a/indra/llcorehttp/tests/testrunner.py +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/env 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=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2010, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -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 - -VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet -# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if -# that construct actually turns on verbosity... -VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) - -if VERBOSE: - def debug(fmt, *args): - print fmt % args - sys.stdout.flush() -else: - debug = lambda *args: None - -def freeport(portlist, expr): - """ - Find a free server port to use. Specifically, evaluate 'expr' (a - callable(port)) until it stops raising EADDRINUSE exception. - - Pass: - - portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the - range, freeport() lets the socket.error exception propagate. If you want - unbounded, you could pass itertools.count(baseport), though of course in - practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain - the range much more sharply: if we're iterating an absurd number of times, - probably something else is wrong. - - expr: a callable accepting a port number, specifically one of the items - from portlist. If calling that callable raises socket.error with - EADDRINUSE, freeport() retrieves the next item from portlist and retries. - - Returns: (expr(port), port) - - port: the value from portlist for which expr(port) succeeded - - Raises: - - Any exception raised by expr(port) other than EADDRINUSE. - - socket.error if, for every item from portlist, expr(port) raises - socket.error. The exception you see is the one from the last item in - portlist. - - StopIteration if portlist is completely empty. - - Example: - - class Server(HTTPServer): - # If you use BaseHTTPServer.HTTPServer, turning off this flag is - # essential for proper operation of freeport()! - allow_reuse_address = False - # ... - server, port = freeport(xrange(8000, 8010), - lambda port: Server(("localhost", port), - MyRequestHandler)) - # pass 'port' to client code - # call server.serve_forever() - """ - try: - # If portlist is completely empty, let StopIteration propagate: that's an - # error because we can't return meaningful values. We have no 'port', - # therefore no 'expr(port)'. - portiter = iter(portlist) - port = portiter.next() - - while True: - try: - # If this value of port works, return as promised. - value = expr(port) - - except socket.error, err: - # Anything other than 'Address already in use', propagate - if err.args[0] != errno.EADDRINUSE: - raise - - # Here we want the next port from portiter. But on StopIteration, - # we want to raise the original exception rather than - # StopIteration. So save the original exc_info(). - type, value, tb = sys.exc_info() - try: - try: - port = portiter.next() - except StopIteration: - raise type, value, tb - finally: - # Clean up local traceback, see docs for sys.exc_info() - del tb - - else: - debug("freeport() returning %s on port %s", value, port) - return value, port - - # Recap of the control flow above: - # If expr(port) doesn't raise, return as promised. - # If expr(port) raises anything but EADDRINUSE, propagate that - # exception. - # If portiter.next() raises StopIteration -- that is, if the port - # value we just passed to expr(port) was the last available -- reraise - # the EADDRINUSE exception. - # If we've actually arrived at this point, portiter.next() delivered a - # new port value. Loop back to pass that to expr(port). - - except Exception, err: - debug("*** freeport() raising %s: %s", err.__class__.__name__, err) - 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. - 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...", " ".join(args)) - if kwds.get("use_path", False): - rc = os.spawnvp(os.P_WAIT, args[0], args) - else: - rc = os.spawnv(os.P_WAIT, args[0], args) - debug("%s returned %s", args[0], rc) - return rc - -# **************************************************************************** -# test code -- manual at this point, see SWAT-564 -# **************************************************************************** -def test_freeport(): - # ------------------------------- Helpers -------------------------------- - from contextlib import contextmanager - # helper Context Manager for expecting an exception - # with exc(SomeError): - # raise SomeError() - # raises AssertionError otherwise. - @contextmanager - def exc(exception_class, *args): - try: - yield - except exception_class, err: - for i, expected_arg in enumerate(args): - assert expected_arg == err.args[i], \ - "Raised %s, but args[%s] is %r instead of %r" % \ - (err.__class__.__name__, i, err.args[i], expected_arg) - print "Caught expected exception %s(%s)" % \ - (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) - else: - assert False, "Failed to raise " + exception_class.__class__.__name__ - - # helper to raise specified exception - def raiser(exception): - raise exception - - # the usual - def assert_equals(a, b): - assert a == b, "%r != %r" % (a, b) - - # ------------------------ Sanity check the above ------------------------ - class SomeError(Exception): pass - # Without extra args, accept any err.args value - with exc(SomeError): - raiser(SomeError("abc")) - # With extra args, accept only the specified value - with exc(SomeError, "abc"): - raiser(SomeError("abc")) - with exc(AssertionError): - with exc(SomeError, "abc"): - raiser(SomeError("def")) - with exc(AssertionError): - with exc(socket.error, errno.EADDRINUSE): - raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) - - # ----------- freeport() without engaging socket functionality ----------- - # If portlist is empty, freeport() raises StopIteration. - with exc(StopIteration): - freeport([], None) - - assert_equals(freeport([17], str), ("17", 17)) - - # This is the magic exception that should prompt us to retry - inuse = socket.error(errno.EADDRINUSE, 'Address already in use') - # Get the iterator to our ports list so we can check later if we've used all - ports = iter(xrange(5)) - with exc(socket.error, errno.EADDRINUSE): - freeport(ports, lambda port: raiser(inuse)) - # did we entirely exhaust 'ports'? - with exc(StopIteration): - ports.next() - - ports = iter(xrange(2)) - # Any exception but EADDRINUSE should quit immediately - with exc(SomeError): - freeport(ports, lambda port: raiser(SomeError())) - assert_equals(ports.next(), 1) - - # ----------- freeport() with platform-dependent socket stuff ------------ - # This is what we should've had unit tests to begin with (see CHOP-661). - def newbind(port): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('127.0.0.1', port)) - return sock - - bound0, port0 = freeport(xrange(7777, 7780), newbind) - assert_equals(port0, 7777) - bound1, port1 = freeport(xrange(7777, 7780), newbind) - assert_equals(port1, 7778) - bound2, port2 = freeport(xrange(7777, 7780), newbind) - assert_equals(port2, 7779) - with exc(socket.error, errno.EADDRINUSE): - bound3, port3 = freeport(xrange(7777, 7780), newbind) - -if __name__ == "__main__": - test_freeport() diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 5b9beb359b..9a2de71142 100755 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -168,7 +168,10 @@ def run(*args, **kwds): # executable passed as our first arg, # - [no e] child should inherit this process's environment. debug("Running %s...", " ".join(args)) - rc = os.spawnv(os.P_WAIT, args[0], args) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) debug("%s returned %s", args[0], rc) return rc -- cgit v1.2.3 From a4ba22fecc8db468377ab14f5652e4176f0488b7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 09:30:49 -0500 Subject: DRTVWR-418: Revamp testrunner to shutdown server Thread at end. Instead of having testrunner.run()'s caller pass a Thread object on which to run the caller's server instance's serve_forever() method, just pass the server instance. testrunner.run() now constructs the Thread. This API change allows run() to also call shutdown() on the server instance when done, and then join() the Thread. The hope is that this will avoid the Python runtime forcing the process termination code to 1 due to forcibly killing the daemon thread still running serve_forever(). While at it, eliminate calls to testrunner.freeport() -- just make the runtime pick a suitable port instead. --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 17 ++--- indra/llmessage/tests/test_llsdmessage_peer.py | 16 ++--- indra/llmessage/tests/testrunner.py | 90 ++++++++++++++++++-------- indra/newview/tests/test_llxmlrpc_peer.py | 27 ++++---- 4 files changed, 91 insertions(+), 59 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index caa204b519..b900ad73ff 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -48,7 +48,7 @@ from llbase import llsd sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "llmessage", "tests")) -from testrunner import freeport, run, debug, VERBOSE +from testrunner import run, debug, VERBOSE class TestHTTPRequestHandler(BaseHTTPRequestHandler): """This subclass of BaseHTTPRequestHandler is to receive and echo @@ -297,22 +297,17 @@ if __name__ == "__main__": if option == "-V" or option == "--valgrind": do_valgrind = True - # 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)) + # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the + # runtime. + httpd = Server(('127.0.0.1', 0), TestHTTPRequestHandler) # 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["LL_TEST_PORT"] = str(port) + os.environ["LL_TEST_PORT"] = str(httpd.server_port) debug("$LL_TEST_PORT = %s", port) if do_valgrind: args = ["valgrind", "--log-file=./valgrind.log"] + args path_search = True - sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) - + sys.exit(run(server_inst=httpd, use_path=path_search, *args)) diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index bac18fa374..a0d5d1b354 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -36,7 +36,7 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from llbase.fastest_elementtree import parse as xml_parse from llbase import llsd -from testrunner import freeport, run, debug, VERBOSE +from testrunner import run, debug, VERBOSE import time _storage=None @@ -155,17 +155,13 @@ 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)) + # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the + # runtime. + httpd = Server(('127.0.0.1', 0), TestHTTPRequestHandler) # 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) + os.environ["PORT"] = str(httpd.server_port) debug("$PORT = %s", port) - sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) + sys.exit(run(server_inst=httpd, *sys.argv[1:])) diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 9a2de71142..09f0f3c681 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 +from threading import Thread 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,39 +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. Otherwise the command must be a full pathname. + + server_inst: an instance of a subclass of SocketServer.BaseServer. + + When you pass server_inst, its serve_forever() method is called on a + separate Thread before the child process is run. It is shutdown() when 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. + raise Error("Obsolete call to testrunner.run(): pass server_inst=, not server=") + + try: + server_inst = kwds.pop("server_inst") + except KeyError: + # We're not starting a thread, so shutdown() is a no-op. + shutdown = lambda: None + else: + # Make a Thread on which to call server_inst.serve_forever(). + thread = Thread(name="server", target=server_inst.serve_forever) + + # Make this a "daemon" thread. 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...", " ".join(args)) - if kwds.get("use_path", False): - rc = os.spawnvp(os.P_WAIT, args[0], args) - else: - rc = os.spawnv(os.P_WAIT, args[0], args) - debug("%s returned %s", args[0], rc) - return rc + + # We used to simply 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 to 1. So try to play nice. + def shutdown(): + # evidently this call blocks until shutdown is complete + server_inst.shutdown() + # which should make it straightforward to join() + thread.join() + + try: + # 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...", " ".join(args)) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s", args[0], rc) + return rc + + finally: + shutdown() # **************************************************************************** # test code -- manual at this point, see SWAT-564 diff --git a/indra/newview/tests/test_llxmlrpc_peer.py b/indra/newview/tests/test_llxmlrpc_peer.py index 281b72a058..12394ad1d9 100755 --- a/indra/newview/tests/test_llxmlrpc_peer.py +++ b/indra/newview/tests/test_llxmlrpc_peer.py @@ -35,11 +35,20 @@ from threading import Thread from SimpleXMLRPCServer import SimpleXMLRPCServer mydir = os.path.dirname(__file__) # expected to be .../indra/newview/tests/ -sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) -sys.path.insert(1, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) -from testrunner import freeport, run, debug +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) +from testrunner import run, debug class TestServer(SimpleXMLRPCServer): + # This server_bind() override is borrowed and simplified from + # BaseHTTPServer.HTTPServer.server_bind(): we want to capture the actual + # server port. BaseHTTPServer.HTTPServer.server_bind() stores the actual + # port in a server_port attribute, but SimpleXMLRPCServer isn't derived + # from HTTPServer. So do it ourselves. + def server_bind(self): + """Override server_bind to store the server port.""" + SimpleXMLRPCServer.server_bind(self) + self.server_port = self.socket.getsockname()[1] + def _dispatch(self, method, params): try: func = getattr(self, method) @@ -67,15 +76,11 @@ class TestServer(SimpleXMLRPCServer): pass if __name__ == "__main__": - # Instantiate a TestServer 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. - xmlrpcd, port = freeport(xrange(8000, 8020), - lambda port: TestServer(('127.0.0.1', port))) + # Make the runtime choose an available port. + xmlrpcd = TestServer(('127.0.0.1', 0)) # 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) - sys.exit(run(server=Thread(name="xmlrpc", target=xmlrpcd.serve_forever), *sys.argv[1:])) + os.environ["PORT"] = str(xmlrpcd.server_port) + sys.exit(run(server_inst=xmlrpcd, *sys.argv[1:])) -- cgit v1.2.3 From e1482838fe08ab4d0c4aafd22d50f98d0fdee6c1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 09:44:55 -0500 Subject: DRTVWR-418: Fix a couple variable references in debugging output. --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 2 +- indra/llmessage/tests/test_llsdmessage_peer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index b900ad73ff..6c223990ca 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -306,7 +306,7 @@ if __name__ == "__main__": # command-line parsing -- and anyway, for C++ integration tests, that's # performed in TUT code rather than our own. os.environ["LL_TEST_PORT"] = str(httpd.server_port) - debug("$LL_TEST_PORT = %s", port) + debug("$LL_TEST_PORT = %s", httpd.server_port) if do_valgrind: args = ["valgrind", "--log-file=./valgrind.log"] + args path_search = True diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index a0d5d1b354..8e1204fb20 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -163,5 +163,5 @@ if __name__ == "__main__": # command-line parsing -- and anyway, for C++ integration tests, that's # performed in TUT code rather than our own. os.environ["PORT"] = str(httpd.server_port) - debug("$PORT = %s", port) + debug("$PORT = %s", httpd.server_port) sys.exit(run(server_inst=httpd, *sys.argv[1:])) -- cgit v1.2.3 From 0532e298a0550c70ca7d94992922d5c040e21702 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 10:05:24 -0500 Subject: DRTVWR-418: Reinstate test that we THOUGHT was killing test run. But since the real problem is quite different, try with that suspected test restored. --- indra/llcorehttp/tests/test_httprequest.hpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 0aad3d1835..463e55dd7e 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1455,10 +1455,6 @@ void HttpRequestTestObjectType::test<14>() set_test_name("HttpRequest GET timeout"); -#if LL_WINDOWS - skip("This test causes our dummy server test_llcorehttp_peer.py to fail"); -#endif - // Handler can be stack-allocated *if* there are no dangling // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. -- cgit v1.2.3 From 2569a1701dd127ae89c4f9e4aaaa6af09fb28d28 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 10:20:06 -0500 Subject: DRTVWR-418: Diagnostic prints to identify hangup --- indra/llmessage/tests/testrunner.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 09f0f3c681..0a53c312fa 100755 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -177,8 +177,14 @@ def run(*args, **kwds): # We're not starting a thread, so shutdown() is a no-op. shutdown = lambda: None else: + # Make a function that reports when serve_forever() returns. + def serve_forever(): + server_inst.serve_forever() + print "%s.serve_forever() returned" % server_inst.__class__.__name__ + sys.stdout.flush() + # Make a Thread on which to call server_inst.serve_forever(). - thread = Thread(name="server", target=server_inst.serve_forever) + thread = Thread(name="server", target=serve_forever) # Make this a "daemon" thread. thread.setDaemon(True) @@ -189,10 +195,16 @@ def run(*args, **kwds): # sys.exit(0), apparently killing the thread causes the Python runtime # to force the process termination code to 1. So try to play nice. def shutdown(): + print "Calling %s.shutdown()" % server_inst.__class__.__name__ + sys.stdout.flush() # evidently this call blocks until shutdown is complete server_inst.shutdown() + print "%s.shutdown() returned" % server_inst.__class__.__name__ + sys.stdout.flush() # which should make it straightforward to join() thread.join() + print "Thread.join() returned" + sys.stdout.flush() try: # choice of os.spawnv(): -- cgit v1.2.3 From 54f95e4d611a192b8a93c23e4c2499096121ae57 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 13:25:42 -0500 Subject: DRTVWR-418: Make testrunner.run() avoid extra Thread altogether. --- indra/llmessage/tests/testrunner.py | 94 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 52 deletions(-) (limited to 'indra') diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 0a53c312fa..c25945067e 100755 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -32,7 +32,7 @@ import sys import re import errno import socket -from threading import Thread +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 @@ -155,13 +155,13 @@ def run(*args, **kwds): In addition, you may pass keyword-only arguments: use_path=True: allow a simple filename as command and search PATH for that - filename. Otherwise the command must be a full pathname. + 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, its serve_forever() method is called on a - separate Thread before the child process is run. It is shutdown() when the - child process terminates. + 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: @@ -171,57 +171,47 @@ def run(*args, **kwds): else: raise Error("Obsolete call to testrunner.run(): pass server_inst=, not server=") + debug("Running %s...", " ".join(args)) + try: server_inst = kwds.pop("server_inst") except KeyError: - # We're not starting a thread, so shutdown() is a no-op. - shutdown = lambda: None + # Without server_inst, this is very simple: just run child process. + rc = subprocess.call(args) else: - # Make a function that reports when serve_forever() returns. - def serve_forever(): - server_inst.serve_forever() - print "%s.serve_forever() returned" % server_inst.__class__.__name__ - sys.stdout.flush() - - # Make a Thread on which to call server_inst.serve_forever(). - thread = Thread(name="server", target=serve_forever) - - # Make this a "daemon" thread. - thread.setDaemon(True) - thread.start() - - # We used to simply 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 to 1. So try to play nice. - def shutdown(): - print "Calling %s.shutdown()" % server_inst.__class__.__name__ - sys.stdout.flush() - # evidently this call blocks until shutdown is complete - server_inst.shutdown() - print "%s.shutdown() returned" % server_inst.__class__.__name__ - sys.stdout.flush() - # which should make it straightforward to join() - thread.join() - print "Thread.join() returned" - sys.stdout.flush() - - try: - # 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...", " ".join(args)) - if kwds.get("use_path", False): - rc = os.spawnvp(os.P_WAIT, args[0], args) - else: - rc = os.spawnv(os.P_WAIT, args[0], args) - debug("%s returned %s", args[0], rc) - return rc - - finally: - shutdown() + # 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 # **************************************************************************** # test code -- manual at this point, see SWAT-564 -- cgit v1.2.3 From 5bb456d80cfbcdfe87526510f3b8297d315afdd8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Dec 2016 14:10:32 -0500 Subject: DRTVWR-418: Apparently (some) Windows hosts still need freeport(). This is the function in indra/llmessage/tests/testrunner.py that iterates through ports in a specified range, looking for an available one. Other platforms understand a specification of port 0 to mean: "You pick one. I'll just use whichever one you picked." --- indra/llcorehttp/tests/test_llcorehttp_peer.py | 18 +++++++++++++----- indra/llmessage/tests/test_llsdmessage_peer.py | 19 ++++++++++++++----- indra/newview/tests/test_llxmlrpc_peer.py | 17 +++++++++++++---- 3 files changed, 40 insertions(+), 14 deletions(-) (limited to 'indra') diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 6c223990ca..493143641b 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -34,7 +34,6 @@ import sys import time import select import getopt -from threading import Thread try: from cStringIO import StringIO except ImportError: @@ -48,7 +47,7 @@ from llbase import llsd sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "llmessage", "tests")) -from testrunner import run, debug, VERBOSE +from testrunner import freeport, run, debug, VERBOSE class TestHTTPRequestHandler(BaseHTTPRequestHandler): """This subclass of BaseHTTPRequestHandler is to receive and echo @@ -297,9 +296,18 @@ if __name__ == "__main__": if option == "-V" or option == "--valgrind": do_valgrind = True - # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the - # runtime. - httpd = Server(('127.0.0.1', 0), 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 diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 8e1204fb20..9cd2959ea1 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -31,12 +31,11 @@ $/LicenseInfo$ import os import sys -from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from llbase.fastest_elementtree import parse as xml_parse from llbase import llsd -from testrunner import run, debug, VERBOSE +from testrunner import freeport, run, debug, VERBOSE import time _storage=None @@ -155,9 +154,19 @@ class Server(HTTPServer): allow_reuse_address = False if __name__ == "__main__": - # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the - # runtime. - httpd = Server(('127.0.0.1', 0), 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 diff --git a/indra/newview/tests/test_llxmlrpc_peer.py b/indra/newview/tests/test_llxmlrpc_peer.py index 12394ad1d9..cff40aa4c2 100755 --- a/indra/newview/tests/test_llxmlrpc_peer.py +++ b/indra/newview/tests/test_llxmlrpc_peer.py @@ -31,12 +31,11 @@ $/LicenseInfo$ import os import sys -from threading import Thread from SimpleXMLRPCServer import SimpleXMLRPCServer mydir = os.path.dirname(__file__) # expected to be .../indra/newview/tests/ sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "llmessage", "tests")) -from testrunner import run, debug +from testrunner import freeport, run, debug class TestServer(SimpleXMLRPCServer): # This server_bind() override is borrowed and simplified from @@ -76,8 +75,18 @@ class TestServer(SimpleXMLRPCServer): pass if __name__ == "__main__": - # Make the runtime choose an available port. - xmlrpcd = TestServer(('127.0.0.1', 0)) + # function to make a server with specified port + make_server = lambda port: TestServer(('127.0.0.1', port)) + + if not sys.platform.startswith("win"): + # Instantiate a TestServer on a port chosen by the runtime. + xmlrpcd = make_server(0) + else: + # "Then there's Windows" + # Instantiate a TestServer on the first free port in the specified + # port range. + xmlrpcd, 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 -- cgit v1.2.3 From a65b586b184d9837e0586b4df0d2e758ccce63f6 Mon Sep 17 00:00:00 2001 From: AndreyL ProductEngine Date: Thu, 8 Dec 2016 17:21:05 +0200 Subject: MAINT-6729 Additional fix for crash in LLImageGL::analyzeAlpha() --- indra/llimage/llimage.cpp | 2 +- indra/llimage/llimage.h | 2 +- indra/llrender/llimagegl.cpp | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 43b6b3bcd6..a07ea14621 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -800,7 +800,7 @@ U8* LLImageBase::getData() return mData; } -bool LLImageBase::isBufferInvalid() +bool LLImageBase::isBufferInvalid() const { return mBadBufferAllocation || mData == NULL ; } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 9cc7431a9c..d0bd4a2aef 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -141,7 +141,7 @@ public: const U8 *getData() const ; U8 *getData() ; - bool isBufferInvalid() ; + bool isBufferInvalid() const; void setSize(S32 width, S32 height, S32 ncomponents); U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData() diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 81a5537f78..20cba68f84 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1267,6 +1267,12 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S llassert(gGLManager.mInited); stop_glerror(); + if (!imageraw || imageraw->isBufferInvalid()) + { + LL_WARNS() << "Trying to create a texture from invalid image data" << LL_ENDL; + return FALSE; + } + if (discard_level < 0) { llassert(mCurrentDiscardLevel >= 0); -- cgit v1.2.3