Skip to content
Closed
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
2 changes: 2 additions & 0 deletions docs/developer/test-utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ Applies a number of settings by default to improve test reliability.

- Uses the ``CHROME_BIN`` environment variable to specify a custom
Chrome binary location.
- Uses the ``CHROMEDRIVER_LOG`` environment variable to enable verbose
ChromeDriver logging to ``chromedriver.log``.
- Uses ``page_load_strategy = "eager"`` to start interacting with the
page before it's fully loaded.
- Adds flags: ``--ignore-certificate-errors``, ``--no-sandbox``,
Expand Down
12 changes: 9 additions & 3 deletions openwisp_utils/tests/selenium.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,15 @@ def get_chrome_webdriver(cls):
# generate a unique port for each browser instance.
options.add_argument(f"--remote-debugging-port={free_port()}")
options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
return webdriver.Chrome(
options=options,
)
kwargs = dict(options=options)
# Optional: Store ChromeDriver logs in a file.
# Pass CHROMEDRIVER_LOG=1 when running tests.
CHROMEDRIVER_LOG = os.environ.get("CHROMEDRIVER_LOG", None)
if CHROMEDRIVER_LOG:
kwargs["service"] = webdriver.ChromeService(
service_args=["--verbose"], log_output="chromedriver.log"
)
Comment on lines +284 to +287

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Parallel test workers share a single chromedriver.log file.

When running with --parallel, each TestCase class calls get_chrome_webdriver() independently, but all write to the same hardcoded chromedriver.log. This can cause interleaved or corrupted log output. Consider incorporating a unique suffix (e.g., the free_port() already used for debugging ports) into the log filename when parallelism is detected. This is consistent with the existing geckodriver.log pattern, so it's a pre-existing concern, but the new code propagates it to Chrome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_utils/tests/selenium.py` around lines 284 - 287, Update
get_chrome_webdriver so CHROMEDRIVER_LOG uses a worker-unique log filename when
tests run in parallel, incorporating the existing free_port() debugging-port
value or equivalent unique suffix; preserve the current filename for
non-parallel runs and keep the change consistent with the geckodriver.log
handling.

return webdriver.Chrome(**kwargs)

def setUp(self):
self.admin = self._create_admin(
Expand Down
29 changes: 29 additions & 0 deletions tests/test_project/tests/test_selenium_mixin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import importlib
import os
import sys
from types import ModuleType
from unittest import TestResult, TestSuite, skip
from unittest.mock import patch

from django.conf import settings
from django.db.backends.base.base import BaseDatabaseWrapper
Expand All @@ -25,6 +27,33 @@ def setUp(self):
pass


class TestChromeWebDriverLogging(SimpleTestCase):
@patch("openwisp_utils.tests.selenium.webdriver.Chrome")
@patch("openwisp_utils.tests.selenium.webdriver.ChromeService")
def test_chromedriver_log_enables_verbose_file_logging(
self, chrome_service, chrome
):
with patch.dict(os.environ, {"CHROMEDRIVER_LOG": "1"}):
SeleniumTestMixin.get_chrome_webdriver()

chrome_service.assert_called_once_with(
service_args=["--verbose"], log_output="chromedriver.log"
)
chrome.assert_called_once_with(
options=chrome.call_args.kwargs["options"],
service=chrome_service.return_value,
)

@patch("openwisp_utils.tests.selenium.webdriver.Chrome")
@patch("openwisp_utils.tests.selenium.webdriver.ChromeService")
def test_chromedriver_log_is_opt_in(self, chrome_service, chrome):
with patch.dict(os.environ, {}, clear=True):
SeleniumTestMixin.get_chrome_webdriver()

chrome_service.assert_not_called()
chrome.assert_called_once_with(options=chrome.call_args.kwargs["options"])


class TestSeleniumMixinSkipHandling(SimpleTestCase):
def _run_skipped_standard_test(self):
class SkippedSeleniumTest(SeleniumTestMixin, SimpleTestCase):
Expand Down
Loading