@@ -16,15 +16,16 @@ def monkey_test_precondition
1616 puts
1717 ensure_device_exists
1818 ensure_app_installed
19- terminate_app
20- open_home_screen ( with_tracker : true )
21- launch_app
19+ terminate_app ( bundle_id )
20+ launch_app ( target_bundle_id : bundle_id , wait_for_state_update : true )
21+ @running_apps = list_running_apps
2222 end
2323
2424 def monkey_test ( gestures )
2525 monkey_test_precondition
2626 app_elements = describe_ui . shuffle
2727 current_time = Time . now
28+ counter = 0
2829 while Time . now < current_time + session_duration
2930 el1_coordinates = central_coordinates ( app_elements . first )
3031 el2_coordinates = central_coordinates ( app_elements . last )
@@ -52,17 +53,17 @@ def monkey_test(gestures)
5253 else
5354 next
5455 end
56+ detect_app_state_change
57+ track_running_apps if counter % 5 == 0 # Track running apps after every 5th action to speed up the test
58+ counter += 1
5559 app_elements = describe_ui . shuffle
56- next unless app_elements . include? ( @home_tracker )
57-
58- save_session
59- Logger . error ( 'App lost' )
6060 end
6161 save_session
6262 end
6363
6464 def repeat_monkey_test
6565 monkey_test_precondition
66+ counter = 0
6667 session_actions . each do |action |
6768 case action [ 'type' ]
6869 when 'tap'
@@ -78,15 +79,12 @@ def repeat_monkey_test
7879 else
7980 next
8081 end
81- Logger . error ( 'App lost' ) if describe_ui . shuffle . include? ( @home_tracker )
82+ detect_app_state_change
83+ track_running_apps if counter % 5 == 0
84+ counter += 1
8285 end
8386 end
8487
85- def open_home_screen ( with_tracker : false )
86- `idb ui button --udid #{ udid } HOME`
87- detect_home_unique_element if with_tracker
88- end
89-
9088 def describe_ui
9189 JSON . parse ( `idb ui describe-all --udid #{ udid } ` )
9290 end
@@ -97,13 +95,13 @@ def describe_point(x, y)
9795 point_info
9896 end
9997
100- def launch_app
101- `idb launch --udid #{ udid } #{ bundle_id } `
102- wait_until_app_launched
98+ def launch_app ( target_bundle_id : , wait_for_state_update : false )
99+ `idb launch --udid #{ udid } #{ target_bundle_id } `
100+ wait_until_app_launched ( target_bundle_id ) if wait_for_state_update
103101 end
104102
105- def terminate_app
106- `idb terminate --udid #{ udid } #{ bundle_id } 2>/dev/null`
103+ def terminate_app ( target_bundle_id )
104+ `idb terminate --udid #{ udid } #{ target_bundle_id } 2>/dev/null`
107105 end
108106
109107 def boot_simulator
@@ -130,6 +128,10 @@ def list_apps
130128 `idb list-apps --udid #{ udid } --json` . split ( "\n " ) . map! { |app | JSON . parse ( app ) }
131129 end
132130
131+ def list_running_apps
132+ list_apps . select { |app | app [ 'process_state' ] == 'Running' }
133+ end
134+
133135 def ensure_app_installed
134136 return if list_apps . any? { |app | app [ 'bundle_id' ] == bundle_id }
135137
@@ -144,6 +146,9 @@ def ensure_device_exists
144146 if device [ 'type' ] == 'simulator'
145147 configure_simulator_keyboard
146148 boot_simulator
149+ else
150+ Logger . error ( 'xcmonkey does not support real devices yet. ' \
151+ 'For more information see https://github.com/alteral/xcmonkey/issues/7' )
147152 end
148153 end
149154
@@ -220,28 +225,57 @@ def save_session
220225 File . write ( "#{ session_path } /xcmonkey-session.json" , JSON . pretty_generate ( @session ) )
221226 end
222227
223- private
228+ # This function takes ≈200ms
229+ def track_running_apps
230+ current_list_of_running_apps = list_running_apps
231+ if @running_apps != current_list_of_running_apps
232+ currently_running_bundle_ids = current_list_of_running_apps . map { |app | app [ 'bundle_id' ] }
233+ previously_running_bundle_ids = @running_apps . map { |app | app [ 'bundle_id' ] }
234+ new_apps = currently_running_bundle_ids - previously_running_bundle_ids
224235
225- def ensure_driver_installed
226- Logger . error ( "'idb' doesn't seem to be installed" ) if `which idb` . strip . empty?
236+ return if new_apps . empty?
237+
238+ launch_app ( target_bundle_id : bundle_id )
239+ new_apps . each do |id |
240+ Logger . warn ( "Shutting down: #{ id } " )
241+ terminate_app ( id )
242+ end
243+ end
227244 end
228245
229- def detect_home_unique_element
230- @home_tracker ||= describe_ui . reverse . detect do |el |
231- sleep ( 1 )
232- !el [ 'AXUniqueId' ] . nil? && !el [ 'AXUniqueId' ] . empty? && el [ 'type' ] == 'Button'
246+ # This function takes ≈300ms
247+ def detect_app_state_change
248+ return unless detect_app_in_background
249+
250+ target_app_is_running = list_running_apps . any? { |app | app [ 'bundle_id' ] == bundle_id }
251+
252+ if target_app_is_running
253+ launch_app ( target_bundle_id : bundle_id )
254+ else
255+ save_session
256+ Logger . error ( "Target app has crashed or been terminated" )
233257 end
234- @home_tracker
235258 end
236259
237- def wait_until_app_launched
260+ def detect_app_in_background
261+ current_app_label = describe_ui . detect { |el | el [ 'type' ] == 'Application' } [ 'AXLabel' ]
262+ current_app_label . nil? || current_app_label . strip . empty?
263+ end
264+
265+ private
266+
267+ def ensure_driver_installed
268+ Logger . error ( "'idb' doesn't seem to be installed" ) if `which idb` . strip . empty?
269+ end
270+
271+ def wait_until_app_launched ( target_bundle_id )
238272 app_is_running = false
239273 current_time = Time . now
240274 while !app_is_running && Time . now < current_time + 5
241- app_info = list_apps . detect { |app | app [ 'bundle_id' ] == bundle_id }
275+ app_info = list_apps . detect { |app | app [ 'bundle_id' ] == target_bundle_id }
242276 app_is_running = app_info && app_info [ 'process_state' ] == 'Running'
243277 end
244- Logger . error ( "Can't run the app #{ bundle_id } " ) unless app_is_running
278+ Logger . error ( "Can't run the app #{ target_bundle_id } " ) unless app_is_running
245279 Logger . info ( 'App info:' , payload : JSON . pretty_generate ( app_info ) )
246280 end
247281end
0 commit comments