parallel_appium 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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