| require 'childprocess' |
| require 'selenium/webdriver/common/socket_poller' |
| require 'net/http' |
| |
| module Selenium |
| |
| # |
| # Wraps the remote server jar |
| # |
| # Usage: |
| # |
| # server = Selenium::Server.new('/path/to/selenium-server-standalone.jar') |
| # server.start |
| # |
| # Automatically download the given version: |
| # |
| # server = Selenium::Server.get '2.6.0' |
| # server.start |
| # |
| # or the latest version: |
| # |
| # server = Selenium::Server.get :latest |
| # server.start |
| # |
| # Run the server in the background: |
| # |
| # server = Selenium::Server.new(jar, :background => true) |
| # server.start |
| # |
| # Add additional arguments: |
| # |
| # server = Selenium::Server.new(jar) |
| # server << ["--additional", "args"] |
| # server.start |
| # |
| |
| class Server |
| class Error < StandardError; end |
| |
| CL_RESET = WebDriver::Platform.windows? ? '' : "\r\e[0K" |
| |
| def self.get(required_version, opts = {}) |
| new(download(required_version), opts) |
| end |
| |
| # |
| # Download the given version of the selenium-server-standalone jar. |
| # |
| |
| def self.download(required_version) |
| required_version = latest if required_version == :latest |
| download_file_name = "selenium-server-standalone-#{required_version}.jar" |
| |
| if File.exists? download_file_name |
| return download_file_name |
| end |
| |
| begin |
| open(download_file_name, "wb") do |destination| |
| net_http.start("selenium.googlecode.com") do |http| |
| resp = http.request_get("/files/#{download_file_name}") do |response| |
| total = response.content_length |
| progress = 0 |
| segment_count = 0 |
| |
| response.read_body do |segment| |
| progress += segment.length |
| segment_count += 1 |
| |
| if segment_count % 15 == 0 |
| percent = (progress.to_f / total.to_f) * 100 |
| print "#{CL_RESET}Downloading #{download_file_name}: #{percent.to_i}% (#{progress} / #{total})" |
| segment_count = 0 |
| end |
| |
| destination.write(segment) |
| end |
| end |
| |
| unless resp.kind_of? Net::HTTPSuccess |
| raise Error, "#{resp.code} for #{download_file_name}" |
| end |
| end |
| end |
| rescue |
| FileUtils.rm download_file_name if File.exists? download_file_name |
| raise |
| end |
| |
| download_file_name |
| end |
| |
| # |
| # Ask Google Code what the latest selenium-server-standalone version is. |
| # |
| |
| def self.latest |
| net_http.start("code.google.com") do |http| |
| resp = http.get("/p/selenium/downloads/list") |
| resp.body.to_s[/selenium-server-standalone-(\d+.\d+.\d+).jar/, 1] |
| end |
| end |
| |
| # |
| # The server port |
| # |
| |
| attr_accessor :port |
| |
| # |
| # The server timeout |
| # |
| |
| attr_accessor :timeout |
| |
| # |
| # Whether to launch the server in the background |
| # |
| |
| attr_accessor :background |
| |
| # |
| # Path to log file, or 'true' for stdout. |
| # |
| |
| attr_accessor :log |
| |
| # |
| # @param [String] jar Path to the server jar. |
| # @param [Hash] opts the options to create the server process with |
| # |
| # @option opts [Integer] :port Port the server should listen on (default: 4444). |
| # @option opts [Integer] :timeout Seconds to wait for server launch/shutdown (default: 30) |
| # @option opts [true,false] :background Run the server in the background (default: false) |
| # @option opts [true,false,String] :log Either a path to a log file, |
| # or true to pass server log to stdout. |
| # @raise [Errno::ENOENT] if the jar file does not exist |
| # |
| |
| def initialize(jar, opts = {}) |
| raise Errno::ENOENT, jar unless File.exist?(jar) |
| |
| @jar = jar |
| @host = "127.0.0.1" |
| @port = opts.fetch(:port, 4444) |
| @timeout = opts.fetch(:timeout, 30) |
| @background = opts.fetch(:background, false) |
| @log = opts[:log] |
| |
| @additional_args = [] |
| end |
| |
| def start |
| process.start |
| poll_for_service |
| |
| process.wait unless @background |
| end |
| |
| def stop |
| begin |
| Net::HTTP.get(@host, "/selenium-server/driver/?cmd=shutDownSeleniumServer", @port) |
| rescue Errno::ECONNREFUSED |
| end |
| |
| stop_process if @process |
| poll_for_shutdown |
| |
| @log_file.close if @log_file |
| end |
| |
| def webdriver_url |
| "http://#{@host}:#{@port}/wd/hub" |
| end |
| |
| def <<(arg) |
| if arg.kind_of?(Array) |
| @additional_args += arg |
| else |
| @additional_args << arg.to_s |
| end |
| end |
| |
| private |
| |
| def self.net_http |
| http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] |
| |
| if http_proxy |
| http_proxy = "http://#{http_proxy}" unless http_proxy.start_with?("http://") |
| uri = URI.parse(http_proxy) |
| |
| Net::HTTP::Proxy(uri.host, uri.port) |
| else |
| Net::HTTP |
| end |
| end |
| |
| def stop_process |
| return unless @process.alive? |
| |
| begin |
| @process.poll_for_exit(5) |
| rescue ChildProcess::TimeoutError |
| @process.stop |
| end |
| rescue Errno::ECHILD |
| # already dead |
| ensure |
| @process = nil |
| end |
| |
| def process |
| @process ||= ( |
| cp = ChildProcess.build("java", "-jar", @jar, "-port", @port.to_s, *@additional_args) |
| io = cp.io |
| |
| if @log.kind_of?(String) |
| @log_file = File.open(@log, "w") |
| io.stdout = io.stderr = @log_file |
| elsif @log |
| io.inherit! |
| end |
| |
| cp.detach = @background |
| |
| cp |
| ) |
| end |
| |
| def poll_for_service |
| unless socket.connected? |
| raise Error, "remote server not launched in #{@timeout} seconds" |
| end |
| end |
| |
| def poll_for_shutdown |
| unless socket.closed? |
| raise Error, "remote server not stopped in #{@timeout} seconds" |
| end |
| end |
| |
| def socket |
| @socket ||= WebDriver::SocketPoller.new(@host, @port, @timeout) |
| end |
| |
| end # Server |
| end # Selenium |