Skip to content

[rb] Allow to set environment variables when staring a browser#17395

Open
y-yagi wants to merge 1 commit intoSeleniumHQ:trunkfrom
y-yagi:allow_to_set_env
Open

[rb] Allow to set environment variables when staring a browser#17395
y-yagi wants to merge 1 commit intoSeleniumHQ:trunkfrom
y-yagi:allow_to_set_env

Conversation

@y-yagi
Copy link
Copy Markdown
Contributor

@y-yagi y-yagi commented Apr 26, 2026

🔗 Related Issues

None

💥 What does this PR do?

Currently, the environment variables used when launching a browser are the same as those of the Ruby process that performs the launch.

However, there are times when want to change the environment variables passed to a browser. In our case, LD_PRELOAD. We want to set LD_PRELOAD to use jemalloc in Ruby, but we want to avoid setting it for Chrome because using jemalloc can cause crashes or unstable behavior.

To address this issue, this PR fixes allow environment variables to be explicitly specified via the service.

🔧 Implementation Notes

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Copilot agent
    • What was generated: all codes are generated by AI
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

Copilot AI review requested due to automatic review settings April 26, 2026 03:44
@selenium-ci selenium-ci added the C-rb Ruby Bindings label Apr 26, 2026
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Allow setting environment variables when starting a browser

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add ability to set environment variables when starting browser
• Allow explicit env variable configuration via Service class
• Pass env variables through ChildProcess to Process.spawn
• Add comprehensive unit tests for env variable handling

Grey Divider

File Changes

1. rb/lib/selenium/webdriver/common/child_process.rb ✨ Enhancement +3/-2

Add environment variable support to ChildProcess

• Add env attribute accessor to ChildProcess class
• Initialize @env instance variable as empty hash
• Pass @env hash as first argument to Process.spawn

rb/lib/selenium/webdriver/common/child_process.rb


2. rb/lib/selenium/webdriver/common/service.rb ✨ Enhancement +3/-2

Add environment variable parameter to Service

• Add env attribute accessor to Service class
• Add env parameter to initialize method with default nil
• Initialize @env instance variable with provided env or empty hash

rb/lib/selenium/webdriver/common/service.rb


3. rb/lib/selenium/webdriver/common/service_manager.rb ✨ Enhancement +2/-0

Pass environment variables to ChildProcess

• Extract env from config and store in instance variable
• Pass @env to ChildProcess instance in build_process method

rb/lib/selenium/webdriver/common/service_manager.rb


View more (2)
4. rb/spec/unit/selenium/webdriver/chrome/service_spec.rb 🧪 Tests +6/-0

Add Chrome service environment variable test

• Add test case verifying Service accepts and stores env parameter
• Test validates environment variables are properly set on service instance

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


5. rb/spec/unit/selenium/webdriver/common/child_process_spec.rb 🧪 Tests +25/-0

Add ChildProcess environment variable tests

• Add test verifying Process.spawn receives env variables correctly
• Add test verifying empty hash passed to Process.spawn by default
• Tests use mocking to verify spawn method receives correct arguments

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


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 26, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Mocks used in child_process_spec 📘 Rule violation ⚙ Maintainability
Description
The newly added unit tests stub Process.spawn and Process.detach, which violates the requirement
to avoid mocks in tests and can reduce fidelity to real behavior.
Code

rb/spec/unit/selenium/webdriver/common/child_process_spec.rb[R30-36]

+          allow(Process).to receive(:spawn).and_return(12_345)
+          allow(Process).to receive(:detach)
+
+          process.start
+
+          expect(Process).to have_received(:spawn).with({'FOO' => 'bar'}, 'echo', 'test', anything)
+        end
Evidence
PR Compliance ID 6 requires avoiding mocks in tests. The added test uses `allow(Process).to
receive(...) and then asserts have_received`, which is mock-based testing.

AGENTS.md
rb/spec/unit/selenium/webdriver/common/child_process_spec.rb[30-36]

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 new unit tests for `ChildProcess#start` use mocks/stubs (`allow(Process).to receive(...)`) and mock assertions (`have_received`), which violates the project's guidance to avoid mocks in tests.

## Issue Context
This PR adds support for passing environment variables to `Process.spawn`. The tests currently validate this by stubbing Ruby's `Process` module.

## Fix Focus Areas
- rb/spec/unit/selenium/webdriver/common/child_process_spec.rb[25-47]

## Suggested direction
- Prefer an integration-like unit test that executes a very small real command and inspects its output/environment (e.g., spawn a Ruby one-liner that prints `ENV['FOO']`) rather than mocking `Process.spawn`.
- If platform differences are a concern, gate the test appropriately (e.g., skip on Windows if needed) while still avoiding mocks.

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



Remediation recommended

2. env input not validated 📘 Rule violation ☼ Reliability
Description
Service#initialize accepts env: and passes it down to Process.spawn without validating
type/shape, which can lead to unclear runtime errors if callers pass a non-Hash or invalid values.
Code

rb/lib/selenium/webdriver/common/service.rb[R69-72]

+      def initialize(path: nil, port: nil, log: nil, args: nil, env: nil)
        port ||= self.class::DEFAULT_PORT
        args ||= []
        path ||= env_path
Evidence
PR Compliance ID 11 requires early validation of configuration inputs with clear exceptions. The new
env: parameter is accepted and stored as @env = env || {} without checking that env is a
Hash suitable for Process.spawn.

rb/lib/selenium/webdriver/common/service.rb[69-86]
Best Practice: Learned patterns

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

## Issue description
`env:` is a new public-ish configuration input on `Service#initialize`, but it is not validated. If a caller passes an unexpected type (e.g., String, Array, nil-ish objects) or invalid env keys/values, failures will occur later (in `Process.spawn`) with non-actionable errors.

## Issue Context
The new feature is intended to let users pass environment variables to the driver/browser process. This is a configuration surface and should fail fast with a clear exception message when misused.

## Fix Focus Areas
- rb/lib/selenium/webdriver/common/service.rb[69-86]

## Suggested direction
- Add a type/shape check such as:
 - raise `ArgumentError, "env must be a Hash"` unless `env.nil? || env.is_a?(Hash)`
- Optionally validate that env keys/values are Strings (or respond to `to_str`) to match Ruby `Process.spawn` expectations, and raise with an actionable message if not.

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


3. Safari env unsupported 🐞 Bug ≡ Correctness
Description
The base Service now supports an env: keyword, but Safari::Service overrides initialize
without env:/**, so passing env: when constructing Safari’s service raises ArgumentError and
the feature can’t be used for Safari.
Code

rb/lib/selenium/webdriver/common/service.rb[69]

+      def initialize(path: nil, port: nil, log: nil, args: nil, env: nil)
Evidence
The PR adds env: support to the base Service initializer and exposes env via an accessor.
However, Safari::Service defines its own initialize signature without env: (and without **
to accept/forward unknown keywords), so callers cannot use the new option for Safari services.

rb/lib/selenium/webdriver/common/service.rb[60-89]
rb/lib/selenium/webdriver/safari/service.rb[23-32]

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

### Issue description
`Safari::Service` overrides `initialize` but does not accept/forward the new `env:` keyword supported by the base `WebDriver::Service`, preventing env customization for Safari and causing `ArgumentError` if `env:` is provided.

### Issue Context
Base `WebDriver::Service#initialize` now takes `env:` and stores it, and `ServiceManager` will use it to set the child process environment.

### Fix Focus Areas
- rb/lib/selenium/webdriver/safari/service.rb[23-32]
- rb/lib/selenium/webdriver/common/service.rb[60-89]

### Suggested fix
- Update `Safari::Service#initialize` to accept `env: nil` (or `**`), and call `super` with the forwarded keywords while preserving the existing `log`-unsupported behavior.
- (Optional) Add/extend a unit spec for `Safari::Service.new(env: {...})` verifying `service.env` is set.

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


Grey Divider

Qodo Logo

Comment on lines +30 to +36
allow(Process).to receive(:spawn).and_return(12_345)
allow(Process).to receive(:detach)

process.start

expect(Process).to have_received(:spawn).with({'FOO' => 'bar'}, 'echo', 'test', anything)
end
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. Mocks used in child_process_spec 📘 Rule violation ⚙ Maintainability

The newly added unit tests stub Process.spawn and Process.detach, which violates the requirement
to avoid mocks in tests and can reduce fidelity to real behavior.
Agent Prompt
## Issue description
The new unit tests for `ChildProcess#start` use mocks/stubs (`allow(Process).to receive(...)`) and mock assertions (`have_received`), which violates the project's guidance to avoid mocks in tests.

## Issue Context
This PR adds support for passing environment variables to `Process.spawn`. The tests currently validate this by stubbing Ruby's `Process` module.

## Fix Focus Areas
- rb/spec/unit/selenium/webdriver/common/child_process_spec.rb[25-47]

## Suggested direction
- Prefer an integration-like unit test that executes a very small real command and inspects its output/environment (e.g., spawn a Ruby one-liner that prints `ENV['FOO']`) rather than mocking `Process.spawn`.
- If platform differences are a concern, gate the test appropriately (e.g., skip on Windows if needed) while still avoiding mocks.

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support in the Ruby bindings to pass an explicit environment hash into the driver service process launch, enabling callers to avoid inheriting (or to override) the Ruby process environment when starting a browser via its driver.

Changes:

  • Add env configuration to WebDriver::Service and propagate it through ServiceManager into ChildProcess.
  • Update ChildProcess to pass the env hash as the first argument to Process.spawn.
  • Add unit tests covering env passing in ChildProcess#start and Chrome::Service initialization.

Reviewed changes

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

Show a summary per file
File Description
rb/lib/selenium/webdriver/common/service.rb Introduces env attribute/keyword on the base Service configuration.
rb/lib/selenium/webdriver/common/service_manager.rb Passes the configured env through to the spawned child process.
rb/lib/selenium/webdriver/common/child_process.rb Uses Process.spawn(env, *command, options) to apply env overrides.
rb/spec/unit/selenium/webdriver/common/child_process_spec.rb Adds specs asserting Process.spawn receives the env hash (and {} by default).
rb/spec/unit/selenium/webdriver/chrome/service_spec.rb Adds a spec asserting Chrome::Service retains the provided env.

Comment on lines +69 to 70
def initialize(path: nil, port: nil, log: nil, args: nil, env: nil)
port ||= self.class::DEFAULT_PORT
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

Service now accepts an env: keyword, but at least one concrete subclass (Safari::Service) overrides initialize without env:/** and will raise ArgumentError: unknown keyword: :env when users try to use this new option. Update the Safari service initializer to accept/pass through env: (or switch to **) so the feature is consistently available across drivers.

Copilot uses AI. Check for mistakes.

WebDriver.logger.debug("Starting process: #{@command} with #{options}", id: :process)
@pid = Process.spawn(*@command, options)
@pid = Process.spawn(@env, *@command, options)
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

env is an attr_accessor, so it can be set to nil (or another non-Hash) after initialization; Process.spawn expects the optional first argument to be a Hash and will raise a TypeError if it isn't. Consider coercing to {} at the call site (or validating in env= and raising a more actionable WebDriver error) to make service startup resilient to nil/invalid env values.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment on lines +52 to +54
it 'spawns process with environment variables' do
process = described_class.new('ruby', '-e', 'puts ENV["SELENIUM_TEST_VAR"]')
process.env = {'SELENIUM_TEST_VAR' => 'test_value'}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

This spec spawns 'ruby' from PATH, which can be a different interpreter than the one running the test suite (and can fail in some CI setups). Use the current Ruby executable (e.g., RbConfig.ruby) for the child process command to make the test deterministic.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The code is very simple and it would provide same behavior on all versions.

Comment thread rb/spec/unit/selenium/webdriver/common/child_process_spec.rb Outdated
Comment on lines +28 to 29
def initialize(path: nil, port: nil, log: nil, args: nil, env: nil)
raise Error::WebDriverError, 'Safari Service does not support setting log output' if log
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

Safari::Service currently lists all keyword parameters explicitly. Other service subclasses (e.g., Chrome/Edge/Firefox) use def initialize(args: nil, **) and then super, which avoids needing to update this signature whenever the base WebDriver::Service adds a new config keyword (as happened here for env). Consider switching Safari::Service to accept ** (while still enforcing the log restriction) to make this more future-proof.

Suggested change
def initialize(path: nil, port: nil, log: nil, args: nil, env: nil)
raise Error::WebDriverError, 'Safari Service does not support setting log output' if log
def initialize(args: nil, **opts)
raise Error::WebDriverError, 'Safari Service does not support setting log output' if opts[:log]

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@y-yagi y-yagi Apr 26, 2026

Choose a reason for hiding this comment

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

I thinks the current implementation is better.

Currently, the environment variables used when launching a browser
are the same as those of the Ruby process that performs the launch.

However, there are times when want to change the environment variables
passed to a browser. In our case, `LD_PRELOAD`. We want to set `LD_PRELOAD`
to use jemalloc in Ruby, but we want to avoid setting it for Chrome
because using jemalloc can cause crashes or unstable behavior.

To address this issue, this PR fixes allow environment variables to be
explicitly specified via the service.
@y-yagi y-yagi force-pushed the allow_to_set_env branch from 2e5da27 to 4572e98 Compare April 26, 2026 05:39
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.

3 participants