-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
333 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,332 @@ | ||
# TODO | ||
# | ||
# Adapter les méthodes pour pouvoir runner les tests de web parallel | ||
# Adapter les méthodes pour dumper les résultats des benchs | ||
# Faire très attention sur la logique des timeouts | ||
# Pouvoir indiquer un timeout par test potentiellement | ||
# Probablement prévoir un json de conf par test, avec une conf par défaut | ||
# Auto-discovery des tests ? | ||
|
||
# 1. OK(setup CI/get driver) | ||
# 2. Ok(run driver + run server with npm run server in the background (kill on completion)) | ||
# 3. auto discovery tests | ||
# 4. load per test config (with default config if not specified) | ||
# 5. run test with the config timeout | ||
# 6. Ok(print logs (if possible as the test run so with a thread, no need for async, just that it prints from time to time)) | ||
# 7. If timeout -> fail the test | ||
# 8. If fail fast stop, otherwise run all tests | ||
# 9. Run tests sequentially | ||
# 10. Ok(If success keep going and mark success) | ||
# 11. Ok(On test success find a way if possible to flush logs) | ||
# 12. Ok(Refresh the test page/the website) | ||
# 13. Run the other tests | ||
|
||
import argparse | ||
import datetime | ||
import pathlib | ||
import time | ||
import threading | ||
import enum | ||
import os | ||
import signal | ||
import subprocess | ||
import socket | ||
import sys | ||
|
||
from selenium import webdriver | ||
from selenium.webdriver.chrome.options import Options | ||
from selenium.webdriver.chrome.service import Service | ||
from selenium.webdriver.common.by import By | ||
from selenium.webdriver.support import expected_conditions as EC | ||
from selenium.webdriver.support.ui import WebDriverWait | ||
from selenium.common.exceptions import TimeoutException | ||
|
||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"-a", | ||
"--address", | ||
dest="address", | ||
default="localhost", | ||
help="Address to testing Node server", | ||
) | ||
parser.add_argument( | ||
"-p", | ||
"--port", | ||
dest="port", | ||
default=3000, | ||
type=int, | ||
help="Port to testing Node server", | ||
) | ||
parser.add_argument( | ||
"-k", | ||
"--browser-kind", | ||
dest="browser_kind", | ||
choices=["chrome", "firefox"], | ||
required=True, | ||
help="Path to web driver file", | ||
) | ||
parser.add_argument( | ||
"-b", | ||
"--browser-path", | ||
dest="browser_path", | ||
required=True, | ||
help="Path to browser file", | ||
) | ||
parser.add_argument( | ||
"-d", | ||
"--driver-path", | ||
dest="driver_path", | ||
required=True, | ||
help="Path to web driver file", | ||
) | ||
parser.add_argument( | ||
"-f", | ||
"--fail-fast", | ||
dest="fail_fast", | ||
action="store_true", | ||
help="Exit on first failed test", | ||
) | ||
parser.add_argument( | ||
"--server-cmd", | ||
dest="server_cmd", | ||
help="Command to execute to launch web server in the background", | ||
) | ||
parser.add_argument( | ||
"--server-workdir", | ||
dest="server_workdir", | ||
help="Path to working directory to launch web server", | ||
) | ||
|
||
|
||
class BrowserKind(enum.Enum): | ||
chrome = 1 | ||
firefox = 2 | ||
|
||
|
||
class Driver: | ||
def __init__(self, browser_path, driver_path, browser_kind, threaded_logs=False): | ||
self.browser_path = browser_path | ||
self.driver_path = driver_path | ||
|
||
self.browser_kind = browser_kind | ||
|
||
self.options = Options() | ||
self.options.binary_location = self.browser_path | ||
self.options.add_argument("--headless") | ||
|
||
self.driver = None | ||
|
||
self.shutting_down = False | ||
|
||
self._log_thread = None | ||
if threaded_logs: | ||
self._log_thread = threading.Thread(target=self._threaded_logs) | ||
|
||
def set_capability(self, name, value): | ||
self.options.set_capability(name, value) | ||
|
||
def get_driver(self): | ||
if self.driver is None: | ||
driver_service = Service(self.driver_path) | ||
|
||
match self.browser_kind: | ||
case BrowserKind.chrome: | ||
self.driver = webdriver.Chrome( | ||
service=driver_service, options=self.options | ||
) | ||
# TODO: Add Firefox support | ||
case _: | ||
print( | ||
f"{self.browser_kind.name.capitalize()} browser driver is not supported" | ||
) | ||
sys.exit(1) | ||
|
||
if self._log_thread: | ||
self._log_thread.start() | ||
|
||
return self.driver | ||
|
||
def get_page(self, server_url, timeout=10): | ||
dr = self.get_driver() | ||
dr.get(server_url) | ||
self.wait_for_page_load(self.get_waiter(timeout)) | ||
|
||
def get_waiter(self, timeout): | ||
return WebDriverWait(self.get_driver(), timeout) | ||
|
||
# Function to wait for page to fully load | ||
def wait_for_page_load(self, waiter): | ||
waiter.until( | ||
lambda d: d.execute_script("return document.readyState") == "complete" | ||
) | ||
|
||
def wait_for_button(self, waiter, element_id): | ||
return waiter.until(EC.element_to_be_clickable((By.ID, element_id))) | ||
|
||
def wait_for_selection(self, waiter, element): | ||
return waiter.until(EC.element_to_be_selected(element)) | ||
|
||
def find_element(self, element_id): | ||
return self.get_driver().find_element(By.ID, element_id) | ||
|
||
def print_log(self, log_type): | ||
logs = self.get_driver().get_log(log_type) | ||
for log in logs: | ||
print(f"Console log: {log}") | ||
|
||
def _threaded_logs(self): | ||
while not self.shutting_down: | ||
self.print_log("browser") | ||
time.sleep(0.2) | ||
|
||
def refresh(self): | ||
self.get_driver().refresh() | ||
|
||
def quit(self): | ||
self.shutting_down = True | ||
if self._log_thread: | ||
self._log_thread.join() | ||
|
||
self.get_driver().quit() | ||
|
||
|
||
def run_test(driver, button_id): | ||
waiter = driver.get_waiter(10) | ||
|
||
# Wait for the button to be clickable before interacting with it | ||
button = driver.wait_for_button(waiter, button_id) | ||
button.click() # Click the button | ||
|
||
# Wait for the checkbox to be checked while printing logs | ||
checkbox_id = "testSuccess" # Replace with the actual checkbox ID | ||
|
||
try: | ||
checkbox = driver.find_element(checkbox_id) # Re-locate the checkbox element | ||
|
||
# Use a loop with a sleep to print logs every 2 seconds while waiting for the checkbox | ||
while True: | ||
try: | ||
checkbox = driver.wait_for_selection(waiter, checkbox) | ||
break # Exit loop when the checkbox is checked | ||
except TimeoutException: | ||
pass # Continue waiting if the checkbox is not checked | ||
|
||
time.sleep(2) # Pause for 2 seconds before checking the checkbox again | ||
|
||
# After the checkbox is confirmed to be checked, refresh the page | ||
driver.refresh() | ||
# Wait for the page to load after refresh | ||
driver.wait_for_page_load(waiter) | ||
|
||
except TimeoutException: | ||
print("timed out waiting for result checkbox to be checked") | ||
raise | ||
|
||
|
||
def start_web_server( | ||
command, working_directory, server_address, server_port, startup_timeout=30 | ||
): | ||
try: | ||
sock = socket.create_connection((server_address, server_port), timeout=2) | ||
except (TimeoutError, ConnectionRefusedError): | ||
# Nothing is alive at this URL, ignoring exception | ||
pass | ||
else: | ||
sock.close() | ||
raise ConnectionError( | ||
f"address and port already in use at ({server_address}, {server_port})" | ||
) | ||
|
||
proc = subprocess.Popen( | ||
command.split(), | ||
cwd=working_directory, | ||
stdout=subprocess.DEVNULL, | ||
start_new_session=True, | ||
) | ||
|
||
print("Starting web server") | ||
|
||
timeout_seconds = 0.5 | ||
start_date = datetime.datetime.now() | ||
while int((datetime.datetime.now() - start_date).total_seconds()) < startup_timeout: | ||
try: | ||
sock = socket.create_connection( | ||
(server_address, server_port), timeout=timeout_seconds | ||
) | ||
except TimeoutError: | ||
pass | ||
except ConnectionRefusedError: | ||
time.sleep(timeout_seconds) | ||
else: | ||
sock.close() | ||
break | ||
else: | ||
terminate_web_server(proc.pid) | ||
raise TimeoutError( | ||
f"timeout after {startup_timeout} seconds while waiting for web server" | ||
) | ||
|
||
return proc | ||
|
||
|
||
def terminate_web_server(pid): | ||
os.killpg(os.getpgid(pid), signal.SIGTERM) | ||
|
||
|
||
if __name__ == "__main__": | ||
args = parser.parse_args() | ||
browser_kind = BrowserKind[args.browser_kind] | ||
|
||
server_process = None | ||
if args.server_cmd: | ||
try: | ||
server_process = start_web_server( | ||
args.server_cmd, args.server_workdir, args.address, args.port | ||
) | ||
except Exception as err: | ||
print(f"Failed to start web server (error: {err})") | ||
sys.exit(1) | ||
|
||
print("Starting web driver") | ||
driver = Driver( | ||
args.browser_path, args.driver_path, browser_kind, threaded_logs=True | ||
) | ||
match browser_kind: | ||
case BrowserKind.chrome: | ||
driver.set_capability("goog:loggingPrefs", {"browser": "ALL"}) | ||
case _: | ||
# A no-op for browser that are not supported | ||
pass | ||
|
||
driver.get_page(f"http://{args.address}:{args.port}", timeout=10) | ||
|
||
failures = [] | ||
|
||
# TODO faire une boucle for ici avec tous les tests/benchs à exécuter | ||
try: | ||
run_test(driver, "compressedServerKeyBenchMessage2Carry2") | ||
print(f"SUCCESS: <TEST_NAME_TO_DISPLAY>") | ||
except Exception: # TODO Faut-il ratisser plus large que TimeoutException | ||
if args.fail_fast: | ||
print(f"FAIL: <TEST_NAME_TO_DISPLAY>") | ||
print("Fail fast is enabled, exiting") | ||
if server_process: | ||
server_process.terminate() | ||
sys.exit(1) | ||
else: | ||
# TODO stocker ici le nom du test ayant échoué | ||
pass | ||
|
||
print("Shutting down web driver") | ||
# Close the browser | ||
driver.quit() | ||
|
||
if server_process: | ||
print("Shutting down web server") | ||
terminate_web_server(server_process.pid) | ||
|
||
if failures: | ||
for fail in failures: | ||
print(f"FAIL: {fail}") | ||
sys.exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
selenium==4.24.0 |
Oops, something went wrong.