Skip to content

fix(rust): prevent empty driver version from being cached in metadata#17757

Open
MathiasPaulenko wants to merge 5 commits into
SeleniumHQ:trunkfrom
MathiasPaulenko:fix/sm-unit-tests-cache-bugs
Open

fix(rust): prevent empty driver version from being cached in metadata#17757
MathiasPaulenko wants to merge 5 commits into
SeleniumHQ:trunkfrom
MathiasPaulenko:fix/sm-unit-tests-cache-bugs

Conversation

@MathiasPaulenko

Copy link
Copy Markdown

Summary

Fixes a caching bug in Selenium Manager where Edge and Firefox cached an empty driver_version in metadata when a version-less HTTP response was received (e.g. misconfigured driver-mirror-url or intercepting proxy). Chrome already guarded against this with !driver_version.is_empty() — this PR applies the same guard to Edge and Firefox.

Root cause

In edge.rs and firefox.rs, the driver-version caching condition was:

if driver_ttl > 0 && !major_browser_version.is_empty() {

Chrome (chrome.rs) had the correct guard:

if driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty() {

Without the !driver_version.is_empty() check, a version-less HTTP 200 response would push an empty string into metadata.drivers, poisoning the cache for the entire TTL window (1 hour). Subsequent runs would find the empty version in metadata and fail to resolve a driver.

Changes

Bug fix

  • rust/src/edge.rs — added && !driver_version.is_empty() to caching guard
  • rust/src/firefox.rs — same fix

Unit tests (new coverage for issue #17641)

Before this PR, Selenium Manager had no unit tests for its pure logic — only integration tests requiring network and the built binary.

New test files:

File Coverage
rust/tests/version_unit_tests.rs parse_version (version extraction, error short-circuit, ESR/snap suffixes, trailing dot, garbage input), version predicates (is_stable, is_beta, is_dev, is_nightly, is_esr, is_version_specific), get_major_version, get_major_browser_version
rust/tests/config_unit_tests.rs str_to_os (valid/invalid OS parsing), OS::is (positive/negative matching), ARCH::is (all arch aliases)
rust/tests/cache_unit_tests.rs find_latest_from_cache (empty, single, multiple, lexical sort bug), collect_files_from_cache, is_driver_and_matches_browser_version (major match, empty-major bug), find_best_driver_from_cache (match, fallback, empty)

Inline #[cfg(test)] modules:

  • rust/src/lib.rsget_index_version (major/minor/patch extraction, out-of-bounds, empty string)
  • rust/src/config.rsget_env_name (simple key, dash-to-underscore, mixed case, empty suffix)

Tests documenting latent bugs (not fixed in this PR)

Two tests intentionally document existing bugs for future fixes:

  1. Lexical sort in find_latest_from_cachesort_by_file_name() orders 9.0.0 after 10.0.0 (lexical, not semver)
  2. starts_with("") in is_driver_and_matches_browser_version — when major_browser_version is empty, starts_with("") matches every cached driver

These tests pass today and serve as regression guards when the bugs are fixed.

Fixes #17641

Edge and Firefox cached empty driver_version in metadata when a
version-less HTTP response was received (e.g. misconfigured
driver-mirror-url or intercepting proxy). Chrome already guarded
against this with `!driver_version.is_empty()`. This change applies
the same guard to Edge and Firefox.

Also adds unit tests for previously untested pure logic:
- parse_version (version string parsing, error short-circuit, ESR/snap)
- get_index_version (major/minor/patch extraction, error cases)
- Version predicates (is_stable, is_beta, is_dev, is_nightly, is_esr,
  is_version_specific)
- get_major_version, get_major_browser_version
- str_to_os, OS::is, ARCH::is (config parsing)
- get_env_name (env var name generation)
- find_latest_from_cache, collect_files_from_cache
- is_driver_and_matches_browser_version (including empty-major bug)
- find_best_driver_from_cache (match, fallback, empty cache)

Tests document latent bugs: lexical sort misorders across digit
boundaries (9 vs 10), and starts_with("") matches all cached
drivers when major version is empty.

Fixes SeleniumHQ#17641
Copilot AI review requested due to automatic review settings July 9, 2026 10:23
@CLAassistant

CLAassistant commented Jul 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@selenium-ci selenium-ci added C-rust Rust code is mostly Selenium Manager B-manager Selenium Manager labels Jul 9, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

fix(rust): avoid caching empty driver versions for Edge/Firefox

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Prevent Edge/Firefox from caching empty driver versions after version-less HTTP responses.
• Add unit tests for version parsing, config parsing, and cache selection logic.
• Document two known cache-selection bugs with regression tests for future fixes.
Diagram

graph TD
  E(["EdgeManager"]) --> H{{"Driver version HTTP"}} --> V["driver_version"] -->|"cache if non-empty"| M[("se-metadata.json")]
  F(["FirefoxManager"]) --> H --> V
  M -->|"TTL lookup"| E
  M -->|"TTL lookup"| F
  UT["Unit tests"] --> L["files.rs/config.rs/lib.rs"] --> M

  subgraph Legend
    direction LR
    _mgr(["Manager"]) ~~~ _ext{{"External HTTP"}} ~~~ _db[("Metadata")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize validation in metadata write path
  • ➕ Guarantees no empty/invalid driver versions are persisted anywhere
  • ➕ Avoids repeating guards across manager implementations
  • ➖ Broader behavioral change; may mask upstream issues if silently dropped
  • ➖ Requires touching shared metadata/helpers and potentially more test updates
2. Represent driver version as Option and require Some(version) to cache
  • ➕ Type-level safety prevents empty-string caching by construction
  • ➕ Makes call sites explicit about missing/invalid versions
  • ➖ Larger refactor across interfaces and call sites
  • ➖ Higher churn/risk than a targeted fix

Recommendation: The chosen approach (adding a non-empty guard in Edge/Firefox to match Chrome) is the lowest-risk, highest-signal fix and aligns behavior across managers. Consider a follow-up to enforce invariants centrally (e.g., metadata write validation) to prevent similar issues from reappearing in new managers.

Files changed (7) +624 / -2

Bug fix (2) +4 / -2
edge.rsGuard against caching empty Edge driver versions +2/-1

Guard against caching empty Edge driver versions

• Tightens the metadata caching condition to require a non-empty driver_version, preventing cache poisoning when the version endpoint returns an empty string.

rust/src/edge.rs

firefox.rsGuard against caching empty GeckoDriver versions +2/-1

Guard against caching empty GeckoDriver versions

• Mirrors Chrome/Edge behavior by requiring driver_version to be non-empty before writing driver metadata, avoiding invalid cached entries on version-less responses.

rust/src/firefox.rs

Tests (5) +620 / -0
config.rsAdd unit tests for env var key generation +25/-0

Add unit tests for env var key generation

• Adds a local #[cfg(test)] module verifying get_env_name casing and dash-to-underscore normalization, including the empty-suffix edge case.

rust/src/config.rs

lib.rsAdd unit tests for get_index_version parsing +37/-0

Add unit tests for get_index_version parsing

• Adds #[cfg(test)] coverage for extracting version components and validating error paths (out-of-bounds index, empty input).

rust/src/lib.rs

cache_unit_tests.rsAdd unit tests for cache scanning and selection helpers +274/-0

Add unit tests for cache scanning and selection helpers

• Introduces tests for find_latest_from_cache/collect_files_from_cache and manager cache selection behavior (match, fallback, empty). Also includes tests that document known latent bugs (lexical sort misordering and empty-major matching).

rust/tests/cache_unit_tests.rs

config_unit_tests.rsAdd unit tests for OS/ARCH parsing and matching +99/-0

Add unit tests for OS/ARCH parsing and matching

• Adds rstest-based coverage for str_to_os, OS::is, and ARCH::is across valid aliases and invalid inputs.

rust/tests/config_unit_tests.rs

version_unit_tests.rsAdd unit tests for version parsing and version predicates +185/-0

Add unit tests for version parsing and version predicates

• Adds coverage for parse_version behaviors (extraction, trimming, error short-circuit, ESR/snap cases) plus manager version predicates and major-version helpers.

rust/tests/version_unit_tests.rs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Selenium Manager cache-poisoning bug where Edge/Firefox could cache an empty driver_version in metadata, aligning their caching guard with Chrome’s behavior. It also adds a set of Rust unit tests to improve deterministic coverage of version parsing, config parsing, and cache selection logic.

Changes:

  • Prevent caching empty driver_version values in edge.rs and firefox.rs metadata updates.
  • Add new Rust unit test suites under rust/tests/ for versions, config parsing, and cache logic.
  • Add inline unit tests in rust/src/lib.rs and rust/src/config.rs for helper functions.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rust/src/edge.rs Adds !driver_version.is_empty() guard before caching driver metadata.
rust/src/firefox.rs Adds !driver_version.is_empty() guard before caching driver metadata.
rust/tests/version_unit_tests.rs Adds unit tests for parse_version and version helper/predicate behavior.
rust/tests/config_unit_tests.rs Adds unit tests for OS/ARCH parsing and matching helpers.
rust/tests/cache_unit_tests.rs Adds unit tests for cache scanning and cache selection helpers.
rust/src/lib.rs Adds inline unit tests for get_index_version.
rust/src/config.rs Adds inline unit tests for get_env_name.

Comment thread rust/tests/cache_unit_tests.rs Outdated
Comment on lines +27 to +35
fn platform_label() -> (&'static str, &'static str, &'static str) {
if cfg!(windows) {
("win64", "windows", ".exe")
} else if cfg!(target_os = "macos") {
("mac-x64", "macos", "")
} else {
("linux64", "linux", "")
}
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in commit 015b4b3create_driver_in_cache now uses the manager's actual get_platform_label() and get_os() instead of hardcoded values, so tests work correctly on Apple Silicon (mac-arm64) and 32-bit Windows (win32).

Comment on lines +159 to +164
#[test]
fn get_major_version_errors_on_garbage() {
let manager = get_manager_by_browser("chrome".to_string()).unwrap();
let result = manager.get_major_version("not-a-version");
assert_eq!(result.unwrap(), "not-a-version");
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in commit 015b4b3 — renamed the test to get_major_version_returns_first_component_for_non_numeric to accurately reflect that get_index_version only splits by . and does not validate numeric content.

- Use manager's actual get_platform_label() instead of hardcoded
  values, so tests work on Apple Silicon (mac-arm64) and 32-bit
  Windows (win32)
- Rename get_major_version_errors_on_garbage to
  get_major_version_returns_first_component_for_non_numeric since
  the test asserts Ok, not Err
@qodo-code-review

qodo-code-review Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 14 rules

Grey Divider


Action required

1. No regression test for guard ✓ Resolved 📘 Rule violation ▣ Testability
Description
The Edge/Firefox metadata caching fix adds a new !driver_version.is_empty() guard, but this PR
does not add any automated test that would fail if that guard were removed. Without a regression
test, the empty-version cache poisoning bug could reappear unnoticed.
Code

rust/src/edge.rs[R255-259]

+                if driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty()
+                {
                    metadata.drivers.push(create_driver_metadata(
                        major_browser_version.as_str(),
                        self.driver_name,
Evidence
PR Compliance ID 389273 requires a regression test for bug fixes. The diff changes the metadata
caching condition in both EdgeManager and FirefoxManager, but the added/modified test code in
this PR does not exercise these caching paths or assert that empty driver_version values are not
written to metadata.

Rule 389273: Require tests for all new functionality and bug fixes
rust/src/edge.rs[255-264]
rust/src/firefox.rs[299-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The caching guard change (`!driver_version.is_empty()`) in Edge/Firefox is not covered by a regression test that would fail on the pre-fix behavior.

## Issue Context
This PR fixes a bug where a version-less HTTP response could cause an empty `driver_version` to be cached in metadata. To meet the testing requirement for bug fixes, add a test that simulates an empty driver-version response and asserts that metadata is not updated with an empty driver version.

## Fix Focus Areas
- rust/src/edge.rs[255-264]
- rust/src/firefox.rs[299-308]
- rust/tests/cache_unit_tests.rs[1-274]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. i686 normalized to x64 ✓ Resolved 🐞 Bug ≡ Correctness
Description
ARCH::X64 treats i686 as an x64 alias, so get_normalized_arch() normalizes an i686 arch to
x64 and downstream code selects 64-bit artifacts (e.g., Firefox selects linux-x86_64 instead of
linux-i686). The new unit test asserts this mapping, which will block correcting the behavior.
Code

rust/tests/config_unit_tests.rs[R72-81]

+#[rstest]
+#[case(X32, "x86")]
+#[case(X32, "i386")]
+#[case(X32, "x32")]
+#[case(X64, "x86_64")]
+#[case(X64, "amd64")]
+#[case(X64, "x64")]
+#[case(X64, "i686")]
+#[case(X64, "ia64")]
+#[case(ARM64, "arm64")]
Evidence
The Rust ARCH alias list explicitly includes i686 under X64; normalization uses that to return
ARCH_X64. Firefox download selection uses X32.is(arch) to choose the linux-i686 platform
label, implying i686 should be recognized as X32. The Java binding in this same repo also
classifies i686 under 32-bit X86.

rust/src/config.rs[188-202]
rust/src/lib.rs[1370-1384]
rust/src/firefox.rs[592-608]
java/src/org/openqa/selenium/Architecture.java[32-55]
java/src/org/openqa/selenium/Architecture.java[130-132]
rust/tests/config_unit_tests.rs[72-86]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`i686` is currently treated as an X64 alias in Rust, causing `get_normalized_arch()` to normalize `i686` to `x64`. This breaks 32-bit x86 handling and can select wrong platform labels/artifacts (Firefox has explicit `linux-i686` handling behind `X32.is(arch)`).

## Issue Context
This repo already treats `i686` as 32-bit x86 in other language bindings (Java `Architecture.X86`) and Rust Firefox download logic uses `X32` to select `linux-i686`.

## Fix Focus Areas
- rust/src/config.rs[188-202]
- rust/src/lib.rs[1370-1384]
- rust/tests/config_unit_tests.rs[72-86]
- rust/src/firefox.rs[592-608]

## What to change
1. In `ARCH::to_str_vector()` move `"i686"` from the `ARCH::X64` aliases into the `ARCH::X32` aliases (and consider whether `"ia64"` should remain X64).
2. Update `rust/tests/config_unit_tests.rs` to expect `i686` under `X32` (and keep `ia64` under `X64` if that remains desired).
3. (Optional but recommended) Add a focused unit test around `get_normalized_arch()` to ensure `i686` normalizes to `ARCH_X86`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Hardcoded platform label tests ✓ Resolved 🐞 Bug ☼ Reliability
Description
platform_label() in cache_unit_tests hardcodes macOS to mac-x64 and Windows to win64, but
managers compute platform labels from arch (e.g., mac-arm64, win32). On non-x64 runners these
tests will create cache paths that cannot satisfy is_driver()'s
path.contains(get_platform_label()) check, producing false failures.
Code

rust/tests/cache_unit_tests.rs[R27-35]

+fn platform_label() -> (&'static str, &'static str, &'static str) {
+    if cfg!(windows) {
+        ("win64", "windows", ".exe")
+    } else if cfg!(target_os = "macos") {
+        ("mac-x64", "macos", "")
+    } else {
+        ("linux64", "linux", "")
+    }
+}
Evidence
The new tests hardcode mac-x64 regardless of CPU, but ChromeManager::get_platform_label()
returns mac-arm64 on ARM64. The cache matching logic requires the platform label substring to be
present in the path, so mismatched labels will prevent any match.

rust/tests/cache_unit_tests.rs[27-54]
rust/src/chrome.rs[444-457]
rust/src/lib.rs[1035-1057]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test helper `platform_label()` hardcodes platform labels and ignores CPU architecture, while production code’s `get_platform_label()` is arch-sensitive. This makes cache tests brittle on ARM64 macOS and other non-x64 environments.

## Issue Context
`is_in_cache()`/`is_driver()` filters require that the cache path contains `self.get_platform_label()`. If the test writes files under `mac-x64` but the manager expects `mac-arm64`, no entries will match.

## Fix Focus Areas
- rust/tests/cache_unit_tests.rs[27-54]
- rust/src/chrome.rs[444-457]
- rust/src/lib.rs[1035-1057]

## What to change
- Replace `platform_label()` with logic derived from a real manager instance, e.g. create a manager (`get_manager_by_browser("chrome"...)`) and use:
 - `manager.get_platform_label()` for the platform dir
 - `manager.get_os()` (or `WINDOWS.is(...)`) to decide extension via `get_binary_extension()` or `get_driver_name_with_extension()`.
- Alternatively, make `platform_label()` consider `cfg!(target_arch = "aarch64")`/`cfg!(target_pointer_width = "32")` so it can emit `mac-arm64` / `win32` when appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Misnamed garbage-version test ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
get_major_version_errors_on_garbage unwraps a successful result and asserts the input string is
returned, contradicting its name and obscuring intended semantics. get_major_version delegates to
get_index_version, which does not validate numeric content.
Code

rust/tests/version_unit_tests.rs[R159-164]

+#[test]
+fn get_major_version_errors_on_garbage() {
+    let manager = get_manager_by_browser("chrome".to_string()).unwrap();
+    let result = manager.get_major_version("not-a-version");
+    assert_eq!(result.unwrap(), "not-a-version");
+}
Evidence
The test name says it should error, but it calls unwrap() and compares the returned value. The
implementation of get_major_version calls get_index_version(full_version, 0), which returns the
first split component without numeric validation, explaining why the test currently passes.

rust/tests/version_unit_tests.rs[159-164]
rust/src/lib.rs[1122-1124]
rust/src/lib.rs[1968-1973]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A test is named as if it expects an error on garbage input, but it asserts success. This makes it unclear whether garbage should be rejected or accepted.

## Issue Context
Current implementation returns the 0th `.`-split component, so `"not-a-version"` returns Ok("not-a-version").

## Fix Focus Areas
- rust/tests/version_unit_tests.rs[159-164]
- rust/src/lib.rs[1122-1124]
- rust/src/lib.rs[1968-1973]

## What to change
- Either rename the test to reflect current behavior (e.g., `get_major_version_returns_input_when_no_dots`) OR change the assertion to `assert!(result.is_err())` and update `get_index_version` to validate that the returned component is numeric (if rejecting garbage is the intended contract).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread rust/src/edge.rs Outdated
Comment thread rust/tests/config_unit_tests.rs
Comment thread rust/tests/cache_unit_tests.rs Outdated
Comment thread rust/tests/version_unit_tests.rs
@qodo-code-review

qodo-code-review Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 14 rules

Grey Divider


Action required

1. should_cache_driver_version lacks docs 📘 Rule violation ✧ Quality
Description
The new public function should_cache_driver_version has no Rust doc comment (///) even though it
is part of the crate’s public API surface. This violates the requirement to document public API
items with structured doc comments, including # Arguments and # Returns where applicable.
Code

rust/src/metadata.rs[R184-190]

+pub fn should_cache_driver_version(
+    driver_ttl: u64,
+    major_browser_version: &str,
+    driver_version: &str,
+) -> bool {
+    driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty()
+}
Evidence
PR Compliance ID 389230 requires /// doc comments for public API items, and structured `#
Arguments/# Returns sections for functions with parameters and non-()` return types. The added
pub fn should_cache_driver_version(...) -> bool has no preceding /// comment in the changed
lines.

Rule 389230: Document public API items with structured doc comments
rust/src/metadata.rs[184-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pub fn should_cache_driver_version(...) -> bool` is a public API item but has no `///` documentation. Per the checklist, public functions should have a brief summary plus structured sections when applicable.

## Issue Context
This function takes multiple parameters and returns a non-`()` value, so the doc comment should include `# Arguments` and `# Returns`.

## Fix Focus Areas
- rust/src/metadata.rs[184-190]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. String passed to helper 🐞 Bug ≡ Correctness
Description
EdgeManager::request_driver_version passes major_browser_version (a String) into
should_cache_driver_version, which requires &str, causing a compile-time type mismatch and
breaking the build.
Code

rust/src/edge.rs[R256-257]

+                if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)
+                {
Evidence
get_major_browser_version() returns String, and edge.rs stores it in major_browser_version
(then uses .as_str() elsewhere), while should_cache_driver_version is defined to take
major_browser_version: &str. Therefore the new call site must pass
major_browser_version.as_str() (or borrow it) to compile.

rust/src/edge.rs[197-206]
rust/src/edge.rs[252-260]
rust/src/lib.rs[1393-1406]
rust/src/metadata.rs[184-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)` passes a `String` where the helper requires `&str`, which is a compile error in Rust.

### Issue Context
In `edge.rs`, `major_browser_version` is a `String` returned from `get_major_browser_version()`, and the helper signature is `(&str, &str)` for the version parameters.

### Fix Focus Areas
- rust/src/edge.rs[252-264]

### Suggested change
Update the call to explicitly pass a string slice, e.g.:
- `should_cache_driver_version(driver_ttl, major_browser_version.as_str(), &driver_version)`
 (or `should_cache_driver_version(driver_ttl, &major_browser_version, &driver_version)` relying on deref coercion)

Keep the rest of the logic unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. No regression test for guard ✓ Resolved 📘 Rule violation ▣ Testability
Description
The Edge/Firefox metadata caching fix adds a new !driver_version.is_empty() guard, but this PR
does not add any automated test that would fail if that guard were removed. Without a regression
test, the empty-version cache poisoning bug could reappear unnoticed.
Code

rust/src/edge.rs[R255-259]

+                if driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty()
+                {
                    metadata.drivers.push(create_driver_metadata(
                        major_browser_version.as_str(),
                        self.driver_name,
Evidence
PR Compliance ID 389273 requires a regression test for bug fixes. The diff changes the metadata
caching condition in both EdgeManager and FirefoxManager, but the added/modified test code in
this PR does not exercise these caching paths or assert that empty driver_version values are not
written to metadata.

Rule 389273: Require tests for all new functionality and bug fixes
rust/src/edge.rs[255-264]
rust/src/firefox.rs[299-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The caching guard change (`!driver_version.is_empty()`) in Edge/Firefox is not covered by a regression test that would fail on the pre-fix behavior.

## Issue Context
This PR fixes a bug where a version-less HTTP response could cause an empty `driver_version` to be cached in metadata. To meet the testing requirement for bug fixes, add a test that simulates an empty driver-version response and asserts that metadata is not updated with an empty driver version.

## Fix Focus Areas
- rust/src/edge.rs[255-264]
- rust/src/firefox.rs[299-308]
- rust/tests/cache_unit_tests.rs[1-274]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. i686 normalized to x64 ✓ Resolved 🐞 Bug ≡ Correctness
Description
ARCH::X64 treats i686 as an x64 alias, so get_normalized_arch() normalizes an i686 arch to
x64 and downstream code selects 64-bit artifacts (e.g., Firefox selects linux-x86_64 instead of
linux-i686). The new unit test asserts this mapping, which will block correcting the behavior.
Code

rust/tests/config_unit_tests.rs[R72-81]

+#[rstest]
+#[case(X32, "x86")]
+#[case(X32, "i386")]
+#[case(X32, "x32")]
+#[case(X64, "x86_64")]
+#[case(X64, "amd64")]
+#[case(X64, "x64")]
+#[case(X64, "i686")]
+#[case(X64, "ia64")]
+#[case(ARM64, "arm64")]
Evidence
The Rust ARCH alias list explicitly includes i686 under X64; normalization uses that to return
ARCH_X64. Firefox download selection uses X32.is(arch) to choose the linux-i686 platform
label, implying i686 should be recognized as X32. The Java binding in this same repo also
classifies i686 under 32-bit X86.

rust/src/config.rs[188-202]
rust/src/lib.rs[1370-1384]
rust/src/firefox.rs[592-608]
java/src/org/openqa/selenium/Architecture.java[32-55]
java/src/org/openqa/selenium/Architecture.java[130-132]
rust/tests/config_unit_tests.rs[72-86]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`i686` is currently treated as an X64 alias in Rust, causing `get_normalized_arch()` to normalize `i686` to `x64`. This breaks 32-bit x86 handling and can select wrong platform labels/artifacts (Firefox has explicit `linux-i686` handling behind `X32.is(arch)`).

## Issue Context
This repo already treats `i686` as 32-bit x86 in other language bindings (Java `Architecture.X86`) and Rust Firefox download logic uses `X32` to select `linux-i686`.

## Fix Focus Areas
- rust/src/config.rs[188-202]
- rust/src/lib.rs[1370-1384]
- rust/tests/config_unit_tests.rs[72-86]
- rust/src/firefox.rs[592-608]

## What to change
1. In `ARCH::to_str_vector()` move `"i686"` from the `ARCH::X64` aliases into the `ARCH::X32` aliases (and consider whether `"ia64"` should remain X64).
2. Update `rust/tests/config_unit_tests.rs` to expect `i686` under `X32` (and keep `ia64` under `X64` if that remains desired).
3. (Optional but recommended) Add a focused unit test around `get_normalized_arch()` to ensure `i686` normalizes to `ARCH_X86`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Hardcoded platform label tests ✓ Resolved 🐞 Bug ☼ Reliability
Description
platform_label() in cache_unit_tests hardcodes macOS to mac-x64 and Windows to win64, but
managers compute platform labels from arch (e.g., mac-arm64, win32). On non-x64 runners these
tests will create cache paths that cannot satisfy is_driver()'s
path.contains(get_platform_label()) check, producing false failures.
Code

rust/tests/cache_unit_tests.rs[R27-35]

+fn platform_label() -> (&'static str, &'static str, &'static str) {
+    if cfg!(windows) {
+        ("win64", "windows", ".exe")
+    } else if cfg!(target_os = "macos") {
+        ("mac-x64", "macos", "")
+    } else {
+        ("linux64", "linux", "")
+    }
+}
Evidence
The new tests hardcode mac-x64 regardless of CPU, but ChromeManager::get_platform_label()
returns mac-arm64 on ARM64. The cache matching logic requires the platform label substring to be
present in the path, so mismatched labels will prevent any match.

rust/tests/cache_unit_tests.rs[27-54]
rust/src/chrome.rs[444-457]
rust/src/lib.rs[1035-1057]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test helper `platform_label()` hardcodes platform labels and ignores CPU architecture, while production code’s `get_platform_label()` is arch-sensitive. This makes cache tests brittle on ARM64 macOS and other non-x64 environments.

## Issue Context
`is_in_cache()`/`is_driver()` filters require that the cache path contains `self.get_platform_label()`. If the test writes files under `mac-x64` but the manager expects `mac-arm64`, no entries will match.

## Fix Focus Areas
- rust/tests/cache_unit_tests.rs[27-54]
- rust/src/chrome.rs[444-457]
- rust/src/lib.rs[1035-1057]

## What to change
- Replace `platform_label()` with logic derived from a real manager instance, e.g. create a manager (`get_manager_by_browser("chrome"...)`) and use:
 - `manager.get_platform_label()` for the platform dir
 - `manager.get_os()` (or `WINDOWS.is(...)`) to decide extension via `get_binary_extension()` or `get_driver_name_with_extension()`.
- Alternatively, make `platform_label()` consider `cfg!(target_arch = "aarch64")`/`cfg!(target_pointer_width = "32")` so it can emit `mac-arm64` / `win32` when appropriate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

6. Misnamed garbage-version test ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
get_major_version_errors_on_garbage unwraps a successful result and asserts the input string is
returned, contradicting its name and obscuring intended semantics. get_major_version delegates to
get_index_version, which does not validate numeric content.
Code

rust/tests/version_unit_tests.rs[R159-164]

+#[test]
+fn get_major_version_errors_on_garbage() {
+    let manager = get_manager_by_browser("chrome".to_string()).unwrap();
+    let result = manager.get_major_version("not-a-version");
+    assert_eq!(result.unwrap(), "not-a-version");
+}
Evidence
The test name says it should error, but it calls unwrap() and compares the returned value. The
implementation of get_major_version calls get_index_version(full_version, 0), which returns the
first split component without numeric validation, explaining why the test currently passes.

rust/tests/version_unit_tests.rs[159-164]
rust/src/lib.rs[1122-1124]
rust/src/lib.rs[1968-1973]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A test is named as if it expects an error on garbage input, but it asserts success. This makes it unclear whether garbage should be rejected or accepted.

## Issue Context
Current implementation returns the 0th `.`-split component, so `"not-a-version"` returns Ok("not-a-version").

## Fix Focus Areas
- rust/tests/version_unit_tests.rs[159-164]
- rust/src/lib.rs[1122-1124]
- rust/src/lib.rs[1968-1973]

## What to change
- Either rename the test to reflect current behavior (e.g., `get_major_version_returns_input_when_no_dots`) OR change the assertion to `assert!(result.is_err())` and update `get_index_version` to validate that the returned component is numeric (if rejecting garbage is the intended contract).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 15ef2a7

Results up to commit e6529d0


🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. should_cache_driver_version lacks docs 📘 Rule violation ✧ Quality
Description
The new public function should_cache_driver_version has no Rust doc comment (///) even though it
is part of the crate’s public API surface. This violates the requirement to document public API
items with structured doc comments, including # Arguments and # Returns where applicable.
Code

rust/src/metadata.rs[R184-190]

+pub fn should_cache_driver_version(
+    driver_ttl: u64,
+    major_browser_version: &str,
+    driver_version: &str,
+) -> bool {
+    driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty()
+}
Evidence
PR Compliance ID 389230 requires /// doc comments for public API items, and structured `#
Arguments/# Returns sections for functions with parameters and non-()` return types. The added
pub fn should_cache_driver_version(...) -> bool has no preceding /// comment in the changed
lines.

Rule 389230: Document public API items with structured doc comments
rust/src/metadata.rs[184-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pub fn should_cache_driver_version(...) -> bool` is a public API item but has no `///` documentation. Per the checklist, public functions should have a brief summary plus structured sections when applicable.

## Issue Context
This function takes multiple parameters and returns a non-`()` value, so the doc comment should include `# Arguments` and `# Returns`.

## Fix Focus Areas
- rust/src/metadata.rs[184-190]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. String passed to helper 🐞 Bug ≡ Correctness
Description
EdgeManager::request_driver_version passes major_browser_version (a String) into
should_cache_driver_version, which requires &str, causing a compile-time type mismatch and
breaking the build.
Code

rust/src/edge.rs[R256-257]

+                if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)
+                {
Evidence
get_major_browser_version() returns String, and edge.rs stores it in major_browser_version
(then uses .as_str() elsewhere), while should_cache_driver_version is defined to take
major_browser_version: &str. Therefore the new call site must pass
major_browser_version.as_str() (or borrow it) to compile.

rust/src/edge.rs[197-206]
rust/src/edge.rs[252-260]
rust/src/lib.rs[1393-1406]
rust/src/metadata.rs[184-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)` passes a `String` where the helper requires `&str`, which is a compile error in Rust.

### Issue Context
In `edge.rs`, `major_browser_version` is a `String` returned from `get_major_browser_version()`, and the helper signature is `(&str, &str)` for the version parameters.

### Fix Focus Areas
- rust/src/edge.rs[252-264]

### Suggested change
Update the call to explicitly pass a string slice, e.g.:
- `should_cache_driver_version(driver_ttl, major_browser_version.as_str(), &driver_version)`
 (or `should_cache_driver_version(driver_ttl, &major_browser_version, &driver_version)` relying on deref coercion)

Keep the rest of the logic unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

… guard, fix i686 arch classification

- Add empty_driver_version_is_not_cached_in_metadata regression test that
  fails if the !driver_version.is_empty() guard is removed from edge.rs/firefox.rs
- Move i686 from ARCH::X64 to ARCH::X32 aliases in config.rs so
  get_normalized_arch() correctly returns ARCH_X86 for 32-bit systems
- Update config_unit_tests.rs to expect i686 under X32 and verify
  X64 does not match i686
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit c7e86d0

Comment thread rust/src/metadata.rs
Comment on lines +184 to +190
pub fn should_cache_driver_version(
driver_ttl: u64,
major_browser_version: &str,
driver_version: &str,
) -> bool {
driver_ttl > 0 && !major_browser_version.is_empty() && !driver_version.is_empty()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. should_cache_driver_version lacks docs 📘 Rule violation ✧ Quality

The new public function should_cache_driver_version has no Rust doc comment (///) even though it
is part of the crate’s public API surface. This violates the requirement to document public API
items with structured doc comments, including # Arguments and # Returns where applicable.
Agent Prompt
## Issue description
`pub fn should_cache_driver_version(...) -> bool` is a public API item but has no `///` documentation. Per the checklist, public functions should have a brief summary plus structured sections when applicable.

## Issue Context
This function takes multiple parameters and returns a non-`()` value, so the doc comment should include `# Arguments` and `# Returns`.

## Fix Focus Areas
- rust/src/metadata.rs[184-190]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread rust/src/edge.rs
Comment on lines +256 to +257
if should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. String passed to helper 🐞 Bug ≡ Correctness

EdgeManager::request_driver_version passes major_browser_version (a String) into
should_cache_driver_version, which requires &str, causing a compile-time type mismatch and
breaking the build.
Agent Prompt
### Issue description
`should_cache_driver_version(driver_ttl, major_browser_version, &driver_version)` passes a `String` where the helper requires `&str`, which is a compile error in Rust.

### Issue Context
In `edge.rs`, `major_browser_version` is a `String` returned from `get_major_browser_version()`, and the helper signature is `(&str, &str)` for the version parameters.

### Fix Focus Areas
- rust/src/edge.rs[252-264]

### Suggested change
Update the call to explicitly pass a string slice, e.g.:
- `should_cache_driver_version(driver_ttl, major_browser_version.as_str(), &driver_version)`
  (or `should_cache_driver_version(driver_ttl, &major_browser_version, &driver_version)` relying on deref coercion)

Keep the rest of the logic unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit e6529d0

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 15ef2a7

@diemol diemol requested a review from bonigarcia July 10, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-manager Selenium Manager C-rust Rust code is mostly Selenium Manager

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Selenium Manager unit-tests

5 participants