Skip to content

[rb] fix tests and custom matchers to work with SE_DEBUG - #17863

Merged
titusfortner merged 2 commits into
SeleniumHQ:trunkfrom
titusfortner:rb-se-debug-matchers
Aug 3, 2026
Merged

[rb] fix tests and custom matchers to work with SE_DEBUG#17863
titusfortner merged 2 commits into
SeleniumHQ:trunkfrom
titusfortner:rb-se-debug-matchers

Conversation

@titusfortner

@titusfortner titusfortner commented Aug 3, 2026

Copy link
Copy Markdown
Member

🔗 Related Issues

Fix regression to custom RSpec matchers from change in #16901
Fix regression to test suite when switching from mocks to custom matchers in #17848
Simplify rspec around logic from #17412

💥 What does this PR do?

  • Fixes IO handling so RSpec matchers work under SE_DEBUG again
  • Fixes bug that prevented Ruby unit test suite from passing with SE_DEBUG enabled

🔧 Implementation Notes

  • SE_DEBUG locks the logger output to prevent runtime overrides; the test suite has no such need, so the custom matchers drop the lock to capture the output
  • Updated test assertions to be dynamic based on whether SE_DEBUG is set rather than modifying the variables unnecessarily
  • Fixed places where SE_DEBUG variable was unilaterally deleted in specs to restore original value (from [rb] output driver logs by default when debug is enabled #16901)
  • Test-only change; no production code touched.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: matcher capture fix and service-spec assertion/hook updates
    • I reviewed all AI output and can explain the change

🔄 Types of changes

  • Bug fix (backwards compatible)

@selenium-ci selenium-ci added the C-rb Ruby Bindings label Aug 3, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Fix RSpec log matcher capture under SE_DEBUG and make service specs env-agnostic

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Restore custom RSpec log matchers when SE_DEBUG forces logger output to stderr.
• Make service spec expectations include SE_DEBUG debug flags when applicable.
• Preserve and restore SE_DEBUG in specs instead of unconditionally deleting it.
Diagram

graph TD
  env["SE_DEBUG env"] --> logger["WebDriver::Logger"] --> matcher["RSpec log matchers"] --> lines["Captured log lines"]
  env --> svc["Service arg builder"] --> specs["Service specs"]
  matcher --> specs
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add a public Logger capture/override helper
  • ➕ Avoids reaching into @output_forced via instance_variable_get/set
  • ➕ Centralizes the “temporarily override output” behavior for future tests/tools
  • ➖ Requires production-code change for a test-only regression
  • ➖ Needs careful API design to avoid weakening SE_DEBUG guarantees
2. Clear SE_DEBUG only for matcher capture blocks
  • ➕ No dependence on logger internals
  • ➕ Keeps capture logic simple
  • ➖ Makes specs less representative of real SE_DEBUG behavior
  • ➖ Harder to reason about when different parts of an example run with different env
3. Capture stderr at the process/spec level instead of logger output
  • ➕ Doesn’t mutate Selenium logger state
  • ➕ Works even if output forcing mechanism changes
  • ➖ More brittle/noisy: captures unrelated output
  • ➖ Harder to filter to only Selenium logger lines

Recommendation: Given the stated constraint of keeping changes test-only, the current approach (temporarily disabling the logger’s output lock and restoring it) is a pragmatic fix. If SE_DEBUG-related issues recur, consider promoting this into a small, explicit Logger API for temporary output overrides to avoid tests depending on internal instance variables.

Files changed (6) +37 / -28

Bug fix (1) +6 / -1
rspec_matchers.rbTemporarily unforce Selenium logger output to capture matcher logs under SE_DEBUG +6/-1

Temporarily unforce Selenium logger output to capture matcher logs under SE_DEBUG

• Updates log-capturing matchers to bypass the logger’s SE_DEBUG output lock by toggling the internal @output_forced flag during capture. Ensures the original logger output and forced state are restored in an ensure block.

rb/spec/rspec_matchers.rb

Tests (5) +31 / -27
service_spec.rbMake Chrome service spec expectations conditional on SE_DEBUG +6/-4

Make Chrome service spec expectations conditional on SE_DEBUG

• Introduces a debug_args helper and updates expectations so default args and log-path args include SE_DEBUG’s --verbose when set. Adjusts the SE_DEBUG around hook to restore any preexisting SE_DEBUG value instead of always deleting it.

rb/spec/unit/selenium/webdriver/chrome/service_spec.rb

service_spec.rbAlign service shortcut specs with SE_DEBUG-added debug flags +8/-4

Align service shortcut specs with SE_DEBUG-added debug flags

• Adds a small helper to append the appropriate debug flag only when SE_DEBUG is set. Updates expectations for Chrome, Edge, Firefox, and IE shortcut constructors to include debug args while keeping Safari unchanged.

rb/spec/unit/selenium/webdriver/common/service_spec.rb

service_spec.rbMake Edge service spec expectations conditional on SE_DEBUG +8/-5

Make Edge service spec expectations conditional on SE_DEBUG

• Adds debug_args and updates expectations for default args, log-path args, and user-provided args to account for --verbose under SE_DEBUG. Updates the SE_DEBUG around hook to restore the prior environment value and adjusts duplicated log-path assertions accordingly.

rb/spec/unit/selenium/webdriver/edge/service_spec.rb

service_spec.rbStop forcibly clearing SE_DEBUG and assert Firefox debug args dynamically +4/-11

Stop forcibly clearing SE_DEBUG and assert Firefox debug args dynamically

• Removes the previous around hook that unconditionally deleted SE_DEBUG for the spec. Adds debug_args and updates count/equality expectations so -v is included only when SE_DEBUG is present.

rb/spec/unit/selenium/webdriver/firefox/service_spec.rb

service_spec.rbMake IE service spec expectations conditional on SE_DEBUG and restore env +5/-3

Make IE service spec expectations conditional on SE_DEBUG and restore env

• Adds debug_args and updates expectations so default and user-provided args include the SE_DEBUG --log-level=DEBUG flag when set. Updates the SE_DEBUG around hook to restore any original value rather than unconditionally deleting it.

rb/spec/unit/selenium/webdriver/ie/service_spec.rb

@qodo-code-review

qodo-code-review Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. SE_DEBUG lock disabled too long 🐞 Bug ≡ Correctness
Description
In capture_log_lines, @output_forced is set to false for the entire yielded block, so code under
test can successfully call Logger#output= even though SE_DEBUG is intended to make output overrides
a no-op. This can make SE_DEBUG spec runs diverge from real runtime behavior and can redirect logs
away from the matcher capture.
Code

rb/spec/rspec_matchers.rb[R78-80]

+      output_forced = Selenium::WebDriver.logger.instance_variable_get(:@output_forced)
+      Selenium::WebDriver.logger.instance_variable_set(:@output_forced, false) if output_forced
      Selenium::WebDriver.logger.output = io
Evidence
SE_DEBUG forces logger output and blocks output overrides via the @output_forced guard, but the
matcher clears that guard across the entire block it is asserting on, enabling output overrides
during the test subject execution.

rb/spec/rspec_matchers.rb[71-89]
rb/lib/selenium/webdriver/common/logger.rb[70-103]
rb/lib/selenium/webdriver.rb[97-104]

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

### Issue description
`capture_log_lines` clears `Selenium::WebDriver.logger`'s `@output_forced` flag for the entire duration of the yielded block. Under `SE_DEBUG`, this changes the semantics of `Logger#output=` inside the code under test (it becomes effective when it should be ignored), which can lead to behavior differences and lost/redirected captured output.

### Issue Context
- `SE_DEBUG` causes the logger to call `stderr!`, which sets `@output_forced = true` and makes `Logger#output=` a guarded no-op.
- The matcher only needs the lock cleared briefly to swap output to a `StringIO` and later restore it.

### Fix Focus Areas
- rb/spec/rspec_matchers.rb[74-90]

### Suggested fix
1. Save the original `@output_forced` value.
2. Temporarily clear it only to set `logger.output = io`, then immediately restore it to the original value *before* `yield`.
3. In `ensure`, temporarily clear it again only long enough to restore `logger.output = default_output`, then restore the original `@output_forced` value.
4. (Optional) Add/extend a unit spec to cover `SE_DEBUG` behavior where code inside the block attempts `WebDriver.logger.output = ...` and verify it still behaves as forced (ignored) while capture still works.

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



Informational

2. Brittle logger ivar access 🐞 Bug ⚙ Maintainability
Description
capture_log_lines disables SE_DEBUG’s forced-output behavior by directly mutating
Selenium::WebDriver.logger’s private @output_forced instance variable, tightly coupling this matcher
to Logger internals. If the logger enforcement mechanism changes, log capture behavior may regress
or become flaky in future refactors/upgrades.
Code

rb/spec/rspec_matchers.rb[78]

+      Selenium::WebDriver.logger.instance_variable_set(:@output_forced, false)
Evidence
The matcher now directly sets @output_forced, which is an internal guard used by
Selenium::WebDriver::Logger#output= to ignore output overrides when forced (e.g., via #stderr!).
This makes the spec helper dependent on a private internal variable name and enforcement mechanism.

rb/spec/rspec_matchers.rb[71-87]
rb/lib/selenium/webdriver/common/logger.rb[70-103]

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

## Issue description
`capture_log_lines` reaches into `Selenium::WebDriver::Logger` internals via `instance_variable_set(:@output_forced, false)`, which is brittle and couples specs to a private implementation detail.

## Issue Context
`Selenium::WebDriver::Logger#output=` gates changes on `@output_forced`, which is set by `#stderr!` under `SE_DEBUG`. The matcher currently toggles that ivar directly.

## Fix Focus Areas
- rb/spec/rspec_matchers.rb[71-87]
- rb/lib/selenium/webdriver/common/logger.rb[70-103]

## Suggested fix
Prefer a supported API over raw ivar mutation, e.g. add a small (possibly @api private) helper on `Selenium::WebDriver::Logger` such as `with_unforced_output { ... }` or `unforce_output!` and use it from `capture_log_lines` to temporarily allow output redirection during matcher capture.

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


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Previous review results

Review updated until commit 2a0ac18 ⚖️ Balanced

Results up to commit 9c6c06a ⚖️ Balanced


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


Remediation recommended
1. SE_DEBUG lock disabled too long 🐞 Bug ≡ Correctness
Description
In capture_log_lines, @output_forced is set to false for the entire yielded block, so code under
test can successfully call Logger#output= even though SE_DEBUG is intended to make output overrides
a no-op. This can make SE_DEBUG spec runs diverge from real runtime behavior and can redirect logs
away from the matcher capture.
Code

rb/spec/rspec_matchers.rb[R78-80]

+      output_forced = Selenium::WebDriver.logger.instance_variable_get(:@output_forced)
+      Selenium::WebDriver.logger.instance_variable_set(:@output_forced, false) if output_forced
      Selenium::WebDriver.logger.output = io
Evidence
SE_DEBUG forces logger output and blocks output overrides via the @output_forced guard, but the
matcher clears that guard across the entire block it is asserting on, enabling output overrides
during the test subject execution.

rb/spec/rspec_matchers.rb[71-89]
rb/lib/selenium/webdriver/common/logger.rb[70-103]
rb/lib/selenium/webdriver.rb[97-104]

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

### Issue description
`capture_log_lines` clears `Selenium::WebDriver.logger`'s `@output_forced` flag for the entire duration of the yielded block. Under `SE_DEBUG`, this changes the semantics of `Logger#output=` inside the code under test (it becomes effective when it should be ignored), which can lead to behavior differences and lost/redirected captured output.

### Issue Context
- `SE_DEBUG` causes the logger to call `stderr!`, which sets `@output_forced = true` and makes `Logger#output=` a guarded no-op.
- The matcher only needs the lock cleared briefly to swap output to a `StringIO` and later restore it.

### Fix Focus Areas
- rb/spec/rspec_matchers.rb[74-90]

### Suggested fix
1. Save the original `@output_forced` value.
2. Temporarily clear it only to set `logger.output = io`, then immediately restore it to the original value *before* `yield`.
3. In `ensure`, temporarily clear it again only long enough to restore `logger.output = default_output`, then restore the original `@output_forced` value.
4. (Optional) Add/extend a unit spec to cover `SE_DEBUG` behavior where code inside the block attempts `WebDriver.logger.output = ...` and verify it still behaves as forced (ignored) while capture still works.

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


Results up to commit d53d3c4 ⚖️ Balanced


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


Informational
1. Brittle logger ivar access 🐞 Bug ⚙ Maintainability
Description
capture_log_lines disables SE_DEBUG’s forced-output behavior by directly mutating
Selenium::WebDriver.logger’s private @output_forced instance variable, tightly coupling this matcher
to Logger internals. If the logger enforcement mechanism changes, log capture behavior may regress
or become flaky in future refactors/upgrades.
Code

rb/spec/rspec_matchers.rb[78]

+      Selenium::WebDriver.logger.instance_variable_set(:@output_forced, false)
Evidence
The matcher now directly sets @output_forced, which is an internal guard used by
Selenium::WebDriver::Logger#output= to ignore output overrides when forced (e.g., via #stderr!).
This makes the spec helper dependent on a private internal variable name and enforcement mechanism.

rb/spec/rspec_matchers.rb[71-87]
rb/lib/selenium/webdriver/common/logger.rb[70-103]

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

## Issue description
`capture_log_lines` reaches into `Selenium::WebDriver::Logger` internals via `instance_variable_set(:@output_forced, false)`, which is brittle and couples specs to a private implementation detail.

## Issue Context
`Selenium::WebDriver::Logger#output=` gates changes on `@output_forced`, which is set by `#stderr!` under `SE_DEBUG`. The matcher currently toggles that ivar directly.

## Fix Focus Areas
- rb/spec/rspec_matchers.rb[71-87]
- rb/lib/selenium/webdriver/common/logger.rb[70-103]

## Suggested fix
Prefer a supported API over raw ivar mutation, e.g. add a small (possibly @api private) helper on `Selenium::WebDriver::Logger` such as `with_unforced_output { ... }` or `unforce_output!` and use it from `capture_log_lines` to temporarily allow output redirection during matcher capture.

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


Qodo Logo

Comment thread rb/spec/rspec_matchers.rb Outdated
Comment thread rb/spec/rspec_matchers.rb
@qodo-code-review

Copy link
Copy Markdown
Contributor

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

@titusfortner
titusfortner force-pushed the rb-se-debug-matchers branch from d53d3c4 to 2a0ac18 Compare August 3, 2026 02:40
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2a0ac18

@titusfortner titusfortner changed the title [rb] capture log matcher output under SE_DEBUG and keep service specs env-agnostic [rb] fix tests and custom matchers to work with SE_DEBUG Aug 3, 2026
@titusfortner
titusfortner merged commit 014d72b into SeleniumHQ:trunk Aug 3, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-rb Ruby Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants