Skip to content

Commit c2eec1c

Browse files
Add new test options (#11)
1 parent e8a336d commit c2eec1c

File tree

12 files changed

+190
-69
lines changed

12 files changed

+190
-69
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

5+
## [1.3.0](https://github.com/alteral/xcmonkey/releases/tag/1.3.0)
6+
7+
_January 29, 2023_
8+
9+
### 🔄 Changed
10+
11+
- `duration` option is renamed to `event-count`
12+
13+
### ✅ Added
14+
15+
- New test options that allow to
16+
- specify gestures to exclude from the test
17+
- set up throttle between events
18+
- ignore app crashes
19+
520
## [1.2.0](https://github.com/alteral/xcmonkey/releases/tag/1.2.0)
621

722
_January 23, 2023_

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ gem 'rubocop-rake', '0.6.0'
1717
gem 'rubocop-require_tools'
1818
gem 'rubocop-rspec', '2.15.0'
1919
gem 'simplecov'
20+
gem 'solargraph'

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ gem 'xcmonkey'
3939
### To run a stress test
4040

4141
```bash
42-
$ xcmonkey test --duration 100 --bundle-id "com.apple.Maps" --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
42+
$ xcmonkey test --event-count 100 --bundle-id "com.apple.Maps" --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
4343

4444
12:44:19.343: Device info: {
4545
"name": "iPhone 14 Pro",
@@ -97,6 +97,26 @@ xcmonkey repeat --session-path "./xcmonkey-session.json"
9797
xcmonkey describe -x 20 -y 625 --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
9898
```
9999

100+
### Test options reference
101+
102+
The table below lists all options you can include on the `xcmonkey test` command line.
103+
104+
| Category | Option | Description | Default |
105+
| --- | --- | --- | --- |
106+
| **General** | `-h, --help` | Display help documentation | |
107+
| | `-v, --version` | Display version information | |
108+
| | `-t, --trace` | Display backtrace when an error occurs | |
109+
| **Events** | `-u, --udid <string>` | Set device UDID | |
110+
| | `-b, --bundle-id <string>` | Set target bundle identifier | |
111+
| | `-s, --session-path <string>` | Path where test session should be saved | |
112+
| | `-e, --event-count <integer>` | Set events count | `60` |
113+
| | `--exclude-taps` | Exclude taps from gestures list | `false` |
114+
| | `--exclude-swipes` | Exclude swipes from gestures list | `false` |
115+
| | `--exclude-presses` | Exclude presses from gestures list | `false` |
116+
| | `--disable-simulator-keyboard` | Should simulator keyboard be disable? | `false` |
117+
| **Debugging** | `--ignore-crashes` | Should app crashes be ignored? | `false` |
118+
| | `--throttle <milliseconds>` | Fixed delay between events | `0` |
119+
100120
## [fastlane](https://github.com/fastlane/fastlane) integration
101121

102122
To run *xcmonkey* from *fastlane*, add the following code to your `Fastfile`:
@@ -106,7 +126,7 @@ require 'xcmonkey'
106126

107127
lane :test do
108128
Xcmonkey.new(
109-
duration: 100,
129+
event_count: 100,
110130
bundle_id: 'com.apple.Maps',
111131
udid: '413EA256-CFFB-4312-94A6-12592BEE4CBA'
112132
).run

bin/xcmonkey

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,26 @@ class Xcmonkey
1717
c.description = 'Runs monkey test'
1818
c.option('-u', '--udid STRING', String, 'Set device UDID')
1919
c.option('-b', '--bundle-id STRING', String, 'Set target bundle identifier')
20-
c.option('-d', '--duration SECONDS', Integer, 'Test duration in seconds. Defaults to `60`')
21-
c.option('-k', '--enable-simulator-keyboard', 'Should simulator keyboard be enabled? Defaults to `true`')
22-
c.option('-s', '--session-path STRING', String, 'Path where monkey testing session should be saved. Defaults to current directory')
20+
c.option('-e', '--event-count NUMBER', Integer, 'Set events count. Defaults to `60`')
21+
c.option('-s', '--session-path STRING', String, 'Path where test session should be saved')
22+
c.option('--throttle MILLISECONDS', Integer, 'Fixed delay between events in milliseconds. Defaults to `0`')
23+
c.option('--exclude-taps', 'Exclude taps from gestures list. Defaults to `false`')
24+
c.option('--exclude-swipes', 'Exclude swipes from gestures list. Defaults to `false`')
25+
c.option('--exclude-presses', 'Exclude presses from gestures list. Defaults to `false`')
26+
c.option('--ignore-crashes', 'Should app crashes be ignored? Defaults to `false`')
27+
c.option('--disable-simulator-keyboard', 'Should simulator keyboard be disable? Defaults to `false`')
2328
c.action do |_, options|
2429
params = {
2530
udid: options.udid,
2631
bundle_id: options.bundle_id,
27-
duration: options.duration,
32+
event_count: options.event_count,
33+
throttle: options.throttle,
2834
session_path: options.session_path,
29-
enable_simulator_keyboard: options.enable_simulator_keyboard
35+
exclude_taps: options.exclude_taps,
36+
exclude_swipes: options.exclude_swipes,
37+
exclude_presses: options.exclude_presses,
38+
ignore_crashes: options.ignore_crashes,
39+
disable_simulator_keyboard: options.disable_simulator_keyboard
3040
}
3141
Xcmonkey.new(params).run
3242
end

fastlane/Fastfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ lane :release do
88
set_github_release(
99
repository_name: 'alteral/xcmonkey',
1010
api_token: ENV.fetch("GITHUB_TOKEN", nil),
11-
name: "xcmonkey v#{version}",
12-
tag_name: "v#{version}",
13-
description: "v#{version}",
11+
name: version,
12+
tag_name: version,
13+
description: "See [CHANGELOG.md](https://github.com/alteral/xcmonkey/blob/main/CHANGELOG.md##{version.delete('.')}})",
1414
commitish: git_branch,
1515
upload_assets: [gem_path]
1616
)

lib/xcmonkey.rb

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,37 @@
77
require_relative 'xcmonkey/driver'
88

99
class Xcmonkey
10-
attr_accessor :driver
10+
attr_accessor :params, :driver
1111

1212
def initialize(params)
13-
params[:session_path] = Dir.pwd if params[:session_path].nil?
14-
params[:duration] = 60 if params[:duration].nil?
15-
params[:enable_simulator_keyboard] = true if params[:enable_simulator_keyboard].nil?
16-
ensure_required_params(params)
13+
params[:event_count] = 60 if params[:event_count].nil?
14+
params[:ignore_crashes] = false if params[:ignore_crashes].nil?
15+
params[:disable_simulator_keyboard] = false if params[:disable_simulator_keyboard].nil?
16+
self.params = params
1717
self.driver = Driver.new(params)
18+
ensure_required_params
1819
end
1920

2021
def run
2122
driver.monkey_test(gestures)
2223
end
2324

2425
def gestures
25-
taps = [:precise_tap, :blind_tap] * 10
26-
swipes = [:precise_swipe, :blind_swipe] * 5
27-
presses = [:precise_press, :blind_press]
26+
taps = params[:exclude_taps] ? [] : [:precise_tap, :blind_tap] * 10
27+
swipes = params[:exclude_swipes] ? [] : [:precise_swipe, :blind_swipe] * 5
28+
presses = params[:exclude_presses] ? [] : [:precise_press, :blind_press]
2829
taps + swipes + presses
2930
end
3031

31-
def ensure_required_params(params)
32+
def ensure_required_params
3233
Logger.error('UDID should be provided') if params[:udid].nil?
3334

3435
Logger.error('Bundle identifier should be provided') if params[:bundle_id].nil?
3536

36-
Logger.error('Session path should be a directory') if params[:session_path].nil? || !File.directory?(params[:session_path])
37+
Logger.error('Session path should be a directory') if params[:session_path] && !File.directory?(params[:session_path])
3738

38-
if params[:duration].nil? || !params[:duration].kind_of?(Integer) || !params[:duration].positive?
39-
Logger.error('Duration must be Integer and not less than 1 second')
39+
if params[:event_count].nil? || !params[:event_count].kind_of?(Integer) || !params[:event_count].positive?
40+
Logger.error('Event count must be Integer and not less than 1')
4041
end
4142
end
4243
end

lib/xcmonkey/driver.rb

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
class Driver
2-
attr_accessor :udid, :bundle_id, :enable_simulator_keyboard, :session_duration, :session_path, :session_actions
2+
attr_accessor :udid, :bundle_id, :disable_simulator_keyboard, :event_count, :session_path, :session_actions, :ignore_crashes, :throttle
33

44
def initialize(params)
55
self.udid = params[:udid]
6+
self.throttle = params[:throttle]
67
self.bundle_id = params[:bundle_id]
7-
self.session_duration = params[:duration]
8+
self.event_count = params[:event_count]
89
self.session_path = params[:session_path]
9-
self.enable_simulator_keyboard = params[:enable_simulator_keyboard]
10+
self.ignore_crashes = params[:ignore_crashes]
11+
self.disable_simulator_keyboard = params[:disable_simulator_keyboard]
1012
self.session_actions = params[:session_actions]
1113
@session = { params: params, actions: [] }
1214
ensure_driver_installed
@@ -23,10 +25,8 @@ def monkey_test_precondition
2325

2426
def monkey_test(gestures)
2527
monkey_test_precondition
26-
app_elements = describe_ui.shuffle
27-
current_time = Time.now
28-
counter = 0
29-
while Time.now < current_time + session_duration
28+
event_count.times do |counter|
29+
app_elements = describe_ui.shuffle
3030
el1_coordinates = central_coordinates(app_elements.first)
3131
el2_coordinates = central_coordinates(app_elements.last)
3232
case gestures.sample
@@ -53,18 +53,14 @@ def monkey_test(gestures)
5353
else
5454
next
5555
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
59-
app_elements = describe_ui.shuffle
56+
checkup(counter)
6057
end
6158
save_session
6259
end
6360

6461
def repeat_monkey_test
6562
monkey_test_precondition
66-
counter = 0
67-
session_actions.each do |action|
63+
session_actions.each_with_index do |action, counter|
6864
case action['type']
6965
when 'tap'
7066
tap(coordinates: { x: action['x'], y: action['y'] })
@@ -79,10 +75,16 @@ def repeat_monkey_test
7975
else
8076
next
8177
end
82-
detect_app_state_change
83-
track_running_apps if counter % 5 == 0
84-
counter += 1
78+
checkup(counter)
79+
end
80+
end
81+
82+
def checkup(counter)
83+
detect_app_state_change
84+
if counter % 5 == 0 || throttle.to_i > 0 # Track running apps after every 5th action
85+
track_running_apps # (unless `throttle` was provided) to speed up the test
8586
end
87+
check_speed_limit
8688
end
8789

8890
def describe_ui
@@ -115,7 +117,7 @@ def shutdown_simulator
115117

116118
def configure_simulator_keyboard
117119
shutdown_simulator
118-
keyboard_status = enable_simulator_keyboard ? 0 : 1
120+
keyboard_status = disable_simulator_keyboard ? 1 : 0
119121
`defaults write com.apple.iphonesimulator ConnectHardwareKeyboard #{keyboard_status}`
120122
end
121123

@@ -165,10 +167,8 @@ def press(coordinates:, duration:)
165167
end
166168

167169
def swipe(start_coordinates:, end_coordinates:, duration:)
168-
Logger.info(
169-
"Swipe (#{duration}s):",
170-
payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
171-
)
170+
payload = "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
171+
Logger.info("Swipe (#{duration}s):", payload: payload)
172172
unless session_actions
173173
@session[:actions] << {
174174
type: :swipe,
@@ -188,8 +188,8 @@ def central_coordinates(element)
188188
x = (frame['x'] + (frame['width'] / 2)).abs.to_i
189189
y = (frame['y'] + (frame['height'] / 2)).abs.to_i
190190
{
191-
x: x > screen_size[:width].to_i ? rand(0..screen_size[:width].to_i) : x,
192-
y: y > screen_size[:height].to_i ? rand(0..screen_size[:height].to_i) : y
191+
x: x > screen_size[:width].to_i ? random_coordinates[:x] : x,
192+
y: y > screen_size[:height].to_i ? random_coordinates[:y] : y
193193
}
194194
end
195195

@@ -222,6 +222,8 @@ def press_duration
222222
end
223223

224224
def save_session
225+
return if session_path.nil?
226+
225227
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
226228
end
227229

@@ -236,6 +238,7 @@ def track_running_apps
236238
return if new_apps.empty?
237239

238240
launch_app(target_bundle_id: bundle_id)
241+
239242
new_apps.each do |id|
240243
Logger.warn("Shutting down: #{id}")
241244
terminate_app(id)
@@ -249,7 +252,7 @@ def detect_app_state_change
249252

250253
target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
251254

252-
if target_app_is_running
255+
if target_app_is_running || ignore_crashes
253256
launch_app(target_bundle_id: bundle_id)
254257
else
255258
save_session
@@ -262,6 +265,10 @@ def detect_app_in_background
262265
current_app_label.nil? || current_app_label.strip.empty?
263266
end
264267

268+
def check_speed_limit
269+
sleep(throttle / 1000.0) if throttle.to_i > 0
270+
end
271+
265272
private
266273

267274
def ensure_driver_installed

lib/xcmonkey/repeater.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Repeater
2-
attr_accessor :udid, :bundle_id, :enable_simulator_keyboard, :actions
2+
attr_accessor :udid, :bundle_id, :disable_simulator_keyboard, :ignore_crashes, :actions, :throttle
33

44
def initialize(params)
55
validate_session(params[:session_path])
@@ -8,8 +8,10 @@ def initialize(params)
88
def run
99
params = {
1010
udid: udid,
11+
throttle: throttle,
1112
bundle_id: bundle_id,
12-
enable_simulator_keyboard: enable_simulator_keyboard,
13+
ignore_crashes: ignore_crashes,
14+
disable_simulator_keyboard: disable_simulator_keyboard,
1315
session_actions: actions
1416
}
1517
Driver.new(params).repeat_monkey_test
@@ -34,6 +36,10 @@ def validate_session(session_path)
3436
self.bundle_id = session['params']['bundle_id']
3537
Logger.error('Provided session is not valid: `bundle_id` should not be `nil`') if bundle_id.nil?
3638

37-
self.enable_simulator_keyboard = session['params']['enable_simulator_keyboard']
39+
self.throttle = session['params']['throttle']
40+
41+
self.ignore_crashes = session['params']['ignore_crashes']
42+
43+
self.disable_simulator_keyboard = session['params']['disable_simulator_keyboard']
3844
end
3945
end

lib/xcmonkey/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class Xcmonkey
2-
VERSION = '1.2.0'
2+
VERSION = '1.3.0'
33
end

0 commit comments

Comments
 (0)