Skip to content
129 changes: 129 additions & 0 deletions rb/spec/integration/selenium/webdriver/spec_support/bazel/runfiles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# frozen_string_literal: true

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Mirrors the Bazel::Runfiles helper in rules_ruby#374; drop this file and depend on
# @rules_ruby//ruby/runfiles once a rules_ruby release ships it.

require 'pathname'

module Bazel
# Resolves runtime paths to data dependencies using either a
# manifest file or a runfiles directory.
class Runfiles
def self.create(env = ENV)
manifest_file = env['RUNFILES_MANIFEST_FILE']
runfiles_dir = env['RUNFILES_DIR']

return new(ManifestBased.new(manifest_file)) if manifest_file && !manifest_file.empty?
return new(DirectoryBased.new(runfiles_dir)) if runfiles_dir && !runfiles_dir.empty?

create_from_program_name($PROGRAM_NAME)
end

def self.create_from_program_name(program_name)
if File.exist?("#{program_name}.runfiles_manifest")
new(ManifestBased.new("#{program_name}.runfiles_manifest"))
elsif File.exist?("#{program_name}.runfiles")
new(DirectoryBased.new("#{program_name}.runfiles"))
else
new(DirectoryBased.new(''))
end
end

def initialize(strategy)
@strategy = strategy
end

def rlocation(path)
raise ArgumentError, 'path must not be empty' if path.to_s.empty?

return path if Pathname.new(path).absolute?

invalid_path = %r{\A\.\.[/\\]|[/\\]\.\.[/\\]|\A\.[/\\]|[/\\]\.[/\\]|[/\\]\.\z|[/\\][/\\]}
raise ArgumentError, "path is not valid: #{path.inspect}" if path.match?(invalid_path)

raise ArgumentError, "path is absolute without a drive letter: #{path.inspect}" if path.start_with?('\\')

@strategy.rlocation(path)
end
Comment thread
titusfortner marked this conversation as resolved.

# Resolves paths by looking them up in a runfiles MANIFEST file.
class ManifestBased
def initialize(manifest_path)
@entries = parse_manifest(manifest_path)
end

def rlocation(path)
return @entries[path] if @entries.key?(path)

prefix = File.dirname(path)
while prefix != '.' && prefix != '/'
base = @entries[prefix]
return "#{base}#{path[prefix.length..]}" if base && !base.empty?

prefix = File.dirname(prefix)
end

nil
end

private

def parse_manifest(path)
entries = {}
return entries unless File.exist?(path)

File.foreach(path) do |line|
line.chomp!
next if line.empty?

key, value = parse_entry(line)
entries[key] = value
end

entries
end

def parse_entry(line)
escaped = line.delete_prefix!(' ')
key, _, value = line.partition(' ')
return [key, value] unless escaped

[unescape(key), unescape(value)]
end

def unescape(str)
str.gsub(/\\[snb]/, '\s' => ' ', '\n' => "\n", '\b' => '\\')
end
end

# Resolves paths by joining them onto a runfiles directory root.
class DirectoryBased
def initialize(runfiles_dir)
@runfiles_dir = runfiles_dir
end

def rlocation(path)
return nil if @runfiles_dir.empty?

File.join(@runfiles_dir, path)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# specific language governing permissions and limitations
# under the License.

require_relative 'bazel/runfiles'

module Selenium
module WebDriver
module SpecSupport
Expand Down Expand Up @@ -128,12 +130,12 @@ def driver_configuration

def driver_path
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
env = {chrome: 'CHROMEDRIVER_BINARY', edge: 'MSEDGEDRIVER_BINARY', firefox: 'GECKODRIVER_BINARY'}[browser]
rlocation(ENV.fetch(env)) if env && ENV.key?(env)
runfiles.rlocation(ENV.fetch(env)) if env && ENV.key?(env)
end

def browser_path
env = "#{browser.to_s.upcase}_BINARY"
rlocation(ENV.fetch(env)) if ENV.key?(env)
runfiles.rlocation(ENV.fetch(env)) if ENV.key?(env)
end

def options_key
Expand All @@ -147,8 +149,12 @@ def w3c_browser_name
def bazel_java
return unless ENV.key?('WD_BAZEL_JAVA_LOCATION')
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

java_path = File.read(File.expand_path(ENV.fetch('WD_BAZEL_JAVA_LOCATION'))).chomp
resolved = rlocation(java_path)
# $(JAVA) is an exec path (external/<repo>/...); strip the prefix to a canonical rlocation
# path, and fall back to the raw path on a lookup miss so we never realpath nil.
java_path = File.read(File.expand_path(ENV.fetch('WD_BAZEL_JAVA_LOCATION'))).chomp.sub(%r{^external/}, '')
resolved = runfiles.rlocation(java_path) || java_path

# Resolve the JDK symlink to its real path to dodge a Windows JVM bug mapping lib\modules.
Platform.windows? && File.exist?(resolved) ? File.realpath(resolved) : resolved
end

Expand Down Expand Up @@ -308,22 +314,22 @@ def chrome_driver(service: nil, **)
service ||= WebDriver::Service.chrome
service.args << '--disable-build-check' if ENV['DISABLE_BUILD_CHECK']
service.args << '--verbose' if WebDriver.logger.debug?
service.executable_path = rlocation(ENV['CHROMEDRIVER_BINARY']) if ENV.key?('CHROMEDRIVER_BINARY')
service.executable_path = runfiles.rlocation(ENV['CHROMEDRIVER_BINARY']) if ENV.key?('CHROMEDRIVER_BINARY')
WebDriver::Driver.for(:chrome, service: service, **)
end

def edge_driver(service: nil, **)
service ||= WebDriver::Service.edge
service.args << '--disable-build-check' if ENV['DISABLE_BUILD_CHECK']
service.args << '--verbose' if WebDriver.logger.debug?
service.executable_path = rlocation(ENV['MSEDGEDRIVER_BINARY']) if ENV.key?('MSEDGEDRIVER_BINARY')
service.executable_path = runfiles.rlocation(ENV['MSEDGEDRIVER_BINARY']) if ENV.key?('MSEDGEDRIVER_BINARY')
WebDriver::Driver.for(:edge, service: service, **)
end

def firefox_driver(service: nil, **)
service ||= WebDriver::Service.firefox
service.args.push('--log', 'trace') if WebDriver.logger.debug?
service.executable_path = rlocation(ENV['GECKODRIVER_BINARY']) if ENV.key?('GECKODRIVER_BINARY')
service.executable_path = runfiles.rlocation(ENV['GECKODRIVER_BINARY']) if ENV.key?('GECKODRIVER_BINARY')
WebDriver::Driver.for(:firefox, service: service, **)
end

Expand All @@ -342,7 +348,7 @@ def safari_preview_driver(**)
def chrome_options(args: [], **opts)
opts[:browser_version] = browser_version
opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url)
opts[:binary] ||= rlocation(ENV['CHROME_BINARY']) if ENV.key?('CHROME_BINARY')
opts[:binary] ||= runfiles.rlocation(ENV['CHROME_BINARY']) if ENV.key?('CHROME_BINARY')
args << '--headless' if ENV['HEADLESS']
args << '--no-sandbox' unless Platform.windows?
args << '--disable-dev-shm-usage' if GlobalTestEnv.rbe?
Expand All @@ -353,7 +359,7 @@ def chrome_options(args: [], **opts)
def edge_options(args: [], **opts)
opts[:browser_version] = browser_version
opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url)
opts[:binary] ||= rlocation(ENV['EDGE_BINARY']) if ENV.key?('EDGE_BINARY')
opts[:binary] ||= runfiles.rlocation(ENV['EDGE_BINARY']) if ENV.key?('EDGE_BINARY')
args << '--headless' if ENV['HEADLESS']
args << '--no-sandbox' unless Platform.windows?
args << '--disable-dev-shm-usage' if GlobalTestEnv.rbe?
Expand All @@ -364,7 +370,7 @@ def edge_options(args: [], **opts)
def firefox_options(args: [], **opts)
opts[:browser_version] = browser_version
opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url)
opts[:binary] ||= rlocation(ENV['FIREFOX_BINARY']) if ENV.key?('FIREFOX_BINARY')
opts[:binary] ||= runfiles.rlocation(ENV['FIREFOX_BINARY']) if ENV.key?('FIREFOX_BINARY')
opts[:unhandled_prompt_behavior] ||= 'ignore'
args << '--headless' if ENV['HEADLESS']
WebDriver::Options.firefox(args: args, **opts)
Expand Down Expand Up @@ -392,20 +398,9 @@ def random_port
sock.close
end

# Resolves a Bazel rootpath to an absolute path using the runfiles tree.
# $(location) returns rootpath like "external/<repo>/<path>" but Bazel 9
# runfiles use rlocation paths like "<repo>/<path>" (no "external/" prefix).
def rlocation(path)
return path if path.nil? || File.exist?(path)

runfiles_dir = ENV.fetch('RUNFILES_DIR', nil)
return path unless runfiles_dir

rlocation_path = path.sub(%r{^external/}, '')
resolved = File.join(runfiles_dir, rlocation_path)
return resolved if File.exist?(resolved)

path
# Resolves Bazel $(rlocationpath ...) values to absolute paths in the runfiles tree.
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated
def runfiles
@runfiles ||= Bazel::Runfiles.create
end
end
end # SpecSupport
Expand Down
40 changes: 20 additions & 20 deletions rb/spec/tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ BROWSERS = {
"WD_SPEC_DRIVER": "chrome",
} | select({
"@selenium//common:use_pinned_linux_chrome": {
"CHROME_BINARY": "$(location @linux_chrome//:chrome-linux64/chrome)",
"CHROMEDRIVER_BINARY": "$(location @linux_chromedriver//:chromedriver)",
"CHROME_BINARY": "$(rlocationpath @linux_chrome//:chrome-linux64/chrome)",
"CHROMEDRIVER_BINARY": "$(rlocationpath @linux_chromedriver//:chromedriver)",
},
"@selenium//common:use_pinned_macos_chrome": {
"CHROME_BINARY": "$(location @mac_chrome//:Chrome.app)/Contents/MacOS/Chrome",
"CHROMEDRIVER_BINARY": "$(location @mac_chromedriver//:chromedriver)",
"CHROME_BINARY": "$(rlocationpath @mac_chrome//:Chrome.app)/Contents/MacOS/Chrome",
"CHROMEDRIVER_BINARY": "$(rlocationpath @mac_chromedriver//:chromedriver)",
},
"//conditions:default": {},
}) | select({
Expand All @@ -45,12 +45,12 @@ BROWSERS = {
"WD_BROWSER_VERSION": "beta",
} | select({
"@selenium//common:use_pinned_linux_chrome": {
"CHROME_BINARY": "$(location @linux_beta_chrome//:chrome-linux64/chrome)",
"CHROMEDRIVER_BINARY": "$(location @linux_beta_chromedriver//:chromedriver)",
"CHROME_BINARY": "$(rlocationpath @linux_beta_chrome//:chrome-linux64/chrome)",
"CHROMEDRIVER_BINARY": "$(rlocationpath @linux_beta_chromedriver//:chromedriver)",
},
"@selenium//common:use_pinned_macos_chrome": {
"CHROME_BINARY": "$(location @mac_beta_chrome//:Chrome.app)/Contents/MacOS/Chrome",
"CHROMEDRIVER_BINARY": "$(location @mac_beta_chromedriver//:chromedriver)",
"CHROME_BINARY": "$(rlocationpath @mac_beta_chrome//:Chrome.app)/Contents/MacOS/Chrome",
"CHROMEDRIVER_BINARY": "$(rlocationpath @mac_beta_chromedriver//:chromedriver)",
},
"//conditions:default": {},
}) | select({
Expand All @@ -69,12 +69,12 @@ BROWSERS = {
"WD_SPEC_DRIVER": "edge",
} | select({
"@selenium//common:use_pinned_linux_edge": {
"EDGE_BINARY": "$(location @linux_edge//:opt/microsoft/msedge/microsoft-edge)",
"MSEDGEDRIVER_BINARY": "$(location @linux_edgedriver//:msedgedriver)",
"EDGE_BINARY": "$(rlocationpath @linux_edge//:opt/microsoft/msedge/microsoft-edge)",
"MSEDGEDRIVER_BINARY": "$(rlocationpath @linux_edgedriver//:msedgedriver)",
},
"@selenium//common:use_pinned_macos_edge": {
"EDGE_BINARY": "$(location @mac_edge//:Edge.app)/Contents/MacOS/Microsoft\\ Edge",
"MSEDGEDRIVER_BINARY": "$(location @mac_edgedriver//:msedgedriver)",
"EDGE_BINARY": "$(rlocationpath @mac_edge//:Edge.app)/Contents/MacOS/Microsoft\\ Edge",
"MSEDGEDRIVER_BINARY": "$(rlocationpath @mac_edgedriver//:msedgedriver)",
},
"//conditions:default": {},
}) | select({
Expand All @@ -92,12 +92,12 @@ BROWSERS = {
"WD_SPEC_DRIVER": "firefox",
} | select({
"@selenium//common:use_pinned_linux_firefox": {
"FIREFOX_BINARY": "$(location @linux_firefox//:firefox/firefox)",
"GECKODRIVER_BINARY": "$(location @linux_geckodriver//:geckodriver)",
"FIREFOX_BINARY": "$(rlocationpath @linux_firefox//:firefox/firefox)",
"GECKODRIVER_BINARY": "$(rlocationpath @linux_geckodriver//:geckodriver)",
},
"@selenium//common:use_pinned_macos_firefox": {
"FIREFOX_BINARY": "$(location @mac_firefox//:Firefox.app)/Contents/MacOS/firefox",
"GECKODRIVER_BINARY": "$(location @mac_geckodriver//:geckodriver)",
"FIREFOX_BINARY": "$(rlocationpath @mac_firefox//:Firefox.app)/Contents/MacOS/firefox",
"GECKODRIVER_BINARY": "$(rlocationpath @mac_geckodriver//:geckodriver)",
},
"//conditions:default": {},
}) | select({
Expand All @@ -117,12 +117,12 @@ BROWSERS = {
"WD_BROWSER_VERSION": "beta",
} | select({
"@selenium//common:use_pinned_linux_firefox": {
"FIREFOX_BINARY": "$(location @linux_beta_firefox//:firefox/firefox)",
"GECKODRIVER_BINARY": "$(location @linux_geckodriver//:geckodriver)",
"FIREFOX_BINARY": "$(rlocationpath @linux_beta_firefox//:firefox/firefox)",
"GECKODRIVER_BINARY": "$(rlocationpath @linux_geckodriver//:geckodriver)",
},
"@selenium//common:use_pinned_macos_firefox": {
"FIREFOX_BINARY": "$(location @mac_beta_firefox//:Firefox.app)/Contents/MacOS/firefox",
"GECKODRIVER_BINARY": "$(location @mac_geckodriver//:geckodriver)",
"FIREFOX_BINARY": "$(rlocationpath @mac_beta_firefox//:Firefox.app)/Contents/MacOS/firefox",
"GECKODRIVER_BINARY": "$(rlocationpath @mac_geckodriver//:geckodriver)",
},
"//conditions:default": {},
}) | select({
Expand Down
Loading