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