Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 29 additions & 17 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import itertools
import json
import logging
import multiprocessing
import threading
import os
import re
import shlex
Expand Down Expand Up @@ -2210,10 +2210,10 @@ def get_zlib_library(self, cmake):
return rtn


# Run a server and a web page. When a test runs, we tell the server about it,
# Create a server and a web page. When a test runs, we tell the server about it,
# which tells the web page, which then opens a window with the test. Doing
# it this way then allows the page to close() itself when done.
def harness_server_func(in_queue, out_queue, port):
def make_test_server(in_queue, out_queue, port):
class TestServerHandler(SimpleHTTPRequestHandler):
# Request header handler for default do_GET() path in
# SimpleHTTPRequestHandler.do_GET(self) below.
Expand Down Expand Up @@ -2313,10 +2313,6 @@ def do_GET(self):
else:
self.send_error(400, f'Not implemented for {code}')
elif 'report_' in self.path:
# the test is reporting its result. first change dir away from the
# test dir, as it will be deleted now that the test is finishing, and
# if we got a ping at that time, we'd return an error
os.chdir(path_from_root())
# for debugging, tests may encode the result and their own url (window.location) as result|url
if '|' in self.path:
path, url = self.path.split('|', 1)
Expand Down Expand Up @@ -2358,8 +2354,6 @@ def do_GET(self):
assert out_queue.empty(), 'the single response from the last test was read'
# tell the browser to load the test
self.wfile.write(b'COMMAND:' + url.encode('utf-8'))
# move us to the right place to serve the files for the new test
os.chdir(dir)
else:
# the browser must keep polling
self.wfile.write(b'(wait)')
Expand Down Expand Up @@ -2395,8 +2389,23 @@ def log_request(code=0, size=0):
# do not have the correct MIME type
SimpleHTTPRequestHandler.extensions_map['.mjs'] = 'text/javascript'

httpd = ThreadingHTTPServer(('localhost', port), TestServerHandler)
httpd.serve_forever() # test runner will kill us
return ThreadingHTTPServer(('localhost', port), TestServerHandler)


class HttpServerThread(threading.Thread):
"""A generic thread class to create and run an http server."""
def __init__(self, server):
super().__init__()
self.server = server

def stop(self):
"""Shuts down the server if it is running."""
self.server.shutdown()

def run(self):
"""Creates the server instance and serves forever until stop() is called."""
# Start the server's main loop (this blocks until shutdown() is called)
self.server.serve_forever()


class Reporting(Enum):
Expand Down Expand Up @@ -2488,21 +2497,24 @@ def setUpClass(cls):
super().setUpClass()
if not has_browser() or EMTEST_BROWSER == 'node':
return
cls.harness_in_queue = multiprocessing.Queue()
cls.harness_out_queue = multiprocessing.Queue()
cls.harness_server = multiprocessing.Process(target=harness_server_func, args=(cls.harness_in_queue, cls.harness_out_queue, cls.PORT))

cls.harness_in_queue = queue.Queue()
cls.harness_out_queue = queue.Queue()
cls.harness_server = HttpServerThread(make_test_server(cls.harness_in_queue, cls.harness_out_queue, cls.PORT))
cls.harness_server.start()
print('[Browser harness server on process %d]' % cls.harness_server.pid)

print(f'[Browser harness server on thread {cls.harness_server.name}]')
cls.browser_open(cls.HARNESS_URL)

@classmethod
def tearDownClass(cls):
super().tearDownClass()
if not has_browser() or EMTEST_BROWSER == 'node':
return
cls.harness_server.terminate()
print('[Browser harness server terminated]')
cls.harness_server.stop()
cls.harness_server.join()
cls.browser_terminate()

if WINDOWS:
# On Windows, shutil.rmtree() in tearDown() raises this exception if we do not wait a bit:
# WindowsError: [Error 32] The process cannot access the file because it is being used by another process.
Expand Down
22 changes: 13 additions & 9 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# found in the LICENSE file.

import argparse
import multiprocessing
import os
import random
import shlex
Expand All @@ -25,14 +24,17 @@
from common import read_file, EMRUN, no_wasm64, no_2gb, no_4gb, copytree
from common import requires_wasm2js, parameterize, find_browser_test_file, with_all_sjlj
from common import also_with_minimal_runtime, also_with_wasm2js, also_with_asan, also_with_wasmfs
from common import HttpServerThread
from tools import shared
from tools import ports
from tools.shared import EMCC, WINDOWS, FILE_PACKAGER, PIPE, DEBUG
from tools.utils import delete_dir


def test_chunked_synchronous_xhr_server(support_byte_ranges, data, port):
def make_test_chunked_synchronous_xhr_server(support_byte_ranges, data, port):
class ChunkedServerHandler(BaseHTTPRequestHandler):
num_get_connections = 0

def sendheaders(s, extra=None, length=None):
length = length or len(data)
s.send_response(200)
Expand All @@ -56,6 +58,11 @@ def do_OPTIONS(s):
s.sendheaders([("Access-Control-Allow-Headers", "Range")], 0)

def do_GET(s):
# CORS preflight makes OPTIONS requests which we need to account for.
expectedConns = 22
s.num_get_connections += 1
assert(s.num_get_connections < expectedConns)

if s.path == '/':
s.sendheaders()
elif not support_byte_ranges:
Expand All @@ -70,11 +77,7 @@ def do_GET(s):
s.sendheaders([], length)
s.wfile.write(data[start:end + 1])

# CORS preflight makes OPTIONS requests which we need to account for.
expectedConns = 22
httpd = HTTPServer(('localhost', 11111), ChunkedServerHandler)
for _ in range(expectedConns + 1):
httpd.handle_request()
return HTTPServer(('localhost', 11111), ChunkedServerHandler)


def also_with_proxying(f):
Expand Down Expand Up @@ -1703,7 +1706,7 @@ def test_chunked_synchronous_xhr(self):
data = os.urandom(10 * chunkSize + 1) # 10 full chunks and one 1 byte chunk
checksum = zlib.adler32(data) & 0xffffffff # Python 2 compatibility: force bigint

server = multiprocessing.Process(target=test_chunked_synchronous_xhr_server, args=(True, data, self.PORT))
server = HttpServerThread(make_test_chunked_synchronous_xhr_server(True, data, self.PORT))
server.start()

# block until the server is actually ready
Expand All @@ -1720,7 +1723,8 @@ def test_chunked_synchronous_xhr(self):
try:
self.run_browser(main, '/report_result?' + str(checksum))
finally:
server.terminate()
server.stop()
server.join()
# Avoid race condition on cleanup, wait a bit so that processes have released file locks so that test tearDown won't
# attempt to rmdir() files in use.
if WINDOWS:
Expand Down