class ParallelAppium::ParallelAppium
Set up environment, Selenium and Appium
Public Class Methods
new()
click to toggle source
# File lib/parallel_appium.rb, line 16 def initialize @server = Server.new end
Public Instance Methods
check_platform(platform)
click to toggle source
Validate platform is valid
# File lib/parallel_appium.rb, line 116 def check_platform(platform) options = %w[ios android all] if platform.nil? puts 'No platform detected... Options: ios,android,all' exit elsif !options.include? platform.downcase puts "Invalid platform #{platform}" exit end end
execute_specs(platform, threads, spec_path, parallel = false)
click to toggle source
Decide whether to execute specs in parallel or not @param [String] platform @param [int] threads @param [String] spec_path @param [boolean] parallel
# File lib/parallel_appium.rb, line 90 def execute_specs(platform, threads, spec_path, parallel = false) command = if parallel "platform=#{platform} parallel_rspec -n #{threads} #{spec_path}" else "platform=#{platform} rspec #{spec_path} --tag #{platform}" end puts "Executing #{command}" exec command end
initialize_appium(**args)
click to toggle source
Load appium text file if available and attempt to start the driver platform is either android or ios, otherwise read from ENV caps is mapping of appium capabilities
# File lib/parallel_appium.rb, line 43 def initialize_appium(**args) platform = args[:platform] caps = args[:caps] platform = ENV['platform'] if platform.nil? if platform.nil? puts 'Platform not found in environment variable' exit end caps = Appium.load_appium_txt file: File.new("#{Dir.pwd}/appium-#{platform}.txt") if caps.nil? if caps.nil? puts 'No capabilities specified' exit end puts 'Preparing to load capabilities' capabilities = load_capabilities(caps) puts 'Loaded capabilities' @driver = Appium::Driver.new(capabilities, true) puts 'Created driver' Appium.promote_appium_methods Object Appium.promote_appium_methods RSpec::Core::ExampleGroup @driver end
kill_process(process)
click to toggle source
Kill process by pattern name
# File lib/parallel_appium.rb, line 21 def kill_process(process) `ps -ef | grep #{process} | awk '{print $2}' | xargs kill -9 >> /dev/null 2>&1` end
load_capabilities(caps)
click to toggle source
Load capabilities based on current device data
# File lib/parallel_appium.rb, line 26 def load_capabilities(caps) device = @server.device_data unless device.nil? caps[:caps][:udid] = device.fetch('udid', nil) caps[:caps][:platformVersion] = device.fetch('os', caps[:caps][:platformVersion]) caps[:caps][:deviceName] = device.fetch('name', caps[:caps][:deviceName]) caps[:caps][:wdaLocalPort] = device.fetch('wdaPort', nil) caps[:caps][:avd] = device.fetch('avd', nil) end caps[:appium_lib][:server_url] = ENV['SERVER_URL'] caps end
setup(platform, file_path, threads, parallel)
click to toggle source
Define Spec path, validate platform and execute specs
# File lib/parallel_appium.rb, line 102 def setup(platform, file_path, threads, parallel) spec_path = 'spec/' spec_path = file_path.to_s unless file_path.nil? puts "SPEC PATH:#{spec_path}" unless %w[ios android].include? platform puts "Invalid platform #{platform}" exit end execute_specs platform, threads, spec_path, parallel end
setup_signal_handler(ios_pid = nil, android_pid = nil)
click to toggle source
Define a signal handler for SIGINT
# File lib/parallel_appium.rb, line 71 def setup_signal_handler(ios_pid = nil, android_pid = nil) Signal.trap('INT') do Process.kill('INT', ios_pid) unless ios_pid.nil? Process.kill('INT', android_pid) unless android_pid.nil? # Kill any existing Appium and Selenium processes kill_process 'appium' kill_process 'selenium' # Terminate ourself exit 1 end end
start(**args)
click to toggle source
Fire necessary appium server instances and Selenium grid server if needed.
# File lib/parallel_appium.rb, line 128 def start(**args) platform = args[:platform] file_path = args[:file_path] # Validate environment variable if ENV['platform'].nil? if platform.nil? puts 'No platform detected in environment and none passed to start...' exit end ENV['platform'] = platform end sleep 3 # Appium ports ios_port = 4725 android_port = 4727 default_port = 4725 platform = ENV['platform'] # Platform is required check_platform platform platform = platform.downcase ENV['BASE_DIR'] = Dir.pwd # Check if multithreaded for distributing tests across devices threads = ENV['THREADS'].nil? ? 1 : ENV['THREADS'].to_i parallel = threads != 1 if platform != 'all' pid = fork do if !parallel ENV['SERVER_URL'] = "http://0.0.0.0:#{default_port}/wd/hub" @server.start_single_appium platform, default_port else ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub' @server.launch_hub_and_nodes platform end setup(platform, file_path, threads, parallel) end puts "PID: #{pid}" setup_signal_handler(pid) Process.waitpid(pid) else # Spin off 2 sub-processes, one for Android connections and another for iOS, # each with redefining environment variables for the server url, number of threads and platform ios_pid = fork do ENV['THREADS'] = threads.to_s if parallel ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub' puts 'Start iOS' @server.launch_hub_and_nodes 'ios' else ENV['SERVER_URL'] = "http://0.0.0.0:#{ios_port}/wd/hub" puts 'Start iOS' @server.start_single_appium 'ios', ios_port end setup('ios', file_path, threads, parallel) end android_pid = fork do ENV['THREADS'] = threads.to_s if parallel ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub' puts 'Start Android' @server.launch_hub_and_nodes 'android' else ENV['SERVER_URL'] = "http://0.0.0.0:#{android_port}/wd/hub" puts 'Start Android' @server.start_single_appium 'android', android_port end setup('android', file_path, threads, parallel) end puts "iOS PID: #{ios_pid}\nAndroid PID: #{android_pid}" setup_signal_handler(ios_pid, android_pid) [ios_pid, android_pid].each { |process_pid| Process.waitpid(process_pid) } end # Kill any existing Appium and Selenium processes kill_process 'appium' kill_process 'selenium' end