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