hive-runner-android 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8df609a1edec60819bf8372dd0519f7851e7105f
4
+ data.tar.gz: f816df9c708723cb1515f205add30d59769a6934
5
+ SHA512:
6
+ metadata.gz: d458e4ce5f50c4df97b8618c5ba97f95a3df6b36f35bf032868ac56c1d843a8be483080408f7b7fd2dc9eb1cc805356969adefeb9642f72d3ad1d6b23acfdec3
7
+ data.tar.gz: aef6e7d5bafde000682b901372ab897e53b57adc10810950ff02f3e9763027fcc286ae3d19f23ee4bc052a59bcb9bd38de5d2d5e28f67620cf20d133acd9e1ba
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # hive-runner-android
2
+ Android module for Hive Runner
3
+
4
+ ## Quick start
5
+ Install the hive-runner gem and set up your hive:
6
+
7
+ gem install hive-runner
8
+ hive_setup my_hive
9
+
10
+ When you are asked whether you want to add a module, press 1 and enter `android` as the module name. You will then be able to select the latest version from either Github or Rubygems.
11
+
12
+ Follow the configuration instructions and, in particular, ensure that the
13
+ `HIVE_CONFIG` variable is set.
14
+
15
+ Start the Hive daemon:
16
+
17
+ hived start
18
+
19
+ Determine the status of the Hive:
20
+
21
+ hived status
22
+
23
+ Stop the Hive:
24
+
25
+ hived stop
26
+
27
+ ## Configuration file
28
+
29
+ Example config file:
30
+
31
+ android:
32
+ name_stub: ANDROID_WORKER
33
+ port_range_size: 10
@@ -0,0 +1,179 @@
1
+ require 'hive/controller'
2
+ require 'hive/worker/android'
3
+ require 'device_api/android'
4
+
5
+ module Hive
6
+ class Controller
7
+ class Android < Controller
8
+
9
+ # Uses either DeviceAPI or DeviceDB to generate queue names for a device
10
+ def calculate_queue_names(device)
11
+ if device.is_a? DeviceAPI::Android::Device
12
+ queues = [
13
+ device.model,
14
+ device.manufacturer,
15
+ 'android',
16
+ "android-#{device.version}",
17
+ "android-#{device.version}-#{device.model}"
18
+ ]
19
+ else
20
+
21
+ queues = [
22
+ device['device_model'],
23
+ device['device_brand'],
24
+ device['os'],
25
+ "#{device['os']}-#{device['os_version']}",
26
+ "#{device['os']}-#{device['os_version']}-#{device['device_model']}"
27
+ ]
28
+
29
+ queues << device["features"] unless device["features"].empty?
30
+
31
+ queues.flatten
32
+ end
33
+ queues
34
+ end
35
+
36
+ def populate_queues(device)
37
+ queues = calculate_queue_names(device)
38
+
39
+ # Add the queue prefix if it hase been setup in the config
40
+ queues = queues.map { |a| "#{@config['queue_prefix']}-#{a}"} if @config['queue_prefix']
41
+
42
+ devicedb_queues = device['device_queues'].map { |d| d['name'] }
43
+ # Check to see if the queues have already been registered with this device
44
+ missing_queues = (queues - devicedb_queues) + (devicedb_queues - queues)
45
+ return if missing_queues.empty?
46
+
47
+ queues << missing_queues
48
+
49
+ queue_ids = queues.flatten.uniq.map { |queue| find_or_create_queue(queue) }
50
+
51
+ values = {
52
+ name: device['name'],
53
+ hive_id: device['hive_id'],
54
+ feature_list: device['features'],
55
+ device_queue_ids: queue_ids
56
+ }
57
+
58
+ Hive.devicedb('Device').edit(device['id'], values)
59
+ end
60
+
61
+ def find_or_create_queue(name)
62
+ queue = Hive.devicedb('Queue').find_by_name(name)
63
+ return queue.first['id'] unless queue.empty?
64
+
65
+ queue = create_queue(name, "#{name} queue created by Hive Runner")
66
+ queue['id'] unless queue.empty?
67
+ end
68
+
69
+ def create_queue(name, description)
70
+ queue_attributes = {
71
+ name: name,
72
+ description: description
73
+ }
74
+
75
+ Hive.devicedb('Queue').register(device_queue: queue_attributes )
76
+ end
77
+
78
+ def detect
79
+ devices = DeviceAPI::Android.devices
80
+ Hive.logger.debug('No devices attached') if devices.empty?
81
+ Hive.logger.debug("#{Time.now} Retrieving hive details")
82
+ hive_details = Hive.devicedb('Hive').find(Hive.id)
83
+ Hive.logger.debug("#{Time.now} Finished fetching hive details")
84
+
85
+ if hive_details.key?('devices')
86
+ # Update the 'cached' results from DeviceDB
87
+ @hive_details = hive_details
88
+ else
89
+ # DeviceDB isn't available - use the cached version
90
+ hive_details = @hive_details
91
+ end
92
+
93
+ if hive_details.is_a? Hash
94
+ # DeviceDB information is available, use it
95
+ hive_details['devices'].select {|a| a['os'] == 'android'}.each do |device|
96
+ registered_device = devices.select { |a| a.serial == device['serial'] && a.status != :unauthorized}
97
+ if registered_device.empty?
98
+ # A previously registered device isn't attached
99
+ Hive.logger.debug("Removing previously registered device - #{device}")
100
+ Hive.devicedb('Device').hive_disconnect(device['id'])
101
+ else
102
+ # A previously registered device is attached, poll it
103
+ Hive.logger.debug("#{Time.now} Polling attached device - #{device}")
104
+ Hive.devicedb('Device').poll(device['id'])
105
+ Hive.logger.debug("#{Time.now} Finished polling device")
106
+
107
+ # Make sure that this device has all the queues it should have
108
+ populate_queues(device)
109
+
110
+ devices = devices - registered_device
111
+ end
112
+ end
113
+
114
+ devices.each do |device|
115
+ register_new_device(device)
116
+ end
117
+
118
+ display_devices(hive_details)
119
+
120
+ hive_details['devices'].select {|a| a['os'] == 'android'}.collect do |hive_device|
121
+ self.create_device(hive_device)
122
+ end
123
+ else
124
+ # DeviceDB isn't available, use DeviceAPI instead
125
+ device_info = devices.map do |device|
126
+ {'id' => device.serial, 'serial' => device.serial, status: 'idle', devices: [{ device_queues: [ calculate_queue_names(device).map { |q| { name: q } } ]}]}
127
+ end
128
+
129
+ device_info.collect do |physical_device|
130
+ self.create_device(physical_device)
131
+ end
132
+ end
133
+ end
134
+
135
+ def register_new_device(device)
136
+ begin
137
+ Hive.logger.debug("Adding new Android device: #{device.model}")
138
+
139
+ attributes = {
140
+ os: 'android',
141
+ os_version: device.version,
142
+ serial: device.serial,
143
+ device_type: 'mobile',
144
+ device_model: device.model,
145
+ device_brand: device.manufacturer,
146
+ device_range: device.range,
147
+ hive: Hive.id
148
+ }
149
+ rescue DeviceAPI::Android::ADBCommandError
150
+ # If a device has been disconnected while we're trying to add it, the device_api
151
+ # gem will throw an error
152
+ Hive.logger.debug('Device disconnected while attempting to add')
153
+ end
154
+
155
+ registration = Hive.devicedb('Device').register(attributes)
156
+ Hive.devicedb('Device').hive_connect(registration['id'], Hive.id)
157
+ end
158
+
159
+ def display_devices(hive_details)
160
+ rows = []
161
+ if hive_details.key?('devices')
162
+ unless hive_details['devices'].empty?
163
+ rows = hive_details['devices'].map do |device|
164
+ [
165
+ "#{device['device_brand']} #{device['device_model']}",
166
+ device['serial'],
167
+ (device['device_queues'].map { |queue| queue['name']}).join("\n"),
168
+ device['status']
169
+ ]
170
+ end
171
+ end
172
+ end
173
+ table = Terminal::Table.new :headings => ['Device', 'Serial', 'Queue Name', 'Status'], :rows => rows
174
+
175
+ Hive.logger.info(table)
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,12 @@
1
+ require 'hive/device'
2
+
3
+ module Hive
4
+ class Device
5
+ class Android < Device
6
+ def initialize(config)
7
+ @identity = config['id']
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ require 'hive/diagnostic'
2
+ module Hive
3
+ class Diagnostic
4
+ class Android
5
+ class Battery < Diagnostic
6
+
7
+ def battery
8
+ self.device_api.battery_info
9
+ end
10
+
11
+ def diagnose
12
+ result = nil
13
+ battery_info = battery
14
+ begin
15
+ if config != nil && config.keys.count != 0
16
+ temperature = battery_info['temperature']
17
+ if temperature.to_i < config['temperature'].to_i
18
+ result = self.pass("Temperature: #{temperature}\n Battery status: OK", "battery")
19
+ else
20
+ result = self.fail("Battery temperature above threshold. Temperature: #{temperature} ", "battery")
21
+ end
22
+ else
23
+ result = self.pass("No parameter specified for battery", "battery")
24
+ end
25
+ rescue
26
+ Hive.logger.error("Invalid Battery Parameter")
27
+ raise InvalidParameterError.new("Invalid Parameter for battery") if !result
28
+ end
29
+ result
30
+ end
31
+
32
+ def repair(result)
33
+ result = self.fail("Battery temperature above threshold.", "battery")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ require 'hive/diagnostic'
2
+ module Hive
3
+ class Diagnostic
4
+ class Android
5
+ class Memory < Diagnostic
6
+
7
+ def memory
8
+ @memory = self.device_api.memory unless @memory
9
+ mem = @memory.mem_info
10
+ return {:free => mem.free.split(' ')[0],
11
+ :total => mem.total.split(' ')[0],
12
+ :used => mem.used.split(' ')[0], }
13
+ end
14
+
15
+ def diagnose
16
+ result = nil
17
+ operator = {:free => :>=, :used => :<= , :total => :==}
18
+ memory_status = memory
19
+ if config != nil && config.keys.count != 0
20
+ config.each do |key, value|
21
+ begin
22
+ if memory_status[:"#{key}"].to_i.send(operator[:"#{key}"], value.to_i)
23
+ result = self.pass("#{key.capitalize} Memory (#{memory_status[:"#{key}"]}) #{operator[:"#{key}"]} #{value}", "memory" )
24
+ else
25
+ result = self.fail("Error: #{key.capitalize} Memory (#{memory_status[:"#{key}"]}) is not #{operator[:"#{key}"]} #{value}", "memory")
26
+ break
27
+ end
28
+ rescue
29
+ Hive.logger.error("Invalid Memory Parameter")
30
+ raise InvalidParameterError.new("Invalid Parameter for memory") if !result
31
+ end
32
+ end
33
+ else
34
+ result = self.pass("No parameter specified", "memory")
35
+ end
36
+ result
37
+ end
38
+
39
+ def repair(result)
40
+ # Add repair for memory
41
+ self.fail("Cannot repair memory", "memory")
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ require 'hive/diagnostic'
2
+ module Hive
3
+ class Diagnostic
4
+ class Android
5
+ class Uptime < Diagnostic
6
+
7
+ def diagnose
8
+ if config.has_key?(:reboot_timeout)
9
+ uptime = self.device_api.uptime
10
+ if uptime < config[:reboot_timeout]
11
+ self.pass("Time for next reboot: #{config[:reboot_timeout] - uptime}s", "Reboot")
12
+ else
13
+ self.fail("Reboot required", "Reboot")
14
+ end
15
+ else
16
+ self.pass("Not configured for reboot", "Reboot")
17
+ end
18
+ end
19
+
20
+ def repair(result)
21
+ Hive.logger.info("Rebooting the device")
22
+ begin
23
+ self.device_api.reboot
24
+ sleep 30
25
+ rescue
26
+ Hive.logger.error("Device not found")
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ require 'hive/diagnostic'
2
+ module Hive
3
+ class Diagnostic
4
+ class Android
5
+ class Wifi < Diagnostic
6
+
7
+ def wifi
8
+ wifi_details = self.device_api.wifi_status
9
+ return {:status => wifi_details[:status].scan(/^[^\/]*/)[0], :access_point => wifi_details[:access_point]}
10
+ end
11
+
12
+ def diagnose
13
+ result = nil
14
+ wifi_status = wifi
15
+
16
+ if wifi_status[:access_point].capitalize == "Xxxx"
17
+ result = self.pass("Kindle returns wifi 'xxxx'", "wifi")
18
+ return result
19
+ end
20
+
21
+ config.each do |key, value|
22
+ if config != nil && config.keys.count != 0
23
+ begin
24
+ if wifi_status[:"#{key}"].capitalize == value.capitalize
25
+ result = self.pass("#{key.capitalize} : #{wifi_status[:"#{key}"]}", "wifi" )
26
+ else
27
+ result = self.fail(" Error: #{key.capitalize} : #{wifi_status[:"#{key}"]} ", "wifi")
28
+ break
29
+ end
30
+ rescue
31
+ Hive.logger.error("Invalid Parameter for Wifi #{key}")
32
+ raise InvalidParameter.new("Invalid Wifi Parameter for Wifi: #{key}") if !result
33
+ end
34
+ else
35
+ result = self.pass("No parameter specified", "wifi")
36
+ end
37
+ end
38
+ result
39
+ end
40
+
41
+ def repair(result)
42
+ Hive.logger.info("Trying to repair wifi")
43
+ options = {:apk => '/path/to/apk/to/toggle/wifi', :package => '/pkg/name/ex: com.wifi.togglewifi'}
44
+ begin
45
+ self.device_api.install(options[:apk])
46
+ self.device_api.start_intent("-n com.wifi.togglewifi/.MainActivity -e wifi true")
47
+ sleep 5
48
+ self.device_api.uninstall(options[:package])
49
+ rescue
50
+ Hive.logger.error("Unable to fix wifi issue")
51
+ end
52
+ diagnose
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ require 'hive/messages'
2
+
3
+ module Hive
4
+ module Messages
5
+ class AndroidJob < Hive::Messages::Job
6
+ def build
7
+ self.target.symbolize_keys[:build]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,102 @@
1
+ require 'hive/worker'
2
+ require 'hive/messages/android_job'
3
+
4
+ module Hive
5
+ class PortReserver
6
+ attr_accessor :ports
7
+ def initialize
8
+ self.ports = {}
9
+ end
10
+
11
+ def reserve(queue_name)
12
+ self.ports[queue_name] = yield
13
+ self.ports[queue_name]
14
+ end
15
+ end
16
+
17
+ class Worker
18
+ class Android < Worker
19
+
20
+ attr_accessor :device
21
+
22
+ def initialize(device)
23
+ @worker_ports = PortReserver.new
24
+ begin
25
+ device.merge!({"device_api" => DeviceAPI::Android.device(device['serial'])})
26
+ rescue DeviceAPI::Android::ADBCommandError
27
+ Hive.logger.info("Device disconnected during worker initialization")
28
+ end
29
+ set_device_status('idle')
30
+ self.device = device
31
+ super(device)
32
+ end
33
+
34
+ def adb_port
35
+ # Assign adb port for this worker
36
+ return @adb_port unless @adb_port.nil?
37
+ @adb_port = @port_allocator.allocate_port
38
+ end
39
+
40
+ def pre_script(job, file_system, script)
41
+ set_device_status('busy')
42
+ script.set_env "TEST_SERVER_PORT", adb_port
43
+
44
+ # TODO: Allow the scheduler to specify the ports to use
45
+ script.set_env "CHARLES_PROXY_PORT", @worker_ports.reserve(queue_name: 'Charles') { @port_allocator.allocate_port }
46
+ script.set_env "APPIUM_PORT", @worker_ports.reserve(queue_name: 'Appium') { @port_allocator.allocate_port }
47
+ script.set_env "BOOTSTRAP_PORT", @worker_ports.reserve(queue_name: 'Bootstrap') { @port_allocator.allocate_port }
48
+ script.set_env "CHROMEDRIVER_PORT", @worker_ports.reserve(queue_name: 'Chromedriver') { @port_allocator.allocate_port }
49
+
50
+ script.set_env 'ADB_DEVICE_ARG', self.device['serial']
51
+
52
+ FileUtils.mkdir(file_system.home_path + '/build')
53
+ apk_path = file_system.home_path + '/build/' + 'build.apk'
54
+
55
+ script.set_env "APK_PATH", apk_path
56
+ if job.build
57
+ file_system.fetch_build(job.build, apk_path)
58
+ DeviceAPI::Android::Signing.sign_apk({apk: apk_path, resign: true})
59
+ end
60
+
61
+ DeviceAPI::Android.device(device['serial']).unlock
62
+
63
+ "#{self.device['serial']} #{@worker_ports.ports['Appium']} #{apk_path} #{file_system.results_path}"
64
+ end
65
+
66
+ def job_message_klass
67
+ Hive::Messages::AndroidJob
68
+ end
69
+
70
+ def post_script(job, file_system, script)
71
+ @log.info('Post script')
72
+ @worker_ports.ports.each do |name, port|
73
+ @port_allocator.release_port(port)
74
+ end
75
+ set_device_status('idle')
76
+ end
77
+
78
+ def device_status
79
+ details = Hive.devicedb('Device').find(@options['id'])
80
+ if details.key?('status')
81
+ @state = details['status']
82
+ else
83
+ @state
84
+ end
85
+ end
86
+
87
+ def set_device_status(status)
88
+ @state = status
89
+ begin
90
+ details = Hive.devicedb('Device').poll(@options['id'], status)
91
+ if details.key?('status')
92
+ details['status']
93
+ else
94
+ @state
95
+ end
96
+ rescue
97
+ @state
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hive-runner-android
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jon Wilson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: device_api-android
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hive-runner
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: terminal-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ description: The Android controller module for Hive Runner
56
+ email: jon.wilson01@bbc.co.uk
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - README.md
62
+ - lib/hive/controller/android.rb
63
+ - lib/hive/device/android.rb
64
+ - lib/hive/diagnostic/android/battery.rb
65
+ - lib/hive/diagnostic/android/memory.rb
66
+ - lib/hive/diagnostic/android/uptime.rb
67
+ - lib/hive/diagnostic/android/wifi.rb
68
+ - lib/hive/messages/android_job.rb
69
+ - lib/hive/worker/android.rb
70
+ homepage: https://github.com/bbc/hive-runner-android
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.4.8
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Hive Runner Android
94
+ test_files: []