parallel_appium 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # parallel_appium
2
+
3
+ Distributed mobile testing in Appium
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'parallel_appium'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install parallel_appium
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`.
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/JavonDavis/parallel_appium. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html).
38
+
39
+ ## Code of Conduct
40
+
41
+ Everyone interacting in the ParallelAppium project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/JavonDavis/parallel_appium/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/apps/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ # Created by .ignore support plugin (hsz.mobi)
2
+ *.apk
3
+ *.app
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'parallel_appium'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEhTCCAu2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBEMRYwFAYDVQQDDA1qYXZv
3
+ bmxkYXZpczE0MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
4
+ FgNjb20wHhcNMTgwNjA2MTcxNjM2WhcNMTkwNjA2MTcxNjM2WjBEMRYwFAYDVQQD
5
+ DA1qYXZvbmxkYXZpczE0MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
6
+ k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDCwCJw
7
+ CoVPoQgQSDGGGRWyt40oMNZHFc85AiRjBU7UMFgYSPQIt0M+SCyJaptM0pJYGG65
8
+ M/hx4Hnqw93ELvPWtAAHgP/9u05K3uiWgsxFRHm5S9JxMS/nvNRatVYvy2hKQGTU
9
+ zJpczncAhu0Lj9tlCuZMdvnhh9PAInrNaXQMkkk4n/XV9dk3lhyqemMFyKJhS0dO
10
+ k3JTn3muVdSOdLMOGJtreHdV5TByA/rIYQGraIqHrlbAbnGOnNunMk53M9eI3zAq
11
+ gz8fydEkaHswv7Mbk3kWnN67UMaNUpoElnEEj0242BtXhXeXr9FnqcFLwUrQLXKK
12
+ kc2BjV1CbK7AHMKLfU2yBQJVqEkb+Xu8dAkpHP333adf7KEW8N0vf+639jgBPbxa
13
+ y25Sh4ZQAuue1NVl43Uj7Qkbb2V4epiNr1JhRrpvMSQah2lx9yNs/NtgdZWHtidd
14
+ +3chmKUroK4elci3NKa76G+iwbD/pJkWg0zFt4cg4+pgWdmjX3PLnd0iv2UCAwEA
15
+ AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSjN7L+WZRc
16
+ /FBz01MZd7lsGI5j7jAiBgNVHREEGzAZgRdqYXZvbmxkYXZpczE0QGdtYWlsLmNv
17
+ bTAiBgNVHRIEGzAZgRdqYXZvbmxkYXZpczE0QGdtYWlsLmNvbTANBgkqhkiG9w0B
18
+ AQsFAAOCAYEAoxRokaBO+VS8xqmPtqEKofcXF2kJDemn9Jn9Begjj7mzqUVHOR9w
19
+ 4HHaAY79m1xm+yiveWgOsHvb04GCjR32dJjAcbb7dsg3aXOd49UoQ0lOVtOigH2s
20
+ v5BqR5svvPKoDA8nP0V9pTOxFboJw5dtY+s95Py6mc6nTsK3XdCUS80PiY3xPWS+
21
+ pCzJWE1mNMhRndpb9E0BAiimdmo3suQo7EVYQ3tPsnHwaSdv52tt9kW+rpoHFRV5
22
+ gjyXjRGX/lW897Zs2AA7yFb7oVvbaAlGyH2MLVnB5aiTywqE0ZgD9CTgSnGYu0iY
23
+ 28Uiprdx/AqGsAVNV8YAsPMEm/81xTXmJ0HRrpyvMHyAXuNPp84t4TsO2DQtAlrU
24
+ XuXlmerWWuxSKZOwV9fJfl+j5fO7bzUyeWK76xvvWGNtIF19xPnGdPpupaClWMHg
25
+ pjBkMT4ow4ZhVgBKPDT2jh74b8g2rIDSbkJIj0lf35WXqJqZzseabGPpTbtzJEap
26
+ HIrd7mPILLe3
27
+ -----END CERTIFICATE-----
data/docs/.gitignore ADDED
File without changes
data/docs/_config.yml ADDED
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-cayman
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'parallel_appium'
4
+ ParallelAppium::ParallelAppium.new.start platform:ARGV[0]
@@ -0,0 +1,35 @@
1
+ module ParallelAppium
2
+ # Connecting to Android devices
3
+ class Android
4
+ # Fire up the Android emulators
5
+ def start_emulators
6
+ emulators = `emulator -list-avds`.split("\n")
7
+ emulators = emulators[0, ENV['THREADS'].to_i]
8
+ Parallel.map(emulators, in_threads: emulators.size) do |emulator|
9
+ spawn("emulator -avd #{emulator} -scale 100dpi -no-boot-anim -no-audio -accel on &", out: '/dev/null')
10
+ end
11
+ end
12
+
13
+ # Get additional information for the Android device with unique identifier udid
14
+ def get_android_device_data(udid)
15
+ specs = { os: 'ro.build.version.release', manufacturer: 'ro.product.manufacturer', model: 'ro.product.model', sdk: 'ro.build.version.sdk' }
16
+ hash = {}
17
+ specs.each do |key, spec|
18
+ value = `adb -s #{udid} shell getprop "#{spec}"`.strip
19
+ hash.merge!(key => value.to_s)
20
+ end
21
+ hash
22
+ end
23
+
24
+ # Devices after cleanup and supplemental data included
25
+ def devices
26
+ start_emulators
27
+ sleep 10
28
+ devices = `adb devices`.split("\n").select { |x| x.include? "\tdevice" }.map.each_with_index { |d, i| {platform: 'android', name: 'android', udid: d.split("\t")[0], wdaPort: 8100 + i, thread: i + 1} }
29
+ devices = devices.map { |x| x.merge(get_android_device_data(x[:udid])) }
30
+
31
+ ENV['DEVICES'] = JSON.generate(devices)
32
+ devices
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ module ParallelAppium
2
+ # Connecting to iOS devices
3
+ class IOS
4
+
5
+ def initialize
6
+ # Get available simulators
7
+ @simulators = `instruments -s devices`.split("\n").reverse
8
+ end
9
+
10
+ # Filter simulator data
11
+ def simulator_information
12
+ re = /\([0-9]+\.[0-9]\) \[[0-9A-Z-]+\]/m
13
+
14
+ # Filter out simulator info for iPhone platform version and udid
15
+ @simulators.select { |simulator_data| simulator_data.include?('iPhone') && !simulator_data.include?('Apple Watch') }
16
+ .map { |simulator_data| simulator_data.scan(re)[0].tr('()[]', '').split }[0, ENV['THREADS'].to_i]
17
+ end
18
+
19
+ # Devices after cleanup and supplemental data included
20
+ def devices
21
+ devices = []
22
+ simulator_information.each_with_index do |data, i|
23
+ devices.push(name: @simulators[i][0, @simulators[i].index('(') - 1], platform: 'ios', os: data[0], udid: data[1],
24
+ wdaPort: 8100 + i + ENV['THREADS'].to_i, thread: i + 1)
25
+ end
26
+ ENV['DEVICES'] = JSON.generate(devices)
27
+ devices
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,164 @@
1
+ module ParallelAppium
2
+ # Setting up the selenium grid server
3
+ class Server
4
+ # Sets the current thread number environment variable(TEST_ENV_NUMBER)
5
+ def thread
6
+ (ENV['TEST_ENV_NUMBER'].nil? || ENV['TEST_ENV_NUMBER'].empty? ? 1 : ENV['TEST_ENV_NUMBER']).to_i
7
+ end
8
+
9
+ # Get the device data from the DEVICES environment variable
10
+ def device_data
11
+ JSON.parse(ENV['DEVICES']).find { |t| t['thread'].eql? thread } unless ENV['DEVICES'].nil?
12
+ end
13
+
14
+ # Save device specifications to output directory
15
+ def save_device_data(dev_array)
16
+ dev_array.each do |device|
17
+ device_hash = {}
18
+ device.each do |key, value|
19
+ device_hash[key] = value
20
+ end
21
+
22
+ # Delete and create output folder
23
+ `rm -rf output`
24
+ `mkdir output`
25
+
26
+ device.each do |k, v|
27
+ open("output/specs-#{device_hash[:udid]}.log", 'a') do |file|
28
+ file << "#{k}: #{v}\n"
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ # Set UDID and name environment variable
35
+ def set_udid_environment_variable
36
+ ENV['UDID'] = device_data['udid'] unless device_data.nil?
37
+ ENV['name'] = device_data['name'] unless device_data.nil? # Unique on ios but could be repeated on android
38
+ end
39
+
40
+ # Get the device information for the respective platform
41
+ def get_devices(platform)
42
+ ENV['THREADS'] = '1' if ENV['THREADS'].nil?
43
+ if platform == 'android'
44
+ Android.new.devices
45
+ elsif platform == 'ios'
46
+ IOS.new.devices
47
+ end
48
+ end
49
+
50
+ # Start the appium server with the specified options
51
+ def appium_server_start(**options)
52
+ command = +'appium'
53
+ command << " --nodeconfig #{options[:config]}" if options.key?(:config)
54
+ command << " -p #{options[:port]}" if options.key?(:port)
55
+ command << " -bp #{options[:bp]}" if options.key?(:bp)
56
+ command << " --log #{Dir.pwd}/output/#{options[:log]}" if options.key?(:log)
57
+ command << " --tmp #{ENV['BASE_DIR']}/tmp/#{options[:tmp]}" if options.key?(:tmp)
58
+ Dir.chdir('.') do
59
+ puts(command)
60
+ pid = spawn(command, out: '/dev/null')
61
+ puts 'Waiting for Appium to start up...'
62
+ sleep 10
63
+ puts "Appium PID: #{pid}"
64
+ puts 'Appium server did not start' if pid.nil?
65
+ end
66
+ end
67
+
68
+ # Generate node config for sellenium grid
69
+ def generate_node_config(file_name, appium_port, device)
70
+ system 'mkdir node_configs >> /dev/null 2>&1'
71
+ f = File.new("#{Dir.pwd}/node_configs/#{file_name}", 'w')
72
+ f.write(JSON.generate(
73
+ capabilities: [{ browserName: device[:udid], maxInstances: 5, platform: device[:platform] }],
74
+ configuration: { cleanUpCycle: 2000,
75
+ timeout: 1_800_000,
76
+ registerCycle: 5000,
77
+ proxy: 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy',
78
+ url: "http://127.0.0.1:#{appium_port}/wd/hub",
79
+ host: '127.0.0.1',
80
+ port: appium_port,
81
+ maxSession: 5,
82
+ register: true,
83
+ hubPort: 4444,
84
+ hubHost: 'localhost' }
85
+ ))
86
+ f.close
87
+ end
88
+
89
+ # Start the Selenium grid server as a hub
90
+ def start_hub
91
+ spawn("java -jar #{File.dirname(__FILE__)}/selenium-server-standalone-3.12.0.jar -role hub -newSessionWaitTimeout 250000 -log #{Dir.pwd}/output/hub.log &", out: '/dev/null')
92
+ sleep 3 # wait for hub to start...
93
+ spawn('open -a safari http://127.0.0.1:4444/grid/console')
94
+ end
95
+
96
+ # Start an appium server or the platform on the specified port
97
+ def start_single_appium(platform, port)
98
+ puts 'Getting Device data'
99
+ devices = get_devices(platform)[0]
100
+ if devices.nil?
101
+ puts "No devices for #{platform}, Exiting..."
102
+ exit
103
+ else
104
+ udid = devices[:udid]
105
+ save_device_data [devices]
106
+ end
107
+ ENV['UDID'] = udid
108
+ appium_server_start udid: udid, log: "appium-#{udid}.log", port: port
109
+ end
110
+
111
+ # Check if a port on an ip address is available
112
+ def port_open?(ip, port)
113
+ begin
114
+ Timeout.timeout(1) do
115
+ begin
116
+ s = TCPSocket.new(ip, port)
117
+ s.close
118
+ return true
119
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
120
+ return false
121
+ end
122
+ end
123
+ rescue Timeout::Error
124
+ return false
125
+ end
126
+ false
127
+ end
128
+
129
+ # Launch the Selenium grid hub and required appium instances
130
+ def launch_hub_and_nodes(platform)
131
+ start_hub unless port_open?('localhost', 4444)
132
+ devices = get_devices(platform)
133
+
134
+ if devices.nil?
135
+ puts "No devices for #{platform}, Exiting...."
136
+ exit
137
+ else
138
+ save_device_data [devices]
139
+ end
140
+
141
+ threads = ENV['THREADS'].to_i
142
+ if devices.size < threads
143
+ puts "Not enough available devices, reducing to #{devices.size} threads"
144
+ ENV['THREADS'] = devices.size.to_s
145
+ else
146
+ puts "Using #{threads} of the available #{devices.size} devices"
147
+ devices = devices[0, threads]
148
+ end
149
+
150
+
151
+ Parallel.map_with_index(devices, in_processes: devices.size) do |device, index|
152
+ offset = platform == 'android' ? 0 : threads
153
+ port = 4000 + index + offset
154
+ bp = 2250 + index + offset
155
+ config_name = "#{device[:udid]}.json"
156
+ generate_node_config config_name, port, device
157
+ node_config = "#{Dir.pwd}/node_configs/#{config_name}"
158
+ puts port
159
+ appium_server_start config: node_config, port: port, bp: bp, udid: device[:udid],
160
+ log: "appium-#{device[:udid]}.log", tmp: device[:udid]
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,3 @@
1
+ module ParallelAppium
2
+ VERSION = '0.2.0'.freeze
3
+ end
@@ -0,0 +1,218 @@
1
+ require 'parallel_appium/version'
2
+ require 'parallel_appium/server'
3
+ require 'parallel_appium/android'
4
+ require 'parallel_appium/ios'
5
+ require 'parallel_tests'
6
+ require 'parallel'
7
+ require 'appium_lib'
8
+ require 'socket'
9
+ require 'timeout'
10
+ require 'json'
11
+
12
+ module ParallelAppium
13
+ # Set up environment, Selenium and Appium
14
+ class ParallelAppium
15
+
16
+ def initialize
17
+ @server = Server.new
18
+ end
19
+
20
+ # Kill process by pattern name
21
+ def kill_process(process)
22
+ `ps -ef | grep #{process} | awk '{print $2}' | xargs kill -9 >> /dev/null 2>&1`
23
+ end
24
+
25
+ # Load capabilities based on current device data
26
+ def load_capabilities(caps)
27
+ device = @server.device_data
28
+ unless device.nil?
29
+ caps[:caps][:udid] = device.fetch('udid', nil)
30
+ caps[:caps][:platformVersion] = device.fetch('os', caps[:caps][:platformVersion])
31
+ caps[:caps][:deviceName] = device.fetch('name', caps[:caps][:deviceName])
32
+ caps[:caps][:wdaLocalPort] = device.fetch('wdaPort', nil)
33
+ end
34
+
35
+ caps[:caps][:sessionOverride] = true
36
+ caps[:caps][:useNewWDA] = true
37
+ # TODO: Optionally set these capabilities below
38
+ caps[:caps][:noReset] = true
39
+ caps[:caps][:fullReset] = false
40
+ caps[:appium_lib][:server_url] = ENV['SERVER_URL']
41
+ caps
42
+ end
43
+
44
+ # Load appium text file if available and attempt to start the driver
45
+ # platform is either android or ios, otherwise read from ENV
46
+ # caps is mapping of appium capabilities
47
+ def initialize_appium(**args)
48
+ platform = args[:platform]
49
+ caps = args[:caps]
50
+
51
+ platform = ENV['platform'] if platform.nil?
52
+
53
+ if platform.nil?
54
+ puts 'Platform not found in environment variable'
55
+ exit
56
+ end
57
+
58
+ caps = Appium.load_appium_txt file: File.new("#{Dir.pwd}/appium-#{platform}.txt") if caps.nil?
59
+
60
+ if caps.nil?
61
+ puts 'No capabilities specified'
62
+ exit
63
+ end
64
+ capabilities = load_capabilities(caps)
65
+ @driver = Appium::Driver.new(capabilities, true)
66
+ @driver.start_driver
67
+ Appium.promote_appium_methods Object
68
+ Appium.promote_appium_methods RSpec::Core::ExampleGroup
69
+ @driver
70
+ end
71
+
72
+ # Define a signal handler for SIGINT
73
+ def setup_signal_handler(ios_pid = nil, android_pid = nil)
74
+ Signal.trap('INT') do
75
+ Process.kill('INT', ios_pid) unless ios_pid.nil?
76
+ Process.kill('INT', android_pid) unless android_pid.nil?
77
+
78
+ # Kill any existing Appium and Selenium processes
79
+ kill_process 'appium'
80
+ kill_process 'selenium'
81
+
82
+ # Terminate ourself
83
+ exit 1
84
+ end
85
+ end
86
+
87
+ # Decide whether to execute specs in parallel or not
88
+ # @param [String] platform
89
+ # @param [int] threads
90
+ # @param [String] spec_path
91
+ # @param [boolean] parallel
92
+ def execute_specs(platform, threads, spec_path, parallel = false)
93
+ command = if parallel
94
+ "platform=#{platform} parallel_rspec -n #{threads} #{spec_path} > output/#{platform}.log"
95
+ else
96
+ "platform=#{platform} rspec #{spec_path} --tag #{platform} > output/#{platform}.log"
97
+ end
98
+
99
+ puts "Executing #{command}"
100
+ exec command
101
+ end
102
+
103
+ # Define Spec path, validate platform and execute specs
104
+ def setup(platform, file_path, threads, parallel)
105
+ spec_path = 'spec/'
106
+ spec_path = file_path.to_s unless file_path.nil?
107
+ puts "SPEC PATH:#{spec_path}"
108
+
109
+ unless %w[ios android].include? platform
110
+ puts "Invalid platform #{platform}"
111
+ exit
112
+ end
113
+
114
+ execute_specs platform, threads, spec_path, parallel
115
+ end
116
+
117
+ # Validate platform is valid
118
+ def check_platform(platform)
119
+ options = %w[ios android all]
120
+ if platform.nil?
121
+ puts 'No platform detected... Options: ios,android,all'
122
+ exit
123
+ elsif !options.include? platform.downcase
124
+ puts "Invalid platform #{platform}"
125
+ exit
126
+ end
127
+ end
128
+
129
+ # Fire necessary appium server instances and Selenium grid server if needed.
130
+ def start(**args)
131
+
132
+ platform = args[:platform]
133
+ file_path = args[:file_path]
134
+
135
+ # Validate environment variable
136
+ if ENV['platform'].nil?
137
+ if platform.nil?
138
+ puts 'No platform detected in environment and none passed to start...'
139
+ exit
140
+ end
141
+ ENV['platform'] = platform
142
+ end
143
+
144
+ sleep 3
145
+
146
+ # Appium ports
147
+ ios_port = 4725
148
+ android_port = 4727
149
+ default_port = 4725
150
+
151
+ platform = ENV['platform']
152
+
153
+ # Platform is required
154
+ check_platform platform
155
+
156
+ platform = platform.downcase
157
+ ENV['BASE_DIR'] = Dir.pwd
158
+
159
+ # Check if multithreaded for distributing tests across devices
160
+ threads = ENV['THREADS'].nil? ? 1 : ENV['THREADS'].to_i
161
+ parallel = threads != 1
162
+
163
+ if platform != 'all'
164
+ pid = fork do
165
+ if !parallel
166
+ ENV['SERVER_URL'] = "http://0.0.0.0:#{default_port}/wd/hub"
167
+ @server.start_single_appium platform, default_port
168
+ else
169
+ ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub'
170
+ @server.launch_hub_and_nodes platform
171
+ end
172
+ setup(platform, file_path, threads, parallel)
173
+ end
174
+
175
+ puts "PID: #{pid}"
176
+ setup_signal_handler(pid)
177
+ Process.waitpid(pid)
178
+ else # Spin off 2 sub-processes, one for Android connections and another for iOS,
179
+ # each with redefining environment variables for the server url, number of threads and platform
180
+ ios_pid = fork do
181
+ ENV['THREADS'] = threads.to_s
182
+ if parallel
183
+ ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub'
184
+ puts 'Start iOS'
185
+ @server.launch_hub_and_nodes 'ios'
186
+ else
187
+ ENV['SERVER_URL'] = "http://0.0.0.0:#{ios_port}/wd/hub"
188
+ puts 'Start iOS'
189
+ @server.start_single_appium 'ios', ios_port
190
+ end
191
+ setup('ios', file_path, threads, parallel)
192
+ end
193
+
194
+ android_pid = fork do
195
+ ENV['THREADS'] = threads.to_s
196
+ if parallel
197
+ ENV['SERVER_URL'] = 'http://localhost:4444/wd/hub'
198
+ puts 'Start Android'
199
+ @server.launch_hub_and_nodes 'android'
200
+ else
201
+ ENV['SERVER_URL'] = "http://0.0.0.0:#{android_port}/wd/hub"
202
+ puts 'Start Android'
203
+ @server.start_single_appium 'android', android_port
204
+ end
205
+ setup('android', file_path, threads, parallel)
206
+ end
207
+
208
+ puts "iOS PID: #{ios_pid}\nAndroid PID: #{android_pid}"
209
+ setup_signal_handler(ios_pid, android_pid)
210
+ [ios_pid, android_pid].each { |process_pid| Process.waitpid(process_pid) }
211
+ end
212
+
213
+ # Kill any existing Appium and Selenium processes
214
+ kill_process 'appium'
215
+ kill_process 'selenium'
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,52 @@
1
+
2
+ lib = File.expand_path('lib', __dir__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parallel_appium/version'
5
+
6
+ summary = 'Start multi-threaded appium servers'
7
+ description = 'Through the creation of multiple environments
8
+ this library allows for the distribution of multiple tests across 1 or more
9
+ devices or simulators. The library uses Selenium Grid and Appium to launch a
10
+ specified number of web driver and appium instances to spread the test load
11
+ across available devices'
12
+
13
+ Gem::Specification.new do |spec|
14
+ spec.name = 'parallel_appium'
15
+ spec.version = ParallelAppium::VERSION
16
+ spec.authors = ['Javon Davis']
17
+ spec.email = ['javonldavis14@gmail.com']
18
+
19
+ spec.summary = summary
20
+ spec.description = description
21
+ spec.homepage = 'https://github.com/JavonDavis/Parallel_Appium'
22
+ spec.license = 'GPL-3.0'
23
+
24
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
25
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
26
+ # if spec.respond_to?(:metadata)
27
+ # spec.metadata['allowed_push_host'] = "http://mygemserver.com"
28
+ # else
29
+ # raise 'RubyGems 2.4.1 or newer is required to protect against ' \
30
+ # 'public gem pushes.'
31
+ # end
32
+
33
+ # Specify which files should be added to the gem when it is released.
34
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
35
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
36
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
37
+ end
38
+ spec.bindir = 'exe'
39
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
40
+ spec.require_paths = ['lib']
41
+
42
+ spec.cert_chain = ['certs/javondavis.pem']
43
+ spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem') if $PROGRAM_NAME =~ /gem\z/
44
+
45
+ spec.add_development_dependency 'appium_lib', '~> 9.14'
46
+ spec.add_development_dependency 'bundler', '~> 1.16'
47
+ spec.add_development_dependency 'json', '~> 1.8'
48
+ spec.add_development_dependency 'parallel', '~> 1.12'
49
+ spec.add_development_dependency 'parallel_tests', '~> 2.21'
50
+ spec.add_development_dependency 'rake', '~> 10.0'
51
+ spec.add_development_dependency 'rspec', '~> 3.0'
52
+ end
data.tar.gz.sig ADDED
Binary file