lib/xcmonkey/driver.rb in xcmonkey-1.1.0 vs lib/xcmonkey/driver.rb in xcmonkey-1.2.0
- old
+ new
@@ -14,19 +14,20 @@
def monkey_test_precondition
puts
ensure_device_exists
ensure_app_installed
- terminate_app
- open_home_screen(with_tracker: true)
- launch_app
+ terminate_app(bundle_id)
+ launch_app(target_bundle_id: bundle_id, wait_for_state_update: true)
+ @running_apps = list_running_apps
end
def monkey_test(gestures)
monkey_test_precondition
app_elements = describe_ui.shuffle
current_time = Time.now
+ counter = 0
while Time.now < current_time + session_duration
el1_coordinates = central_coordinates(app_elements.first)
el2_coordinates = central_coordinates(app_elements.last)
case gestures.sample
when :precise_tap
@@ -50,21 +51,21 @@
duration: swipe_duration
)
else
next
end
+ detect_app_state_change
+ track_running_apps if counter % 5 == 0 # Track running apps after every 5th action to speed up the test
+ counter += 1
app_elements = describe_ui.shuffle
- next unless app_elements.include?(@home_tracker)
-
- save_session
- Logger.error('App lost')
end
save_session
end
def repeat_monkey_test
monkey_test_precondition
+ counter = 0
session_actions.each do |action|
case action['type']
when 'tap'
tap(coordinates: { x: action['x'], y: action['y'] })
when 'press'
@@ -76,36 +77,33 @@
duration: action['duration']
)
else
next
end
- Logger.error('App lost') if describe_ui.shuffle.include?(@home_tracker)
+ detect_app_state_change
+ track_running_apps if counter % 5 == 0
+ counter += 1
end
end
- def open_home_screen(with_tracker: false)
- `idb ui button --udid #{udid} HOME`
- detect_home_unique_element if with_tracker
- end
-
def describe_ui
JSON.parse(`idb ui describe-all --udid #{udid}`)
end
def describe_point(x, y)
point_info = JSON.parse(`idb ui describe-point --udid #{udid} #{x} #{y}`)
Logger.info("x:#{x} y:#{y} point info:", payload: JSON.pretty_generate(point_info))
point_info
end
- def launch_app
- `idb launch --udid #{udid} #{bundle_id}`
- wait_until_app_launched
+ def launch_app(target_bundle_id:, wait_for_state_update: false)
+ `idb launch --udid #{udid} #{target_bundle_id}`
+ wait_until_app_launched(target_bundle_id) if wait_for_state_update
end
- def terminate_app
- `idb terminate --udid #{udid} #{bundle_id} 2>/dev/null`
+ def terminate_app(target_bundle_id)
+ `idb terminate --udid #{udid} #{target_bundle_id} 2>/dev/null`
end
def boot_simulator
`idb boot #{udid}`
Logger.error("Failed to boot #{udid}") if device_info['state'] != 'Booted'
@@ -128,10 +126,14 @@
def list_apps
`idb list-apps --udid #{udid} --json`.split("\n").map! { |app| JSON.parse(app) }
end
+ def list_running_apps
+ list_apps.select { |app| app['process_state'] == 'Running' }
+ end
+
def ensure_app_installed
return if list_apps.any? { |app| app['bundle_id'] == bundle_id }
Logger.error("App #{bundle_id} is not installed on device #{udid}")
end
@@ -142,10 +144,13 @@
Logger.info('Device info:', payload: JSON.pretty_generate(device))
if device['type'] == 'simulator'
configure_simulator_keyboard
boot_simulator
+ else
+ Logger.error('xcmonkey does not support real devices yet. ' \
+ 'For more information see https://github.com/alteral/xcmonkey/issues/7')
end
end
def tap(coordinates:)
Logger.info('Tap:', payload: JSON.pretty_generate(coordinates))
@@ -218,30 +223,59 @@
def save_session
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
end
- private
+ # This function takes ≈200ms
+ def track_running_apps
+ current_list_of_running_apps = list_running_apps
+ if @running_apps != current_list_of_running_apps
+ currently_running_bundle_ids = current_list_of_running_apps.map { |app| app['bundle_id'] }
+ previously_running_bundle_ids = @running_apps.map { |app| app['bundle_id'] }
+ new_apps = currently_running_bundle_ids - previously_running_bundle_ids
- def ensure_driver_installed
- Logger.error("'idb' doesn't seem to be installed") if `which idb`.strip.empty?
+ return if new_apps.empty?
+
+ launch_app(target_bundle_id: bundle_id)
+ new_apps.each do |id|
+ Logger.warn("Shutting down: #{id}")
+ terminate_app(id)
+ end
+ end
end
- def detect_home_unique_element
- @home_tracker ||= describe_ui.reverse.detect do |el|
- sleep(1)
- !el['AXUniqueId'].nil? && !el['AXUniqueId'].empty? && el['type'] == 'Button'
+ # This function takes ≈300ms
+ def detect_app_state_change
+ return unless detect_app_in_background
+
+ target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
+
+ if target_app_is_running
+ launch_app(target_bundle_id: bundle_id)
+ else
+ save_session
+ Logger.error("Target app has crashed or been terminated")
end
- @home_tracker
end
- def wait_until_app_launched
+ def detect_app_in_background
+ current_app_label = describe_ui.detect { |el| el['type'] == 'Application' }['AXLabel']
+ current_app_label.nil? || current_app_label.strip.empty?
+ end
+
+ private
+
+ def ensure_driver_installed
+ Logger.error("'idb' doesn't seem to be installed") if `which idb`.strip.empty?
+ end
+
+ def wait_until_app_launched(target_bundle_id)
app_is_running = false
current_time = Time.now
while !app_is_running && Time.now < current_time + 5
- app_info = list_apps.detect { |app| app['bundle_id'] == bundle_id }
+ app_info = list_apps.detect { |app| app['bundle_id'] == target_bundle_id }
app_is_running = app_info && app_info['process_state'] == 'Running'
end
- Logger.error("Can't run the app #{bundle_id}") unless app_is_running
+ Logger.error("Can't run the app #{target_bundle_id}") unless app_is_running
Logger.info('App info:', payload: JSON.pretty_generate(app_info))
end
end