devtools: Reland devtools test fixes missing from #38359

Co-authored-by: atbrakhi <atbrakhi@igalia.com>
Signed-off-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
Delan Azabani 2025-08-07 18:43:42 +08:00
parent 616b86fb6d
commit 1e74ff465c
4 changed files with 56 additions and 34 deletions

View file

@ -38,9 +38,9 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
def __init__(self, methodName="runTest"): def __init__(self, methodName="runTest"):
super().__init__(methodName) super().__init__(methodName)
self.servoshell = None self.servoshell = None
self.base_url = None self.base_urls = None
self.web_server = None self.web_servers = None
self.web_server_thread = None self.web_server_threads = None
# Classic script vs module script: # Classic script vs module script:
# - <https://html.spec.whatwg.org/multipage/#classic-script> # - <https://html.spec.whatwg.org/multipage/#classic-script>
@ -59,13 +59,13 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
[ [
tuple( tuple(
[ [
f"{self.base_url}/classic.js", f"{self.base_urls[0]}/classic.js",
f"{self.base_url}/test.html", f"{self.base_urls[0]}/test.html",
"https://servo.org/js/load-table.js", f"{self.base_urls[1]}/classic.js",
f"{self.base_url}/test.html", f"{self.base_urls[0]}/test.html",
] ]
), ),
tuple([f"{self.base_url}/worker.js"]), tuple([f"{self.base_urls[0]}/worker.js"]),
] ]
), ),
) )
@ -84,8 +84,8 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
def test_sources_list_with_data_external_classic_script(self): def test_sources_list_with_data_external_classic_script(self):
self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources")) self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources"))
self.run_servoshell(url=f'data:text/html,<script src="{self.base_url}/classic.js"></script>') self.run_servoshell(url=f'data:text/html,<script src="{self.base_urls[0]}/classic.js"></script>')
self.assert_sources_list(1, set([tuple([f"{self.base_url}/classic.js"])])) self.assert_sources_list(1, set([tuple([f"{self.base_urls[0]}/classic.js"])]))
def test_sources_list_with_data_empty_inline_module_script(self): def test_sources_list_with_data_empty_inline_module_script(self):
self.run_servoshell(url="data:text/html,<script type=module></script>") self.run_servoshell(url="data:text/html,<script type=module></script>")
@ -102,15 +102,15 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
def test_source_content_external_script(self): def test_source_content_external_script(self):
self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources")) self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources"))
self.run_servoshell(url=f'data:text/html,<script src="{self.base_url}/classic.js"></script>') self.run_servoshell(url=f'data:text/html,<script src="{self.base_urls[0]}/classic.js"></script>')
expected_content = 'console.log("external classic");\n' expected_content = 'console.log("external classic");\n'
self.assert_source_content(f"{self.base_url}/classic.js", expected_content) self.assert_source_content(f"{self.base_urls[0]}/classic.js", expected_content)
def test_source_content_html_file(self): def test_source_content_html_file(self):
self.start_web_server(test_dir=self.get_test_path("sources")) self.start_web_server(test_dir=self.get_test_path("sources"))
self.run_servoshell() self.run_servoshell()
expected_content = open(self.get_test_path("sources/test.html")).read() expected_content = open(self.get_test_path("sources/test.html")).read()
self.assert_source_content(f"{self.base_url}/test.html", expected_content) self.assert_source_content(f"{self.base_urls[0]}/test.html", expected_content)
def test_source_content_with_inline_module_import_external(self): def test_source_content_with_inline_module_import_external(self):
self.start_web_server(test_dir=self.get_test_path("sources_content_with_inline_module_import_external")) self.start_web_server(test_dir=self.get_test_path("sources_content_with_inline_module_import_external"))
@ -147,13 +147,16 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
self.start_web_server(test_dir=self.get_test_path("sources_content_with_responsexml")) self.start_web_server(test_dir=self.get_test_path("sources_content_with_responsexml"))
self.run_servoshell() self.run_servoshell()
expected_content = open(self.get_test_path("sources_content_with_responsexml/test.html")).read() expected_content = open(self.get_test_path("sources_content_with_responsexml/test.html")).read()
self.assert_source_content(f"{self.base_url}/test.html", expected_content) self.assert_source_content(f"{self.base_urls[0]}/test.html", expected_content)
# Sets `base_url` and `web_server` and `web_server_thread`. # Sets `base_url` and `web_server` and `web_server_thread`.
def start_web_server(self, *, test_dir=None): def start_web_server(self, *, test_dir=None, num_servers=2):
assert self.base_urls is None and self.web_servers is None and self.web_server_threads is None
if test_dir is None: if test_dir is None:
test_dir = os.path.join(DevtoolsTests.script_path, "devtools_tests") test_dir = os.path.join(DevtoolsTests.script_path, "devtools_tests")
base_url = Future() base_urls = [Future() for i in range(num_servers)]
self.web_servers = [None for i in range(num_servers)]
self.web_server_threads = [None for i in range(num_servers)]
class Handler(http.server.SimpleHTTPRequestHandler): class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -163,15 +166,24 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
if LOG_REQUESTS: if LOG_REQUESTS:
return super().log_message(format, *args) return super().log_message(format, *args)
def server_thread(): def server_thread(index):
self.web_server = socketserver.TCPServer(("0.0.0.0", 0), Handler) # There may be client sockets still open in TIME_WAIT state from previous tests, and they may stay open for
base_url.set_result(f"http://127.0.0.1:{self.web_server.server_address[1]}") # some minutes. Set SO_REUSEADDR to avoid bind failure with EADDRINUSE in these cases.
self.web_server.serve_forever() # <https://stackoverflow.com/questions/14388706>
socketserver.TCPServer.allow_reuse_address = True
# Listen on all IPv4 interfaces, port 10000 + index.
web_server = socketserver.TCPServer(("127.0.0.1", 10000 + index), Handler)
base_url = f"http://127.0.0.1:{web_server.server_address[1]}"
base_urls[index].set_result(base_url)
self.web_servers[index] = web_server
web_server.serve_forever()
# Start a web server for the test. # Start a web server for the test.
self.web_server_thread = Thread(target=server_thread) for index in range(num_servers):
self.web_server_thread.start() thread = Thread(target=server_thread, args=[index])
self.base_url = base_url.result(1) self.web_server_threads[index] = thread
thread.start()
self.base_urls = [base_url.result(1) for base_url in base_urls]
# Sets `servoshell`. # Sets `servoshell`.
def run_servoshell(self, *, url=None): def run_servoshell(self, *, url=None):
@ -180,7 +192,7 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
# Run servoshell. # Run servoshell.
if url is None: if url is None:
url = f"{self.base_url}/test.html" url = f"{self.base_urls[0]}/test.html"
self.servoshell = subprocess.Popen([f"target/{self.build_type.directory_name()}/servo", "--devtools=6080", url]) self.servoshell = subprocess.Popen([f"target/{self.build_type.directory_name()}/servo", "--devtools=6080", url])
# FIXME: Dont do this # FIXME: Dont do this
@ -192,15 +204,19 @@ class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
self.servoshell.terminate() self.servoshell.terminate()
self.servoshell = None self.servoshell = None
# Stop the web server. # Stop the web servers.
if self.web_server is not None: if self.web_servers is not None:
self.web_server.shutdown() for web_server in self.web_servers:
self.web_server = None web_server.shutdown()
if self.web_server_thread is not None: web_server.server_close()
self.web_server_thread.join() os.system("ss -lnp | egrep '1000[01]'")
self.web_server_thread = None self.web_servers = None
if self.base_url is not None: if self.web_server_threads is not None:
self.base_url = None for web_server_thread in self.web_server_threads:
web_server_thread.join()
self.web_server_threads = None
if self.base_urls is not None:
self.base_urls = None
def _setup_devtools_client(self, expected_targets=1): def _setup_devtools_client(self, expected_targets=1):
client = RDPClient() client = RDPClient()

View file

@ -8,4 +8,4 @@
import module from "./module.js"; import module from "./module.js";
console.log("inline module"); console.log("inline module");
</script> </script>
<script src="https://servo.org/js/load-table.js"></script> <script src="http://127.0.0.1:10001/classic.js"></script>

View file

@ -0,0 +1,2 @@
export default 1;
console.log("external module");

View file

@ -0,0 +1,4 @@
<!doctype html><meta charset=utf-8>
<script type="module">
import module from "./module.js";
</script>