'problems getting ThreadedTCPServer to work with selenium and report-portal
I've encountered a behavior that I simply do not understand. Implementing a ThreadedTCPServer with selenium was working fine until I decided to attach reportportal...
For the sake of simplicity - I've tried to boil down the code to the minimum necessary to reproduce the issue:
import http.server
import socketserver
from typing import Optional
from selenium.webdriver.chrome.webdriver import WebDriver
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def title_extractor(
headless: bool = True
):
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor() as executor:
host, port = "localhost", 8050
with ThreadedTCPServer((host, port), http.server.SimpleHTTPRequestHandler) as server:
print(f"serving at port {port}")
# Start a thread for the server
print('Starting server ...')
executor.submit(server.serve_forever)
# Start a thread for selenium
print('Starting page_worker ...')
future_nonce = executor.submit(page_worker, None, headless)
print('... page_worker done')
done_set, not_done_set = concurrent.futures.wait(
[future_nonce],
timeout=300,
return_when=concurrent.futures.ALL_COMPLETED
)
for future in done_set:
if future.done():
print('encountered future.done()')
pass
if future.cancelled():
print('encountered future.cancelled()')
pass
if future.exception(timeout=3):
print(f'encountered future.exception(): {str(future.exception())}')
result = None
continue
# raise RuntimeError(str(future.exception()))
print(f'future: {str(future)}')
result = future.result(timeout=3)
print('... future result finished')
print(f"Result:\t{result}")
print('Shutting down server ...')
server.shutdown()
executor.shutdown(wait=False)
print('... server and executor shut down')
return result
def page_worker(
driver: Optional[WebDriver] = None,
headless: bool = True
) -> Optional[str]:
import selenium
from selenium.webdriver.chrome.options import Options
terminate_driver = False
options = Options()
options.add_argument("--no-sandbox")
options.add_argument("--ignore-certificate-errors")
if headless:
options.add_argument("--headless")
# Start a new driver and stop it afterwards at the end of the worker call
if driver is None:
terminate_driver = True
url = "https://google.com"
print(f"Starting chrome driver with URL={url}\n"
f"headless={headless}\toptions={str(options)}")
driver = selenium.webdriver.Chrome(options=options)
driver.get(url)
print(f"driver={driver}")
title = driver.title
if terminate_driver:
print("Stopping driver")
driver.quit()
print(f"Worker terminating")
return title
def test__page_worker_with_report_portal():
title = title_extractor(headless=True)
print(f"Worker finished. Page title: {title}")
Here's the call (without reportportal integration) that works:
python3 -m pytest --show-capture=all -s -v ./Tests/Debugging/NonceWithRP.py
Here's the result:
(venv) bartoszc@bartoszclnx:~/PycharmProjects/ryd-tests$ python3 -m pytest --show-capture=all -s -v ./Tests/Debugging/NonceWithRP.py
MARKERS: {''}
======================================================= test session starts =======================================================
platform linux -- Python 3.8.10, pytest-7.1.1, pluggy-1.0.0 -- .../venv/bin/python3
cachedir: .pytest_cache
rootdir: ..., configfile: pytest.ini
plugins: allure-pytest-2.9.45, reportportal-5.1.0, repeat-0.9.1
collected 1 item
Tests/Debugging/NonceWithRP.py::test__page_worker_with_report_portal serving at port 8050
Starting server ...
Starting page_worker ...
Starting chrome driver with URL=https://google.com
headless=True options=<selenium.webdriver.chrome.options.Options object at 0x7fa3149e8eb0>... page_worker done
---------------------------------------------------------- live log call ----------------------------------------------------------
DEBUG 17:38:10: POST http://localhost:58501/session {"capabilities": {"firstMatch": [{}], "alwaysMatch": {"browserName": "chrome", "pageLoadStrategy": "normal", "goog:chromeOptions": {"extensions": [], "args": ["--no-sandbox", "--ignore-certificate-errors", "--headless"]}}}, "desiredCapabilities": {"browserName": "chrome", "pageLoadStrategy": "normal", "goog:chromeOptions": {"extensions": [], "args": ["--no-sandbox", "--ignore-certificate-errors", "--headless"]}}}
DEBUG 17:38:10: Starting new HTTP connection (1): localhost:58501
DEBUG 17:38:10: http://localhost:58501 "POST /session HTTP/1.1" 200 752
DEBUG 17:38:10: Finished Request
DEBUG 17:38:10: POST http://localhost:58501/session/cbf04df70cb13b87b74624492c5682ae/url {"url": "https://google.com"}
DEBUG 17:38:11: http://localhost:58501 "POST /session/cbf04df70cb13b87b74624492c5682ae/url HTTP/1.1" 200 14
DEBUG 17:38:11: Finished Request
driver=<selenium.webdriver.chrome.webdriver.WebDriver (session="cbf04df70cb13b87b74624492c5682ae")>
DEBUG 17:38:11: GET http://localhost:58501/session/cbf04df70cb13b87b74624492c5682ae/title {}
DEBUG 17:38:11: http://localhost:58501 "GET /session/cbf04df70cb13b87b74624492c5682ae/title HTTP/1.1" 200 18
DEBUG 17:38:11: Finished Request
Stopping driver
DEBUG 17:38:11: DELETE http://localhost:58501/session/cbf04df70cb13b87b74624492c5682ae {}
DEBUG 17:38:11: http://localhost:58501 "DELETE /session/cbf04df70cb13b87b74624492c5682ae HTTP/1.1" 200 14
DEBUG 17:38:11: Finished Request
Worker terminating
encountered future.done()
future: <Future at 0x7fa314b0e340 state=finished returned str>
... future result finished
Result: Google
Shutting down server ...
... server and executor shut down
Worker finished. Page title: Google
PASSED
Here's the call with reportportal integration, where it breaks:
python3 -m pytest --reportportal --show-capture=all -s -v ./Tests/Debugging/NonceWithRP.py
Here's the result:
MARKERS: {''}
------------------------------------------------------ live log sessionstart ------------------------------------------------------
DEBUG 17:41:37: ReportPortal - Init service: endpoint=...
DEBUG 17:41:37: Starting new HTTP connection (1): ...
DEBUG 17:41:37: ... "GET /api/v1/testchuck/settings HTTP/1.1" 200 None
DEBUG 17:41:37: ReportPortal - Start launch: request_body=...
DEBUG 17:41:37: ... "POST /api/v2/testchuck/launch HTTP/1.1" 201 None
DEBUG 17:41:37: start_launch - ID: ...
DEBUG 17:41:37: ReportPortal - Launch started: id=...
DEBUG 17:41:37: Starting new HTTPS connection (1): www.google-analytics.com:443
DEBUG 17:41:38: https://www.google-analytics.com:443 "POST /collect HTTP/1.1" 200 35
======================================================= test session starts =======================================================
platform linux -- Python 3.8.10, pytest-7.1.1, pluggy-1.0.0 -- .../venv/bin/python3
cachedir: .pytest_cache
rootdir: ..., configfile: pytest.ini
plugins: allure-pytest-2.9.45, reportportal-5.1.0, repeat-0.9.1
collected 1 item
------------------------------------------------------- live log collection -------------------------------------------------------
DEBUG 17:41:38: ReportPortal - Start TestItem: request_body={'attributes': [], 'name': 'Tests/Debugging/NonceWithRP.py::test__page_worker_with_report_portal', 'description': None, 'start_time': '1650555698079', 'item_type': 'STEP', 'code_ref': 'Tests/Debugging/NonceWithRP.py:test__page_worker_with_report_portal', 'parameters': None, 'parent_item_id': None, 'test_case_id': 'Tests/Debugging/NonceWithRP.py:test__page_worker_with_report_portal'}
DEBUG 17:41:38: ... "POST /api/v2/testchuck/item HTTP/1.1" 201 None
DEBUG 17:41:38: start_test_item - ID: ...
Tests/Debugging/NonceWithRP.py::test__page_worker_with_report_portal serving at port 8050
Starting server ...
Starting page_worker ...
Starting chrome driver with URL=https://google.com
headless=True options=<selenium.webdriver.chrome.options.Options object at 0x7fcac84b5370>
... page_worker done
---------------------------------------------------------- live log call ----------------------------------------------------------
DEBUG 17:41:38: POST http://localhost:56813/session {"capabilities": {"firstMatch": [{}], "alwaysMatch": {"browserName": "chrome", "pageLoadStrategy": "normal", "goog:chromeOptions": {"extensions": [], "args": ["--no-sandbox", "--ignore-certificate-errors", "--headless"]}}}, "desiredCapabilities": {"browserName": "chrome", "pageLoadStrategy": "normal", "goog:chromeOptions": {"extensions": [], "args": ["--no-sandbox", "--ignore-certificate-errors", "--headless"]}}}
encountered future.done()
encountered future.exception(): 'NoneType' object has no attribute 'log'
Shutting down server ...
... server and executor shut down
Worker finished. Page title: None
PASSED
--------------------------------------------------------- live log finish ---------------------------------------------------------
DEBUG 17:41:39: ReportPortal - Finish TestItem: request_body={'end_time': '1650555699144', 'status': 'PASSED', 'issue': None, 'item_id': '6647d85e-4c35-4798-acc5-17b18dcb5c48'}
DEBUG 17:41:39: ... "PUT /api/v2/testchuck/item/6647d85e-4c35-4798-acc5-17b18dcb5c48 HTTP/1.1" 200 None
DEBUG 17:41:39: finish_test_item - ID: 6647d85e-4c35-4798-acc5-17b18dcb5c48
DEBUG 17:41:39: response message: Accepted finish request for test item ID = 6647d85e-4c35-4798-acc5-17b18dcb5c48
----------------------------------------------------- live log sessionfinish ------------------------------------------------------
DEBUG 17:41:39: ReportPortal - Finish launch: request_body={'end_time': '1650555699384'}
DEBUG 17:41:39: ... "PUT /api/v2/testchuck/launch/1c4b1457-4e0a-4804-8749-391f77899385/finish HTTP/1.1" 200 None
DEBUG 17:41:39: finish_launch - ID: 1c4b1457-4e0a-4804-8749-391f77899385
DEBUG 17:41:39: response message: <reportportal_client.static.defines._PresenceSentinel object at 0x7fcac9ed9cd0>
DEBUG 17:41:39: Waiting for worker <reportportal_client.core.worker.APIWorker object at 0x7fcac8e27550> to completeprocessing batches.
DEBUG 17:41:39: [APIWorker] Received {ControlCommand.STOP} command
DEBUG 17:41:39: [APIWorker] Processing {ControlCommand.STOP} command
DEBUG 17:41:39: [APIWorker] Exiting due to {ControlCommand.STOP} command
It seems that there is a problem with the logging that seems to be invoked by the web-driver. What I don't understand is - why does the future_nonce terminate as 'done' when the log.debug messages begin - and only after the first debug message?
For the sake of what I need to do in the end - I do need to have the multi-threaded solution, where one thread serves the page, another thread operates selenium automation on the page. The code above is just a minimal implementation to reproduce the issue.
Could someone please help me in obtaining the same behavior (meaning: a complete execution of the page_worker, returning the page title), but with reportportal integration in this 'infrastructure' ?
Solution 1:[1]
This was a bug inside Report Portal client which is fixed in version 5.2.2 Please use the latest version of the client: https://pypi.org/project/reportportal-client/
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | HardNorth |