2014-08-26 08:13:32 +04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
import cStringIO as StringIO
|
2014-09-12 21:57:26 +04:00
|
|
|
import glob
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
import imp
|
2014-08-26 08:13:32 +04:00
|
|
|
import optparse
|
|
|
|
import os
|
2014-09-05 19:39:51 +04:00
|
|
|
import posixpath
|
2014-11-21 05:00:45 +03:00
|
|
|
import shlex
|
2014-09-05 19:39:51 +04:00
|
|
|
import SimpleHTTPServer
|
|
|
|
import SocketServer
|
|
|
|
import socket
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
import string
|
2014-08-26 08:13:32 +04:00
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import threading
|
|
|
|
import time
|
2014-09-05 19:39:51 +04:00
|
|
|
import urllib
|
2014-08-26 08:13:32 +04:00
|
|
|
|
2014-09-17 06:57:45 +04:00
|
|
|
TIMEOUT = 10 # Maximum duration of PhantomJS execution (in seconds)
|
2014-08-26 08:13:32 +04:00
|
|
|
|
2014-09-05 19:39:51 +04:00
|
|
|
HTTP_PORT = 9180
|
|
|
|
http_running = False
|
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
TESTS = [
|
2014-09-12 21:57:26 +04:00
|
|
|
'basics/*.js',
|
|
|
|
'module/system/*.js',
|
|
|
|
'module/webpage/*.js',
|
2014-10-01 09:00:53 +04:00
|
|
|
'module/cookiejar/*.js',
|
2014-09-12 21:57:26 +04:00
|
|
|
'standards/javascript/*.js',
|
2014-09-30 08:29:44 +04:00
|
|
|
'standards/console/*.js',
|
2014-09-12 21:57:26 +04:00
|
|
|
'regression/*.js',
|
2014-08-26 08:13:32 +04:00
|
|
|
'run-tests.js'
|
|
|
|
]
|
|
|
|
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
# This should be in the standard library somewhere, but as far as I
|
|
|
|
# can tell, isn't.
|
|
|
|
def import_file_as_module(path):
|
2014-09-17 00:56:27 +04:00
|
|
|
# All Python response hooks, no matter how deep below www_path,
|
|
|
|
# are treated as direct children of the fake "test_www" package.
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
if 'test_www' not in sys.modules:
|
2014-09-17 00:56:27 +04:00
|
|
|
imp.load_source('test_www', www_path + '/__init__.py')
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
|
|
|
|
tr = string.maketrans('-./%', '____')
|
|
|
|
modname = 'test_www.' + path.translate(tr)
|
|
|
|
try:
|
|
|
|
return sys.modules[modname]
|
|
|
|
except KeyError:
|
|
|
|
return imp.load_source(modname, path)
|
2014-09-05 19:39:51 +04:00
|
|
|
|
2014-09-08 07:04:26 +04:00
|
|
|
class FileHandler(SimpleHTTPServer.SimpleHTTPRequestHandler, object):
|
|
|
|
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self._cached_untranslated_path = None
|
|
|
|
self._cached_translated_path = None
|
|
|
|
super(FileHandler, self).__init__(*args, **kwargs)
|
2014-09-05 19:39:51 +04:00
|
|
|
|
|
|
|
# silent, do not pollute stdout nor stderr.
|
|
|
|
def log_message(self, format, *args):
|
|
|
|
return
|
|
|
|
|
2014-09-17 00:56:27 +04:00
|
|
|
# allow provision of a .py file that will be interpreted to
|
|
|
|
# produce the response.
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
def send_head(self):
|
|
|
|
path = self.translate_path(self.path)
|
2014-09-17 00:56:27 +04:00
|
|
|
|
|
|
|
# do not allow direct references to .py(c) files,
|
|
|
|
# or indirect references to __init__.py
|
|
|
|
if (path.endswith('.py') or path.endswith('.pyc') or
|
|
|
|
path.endswith('__init__')):
|
|
|
|
self.send_error(404, 'File not found')
|
|
|
|
return None
|
|
|
|
|
|
|
|
if os.path.exists(path):
|
|
|
|
return super(FileHandler, self).send_head()
|
|
|
|
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
py = path + '.py'
|
2014-09-17 00:56:27 +04:00
|
|
|
if os.path.exists(py):
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
try:
|
|
|
|
mod = import_file_as_module(py)
|
|
|
|
return mod.handle_request(self)
|
|
|
|
except:
|
|
|
|
import cgitb
|
|
|
|
buf = StringIO.StringIO()
|
|
|
|
cgitb.Hook(file=buf).handle()
|
|
|
|
buf = buf.getvalue()
|
|
|
|
|
|
|
|
self.send_response(500, 'Internal Server Error')
|
|
|
|
self.send_header('Content-Type', 'text/html')
|
|
|
|
self.send_header('Content-Length', str(len(buf)))
|
|
|
|
self.end_headers()
|
|
|
|
return StringIO.StringIO(buf)
|
|
|
|
|
2014-09-17 00:56:27 +04:00
|
|
|
self.send_error(404, 'File not found')
|
|
|
|
return None
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
|
2014-09-05 19:39:51 +04:00
|
|
|
# modified version of SimpleHTTPRequestHandler's translate_path
|
|
|
|
# to resolve the URL relative to the www/ directory
|
|
|
|
# (e.g. /foo -> test/www/foo)
|
|
|
|
def translate_path(self, path):
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
|
|
|
|
# Cache for efficiency, since our send_head calls this and
|
|
|
|
# then, in the normal case, the parent class's send_head
|
|
|
|
# immediately calls it again.
|
|
|
|
if (self._cached_translated_path is not None and
|
|
|
|
self._cached_untranslated_path == path):
|
|
|
|
return self._cached_translated_path
|
|
|
|
|
|
|
|
orig_path = path
|
|
|
|
|
Improvements to logic in FileHandler.translate_path.
We already have 'base_path' as a global variable, so use that (or rather,
a new derivative of that) instead of inspect.getfile(inspect.currentframe()).
Rather than splitting the path and then iterating over every component
trying to figure out if it's "special", take advantage of the fact that
posixpath.normpath() will reliably produce a path with all ../ and bare-/
components at the very beginning. Preserve the distinction between paths
with and without a trailing /, as SimpleHTTPRequestHandler expects.
So that paths containing unusual (e.g. non-ASCII, non-UTF-8) segments can
safely be tested, unquote and then requote the entire path. This means
that the *bytes* corresponding to the ASCII characters
0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz -_./
are guaranteed to be unquoted, and all other *bytes* are guaranteed to be
%-quoted. (Note especially that *whether or not* the tests are being run
on Windows, \ in the original URL will be represented as %5C, and will
*not* be treated as a pathname separator.)
So that the tests behave consistently whether or not the OS has a case-
sensitive filesystem, after the above transformation, lowercase the entire
path.
Ongoing work on issue #12439.
2014-09-12 22:00:42 +04:00
|
|
|
# Strip query string and/or fragment, if present.
|
|
|
|
x = path.find('?')
|
|
|
|
if x != -1: path = path[:x]
|
|
|
|
x = path.find('#')
|
|
|
|
if x != -1: path = path[:x]
|
|
|
|
|
|
|
|
# Ensure consistent encoding of special characters, then
|
|
|
|
# lowercase everything so that the tests behave consistently
|
|
|
|
# whether or not the local filesystem is case-sensitive.
|
|
|
|
path = urllib.quote(urllib.unquote(path)).lower()
|
|
|
|
|
|
|
|
# Prevent access to files outside www/.
|
|
|
|
# At this point we want specifically POSIX-like treatment of 'path'
|
|
|
|
# because it is still a URL component and not a filesystem path.
|
|
|
|
# SimpleHTTPRequestHandler.send_head() expects us to preserve the
|
|
|
|
# distinction between paths with and without a trailing slash, but
|
|
|
|
# posixpath.normpath() discards that distinction.
|
|
|
|
trailing_slash = path.endswith('/')
|
|
|
|
path = posixpath.normpath(path)
|
|
|
|
while path.startswith('/'):
|
|
|
|
path = path[1:]
|
|
|
|
while path.startswith('../'):
|
|
|
|
path = path[3:]
|
|
|
|
|
|
|
|
# Now resolve the normalized, clamped path relative to the www/
|
|
|
|
# directory, according to local OS conventions.
|
|
|
|
path = os.path.normpath(os.path.join(www_path, *path.split('/')))
|
|
|
|
if trailing_slash:
|
|
|
|
# it must be a '/' even on Windows
|
|
|
|
path += '/'
|
Support Python scripts in test/www/.
Rather than add all the special URLs we'll ever want to run-tests.py,
the embedded HTTP server now supports scripts in test/www/. If you
try to load http://localhost:9180/path and test/www/path doesn't exist
but test/www/path.py does, then test/www/path.py is loaded as a module.
That module must export one function, handle_request(), which is called
to produce the response.
handle_request() has the same semantics as
SimpleHTTPRequestHandler.send_head(). That is, it takes one argument,
the SimpleHTTPRequestHandler object, conventionally named 'req'.
It should call the send_response(), send_header(), and end_headers()
methods of that object as appropriate. And it should return a readable
filelike whose contents are taken as the body of the response.
If either module import or handle_request() throws any exception, the
HTTP response will be a 500 Internal Server Error with body provided
by cgitb.
Ongoing work on issue #12439.
2014-09-12 23:43:29 +04:00
|
|
|
|
|
|
|
self._cached_untranslated_path = orig_path
|
|
|
|
self._cached_translated_path = path
|
2014-09-05 19:39:51 +04:00
|
|
|
return path
|
|
|
|
|
2014-09-17 01:00:52 +04:00
|
|
|
# This is how you are officially supposed to set SO_REUSEADDR per
|
|
|
|
# https://docs.python.org/2/library/socketserver.html#SocketServer.BaseServer.allow_reuse_address
|
|
|
|
|
|
|
|
class TCPServer(SocketServer.TCPServer):
|
|
|
|
allow_reuse_address = True
|
|
|
|
|
2014-09-05 19:39:51 +04:00
|
|
|
def run_httpd():
|
|
|
|
global http_running
|
|
|
|
handler = FileHandler
|
|
|
|
handler.extensions_map.update({
|
|
|
|
'.htm': 'text/html',
|
|
|
|
'.html': 'text/html',
|
|
|
|
'.css': 'text/css',
|
|
|
|
'.js': 'application/javascript',
|
|
|
|
'.json': 'application/json'
|
|
|
|
})
|
|
|
|
try:
|
2014-09-17 01:00:52 +04:00
|
|
|
httpd = TCPServer(('', HTTP_PORT), handler)
|
2014-09-05 19:39:51 +04:00
|
|
|
while http_running:
|
|
|
|
httpd.handle_request()
|
2014-09-17 01:00:52 +04:00
|
|
|
except socket.error as e:
|
2014-09-05 19:39:51 +04:00
|
|
|
print 'Fatal error: unable to launch a test server at port', HTTP_PORT
|
2014-09-17 01:00:52 +04:00
|
|
|
print str(e)
|
2014-09-05 19:39:51 +04:00
|
|
|
http_running = False
|
|
|
|
sys.exit(1)
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def setup_server():
|
|
|
|
global http_running
|
|
|
|
|
|
|
|
http_running = True
|
|
|
|
httpd_thread = threading.Thread(target=run_httpd)
|
|
|
|
httpd_thread.start()
|
|
|
|
|
|
|
|
time.sleep(2)
|
|
|
|
if http_running:
|
|
|
|
if options.verbose:
|
|
|
|
print 'serving at port', HTTP_PORT
|
|
|
|
|
|
|
|
|
|
|
|
def terminate_server():
|
|
|
|
global http_running
|
|
|
|
http_running = False
|
|
|
|
|
|
|
|
# ping the server once to trigger the check for http_running (after every request)
|
|
|
|
urllib.urlopen('http://localhost:{0}'.format(HTTP_PORT))
|
|
|
|
|
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
def init():
|
Improvements to logic in FileHandler.translate_path.
We already have 'base_path' as a global variable, so use that (or rather,
a new derivative of that) instead of inspect.getfile(inspect.currentframe()).
Rather than splitting the path and then iterating over every component
trying to figure out if it's "special", take advantage of the fact that
posixpath.normpath() will reliably produce a path with all ../ and bare-/
components at the very beginning. Preserve the distinction between paths
with and without a trailing /, as SimpleHTTPRequestHandler expects.
So that paths containing unusual (e.g. non-ASCII, non-UTF-8) segments can
safely be tested, unquote and then requote the entire path. This means
that the *bytes* corresponding to the ASCII characters
0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz -_./
are guaranteed to be unquoted, and all other *bytes* are guaranteed to be
%-quoted. (Note especially that *whether or not* the tests are being run
on Windows, \ in the original URL will be represented as %5C, and will
*not* be treated as a pathname separator.)
So that the tests behave consistently whether or not the OS has a case-
sensitive filesystem, after the above transformation, lowercase the entire
path.
Ongoing work on issue #12439.
2014-09-12 22:00:42 +04:00
|
|
|
global base_path, www_path, phantomjs_exe, options
|
2014-08-26 08:13:32 +04:00
|
|
|
|
|
|
|
base_path = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
phantomjs_exe = os.path.normpath(base_path + '/../bin/phantomjs')
|
|
|
|
if not os.path.isfile(phantomjs_exe):
|
|
|
|
print 'Could not locate ' + phantomjs_exe
|
|
|
|
sys.exit(1)
|
|
|
|
|
Improvements to logic in FileHandler.translate_path.
We already have 'base_path' as a global variable, so use that (or rather,
a new derivative of that) instead of inspect.getfile(inspect.currentframe()).
Rather than splitting the path and then iterating over every component
trying to figure out if it's "special", take advantage of the fact that
posixpath.normpath() will reliably produce a path with all ../ and bare-/
components at the very beginning. Preserve the distinction between paths
with and without a trailing /, as SimpleHTTPRequestHandler expects.
So that paths containing unusual (e.g. non-ASCII, non-UTF-8) segments can
safely be tested, unquote and then requote the entire path. This means
that the *bytes* corresponding to the ASCII characters
0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz -_./
are guaranteed to be unquoted, and all other *bytes* are guaranteed to be
%-quoted. (Note especially that *whether or not* the tests are being run
on Windows, \ in the original URL will be represented as %5C, and will
*not* be treated as a pathname separator.)
So that the tests behave consistently whether or not the OS has a case-
sensitive filesystem, after the above transformation, lowercase the entire
path.
Ongoing work on issue #12439.
2014-09-12 22:00:42 +04:00
|
|
|
www_path = os.path.join(base_path, 'www')
|
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
parser = optparse.OptionParser(
|
2014-09-13 00:05:23 +04:00
|
|
|
usage='%prog [options] [tests to run...]',
|
2014-08-26 08:13:32 +04:00
|
|
|
description='Run PhantomJS tests.'
|
|
|
|
)
|
|
|
|
parser.add_option('--verbose', action='store_true', default=False, help='Show a verbose log')
|
|
|
|
(options, args) = parser.parse_args(sys.argv[1:])
|
|
|
|
|
2014-09-13 00:05:23 +04:00
|
|
|
options.to_run = args
|
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
if options.verbose:
|
|
|
|
returncode, version = run_phantomjs('--version')
|
|
|
|
print 'Checking PhantomJS version %s' % version
|
|
|
|
|
|
|
|
|
2014-11-21 05:00:45 +03:00
|
|
|
def run_phantomjs(script, script_args=[], pjs_args=[]):
|
2014-08-26 08:13:32 +04:00
|
|
|
output = []
|
2014-11-21 05:00:45 +03:00
|
|
|
command = [phantomjs_exe]
|
|
|
|
command.extend(pjs_args)
|
|
|
|
command.append(script)
|
|
|
|
command.extend(script_args)
|
2014-08-26 08:13:32 +04:00
|
|
|
process = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
|
|
|
|
|
|
|
|
def runner():
|
|
|
|
while True:
|
|
|
|
line = process.stdout.readline()
|
|
|
|
if line != '':
|
|
|
|
output.append(line.rstrip())
|
|
|
|
if options.verbose:
|
|
|
|
print '%s' % line.rstrip()
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
thread = threading.Thread(target=runner)
|
|
|
|
thread.start()
|
|
|
|
thread.join(TIMEOUT)
|
|
|
|
if thread.is_alive():
|
|
|
|
print 'Process is running more than ' + str(TIMEOUT) + ' seconds. Terminating...'
|
|
|
|
process.terminate()
|
|
|
|
thread.join()
|
|
|
|
process.wait()
|
|
|
|
return process.returncode, '\n'.join(output)
|
|
|
|
|
|
|
|
|
2014-09-12 21:57:26 +04:00
|
|
|
def run_test(script, name):
|
2014-11-21 05:00:45 +03:00
|
|
|
script_args = []
|
|
|
|
pjs_args = []
|
2014-08-26 08:13:32 +04:00
|
|
|
if options.verbose:
|
2014-11-21 05:00:45 +03:00
|
|
|
script_args.append('--verbose')
|
2014-08-26 08:13:32 +04:00
|
|
|
|
2014-11-21 05:00:45 +03:00
|
|
|
try:
|
|
|
|
with open(script, "rt") as s:
|
|
|
|
p_prefix = "// phantomjs: "
|
|
|
|
s_prefix = "// script: "
|
|
|
|
for line in s:
|
|
|
|
if line.startswith(p_prefix):
|
|
|
|
pjs_args.extend(shlex.split(line[len(p_prefix):]))
|
|
|
|
if line.startswith(s_prefix):
|
|
|
|
script_args.extend(shlex.split(line[len(s_prefix):]))
|
|
|
|
if not line.startswith("//"):
|
|
|
|
break
|
|
|
|
except OSError as e:
|
|
|
|
print '%s: %s: %s' % (name, e.filename, e.strerror)
|
|
|
|
return 1
|
|
|
|
|
|
|
|
print '%s:' % name
|
|
|
|
returncode, output = run_phantomjs(script, script_args, pjs_args)
|
|
|
|
if returncode != 0:
|
|
|
|
if not options.verbose:
|
|
|
|
print '%s' % output
|
|
|
|
return 1
|
|
|
|
|
|
|
|
return 0
|
2014-08-26 08:13:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
def run_tests():
|
2014-09-05 19:39:51 +04:00
|
|
|
setup_server()
|
|
|
|
if not http_running:
|
2014-09-13 00:05:23 +04:00
|
|
|
return 1
|
2014-09-05 19:39:51 +04:00
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
start = time.time()
|
|
|
|
if options.verbose:
|
|
|
|
print 'Starting the tests...'
|
|
|
|
|
|
|
|
result = 0
|
2014-09-13 00:05:23 +04:00
|
|
|
any_executed = False
|
2014-09-12 21:57:26 +04:00
|
|
|
for test_group in TESTS:
|
|
|
|
test_group_name = os.path.dirname(test_group)
|
|
|
|
test_glob = os.path.normpath(base_path + '/' + test_group)
|
|
|
|
|
|
|
|
if options.verbose:
|
2014-08-26 08:13:32 +04:00
|
|
|
print
|
2014-09-13 00:05:23 +04:00
|
|
|
print 'Test group: %s...' % test_group_name
|
2014-09-12 21:57:26 +04:00
|
|
|
|
|
|
|
for test_script in glob.glob(test_glob):
|
|
|
|
tname = test_group_name + '/' + os.path.basename(test_script)
|
2014-09-13 00:05:23 +04:00
|
|
|
if options.to_run:
|
|
|
|
for to_run in options.to_run:
|
|
|
|
if to_run in tname:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
if options.verbose:
|
|
|
|
print "%s: skipped" % tname
|
|
|
|
continue
|
|
|
|
|
|
|
|
any_executed = True
|
2014-09-12 21:57:26 +04:00
|
|
|
ret = run_test(test_script, tname)
|
|
|
|
if ret != 0:
|
|
|
|
print 'The test %s FAILED' % tname
|
|
|
|
print
|
|
|
|
result = 1
|
2014-08-26 08:13:32 +04:00
|
|
|
|
2014-09-13 00:05:23 +04:00
|
|
|
if not any_executed:
|
|
|
|
result = 1
|
|
|
|
print
|
|
|
|
print 'ALL TESTS SKIPPED.'
|
|
|
|
|
2014-08-26 08:13:32 +04:00
|
|
|
if result == 0:
|
|
|
|
print
|
|
|
|
print 'No failure. Finished in %d seconds.' % (time.time() - start)
|
|
|
|
|
2014-09-05 19:39:51 +04:00
|
|
|
terminate_server()
|
|
|
|
return result
|
2014-08-26 08:13:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
init()
|
|
|
|
sys.exit(run_tests())
|