xcmonkey 1.1.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/stale.yml +18 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile +14 -0
- data/README.md +23 -3
- data/bin/xcmonkey +15 -5
- data/fastlane/Fastfile +3 -3
- data/lib/xcmonkey/driver.rb +83 -42
- data/lib/xcmonkey/repeater.rb +9 -3
- data/lib/xcmonkey/version.rb +1 -1
- data/lib/xcmonkey.rb +13 -12
- data/spec/describer_spec.rb +4 -2
- data/spec/driver_spec.rb +111 -32
- data/spec/repeater_spec.rb +5 -5
- data/spec/xcmonkey_spec.rb +38 -10
- data/xcmonkey.gemspec +16 -26
- metadata +4 -170
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be0e0b66cbc58704452a6defce61d6bd9f7ba88f7489796f4b7cc6a4f4489a11
|
4
|
+
data.tar.gz: 8551a6fd090f89cf20c2cd2e81bc8edc4856b9eb5516a50227db772cb0f013eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f3e1fe8bb0bccfd056c9f7087c32c2cd06adee0d0dca6a7b7ba6b8e038130e8d0f991c1e3bcb8d4b7c6d1c9ad63a5ae1fa718e979baaa8ff8f41ba98ae03b56
|
7
|
+
data.tar.gz: 1aabd404928c4cbc3e9d7a3fbe0c9f88ca155c25f745eb7e708cca7330a340740908343af231bc9fd9e5759d380f31816a920a53c40a6be6dbfa1916b50363db
|
data/.github/stale.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Number of days of inactivity before an issue becomes stale
|
2
|
+
daysUntilStale: 14
|
3
|
+
# Number of days of inactivity before a stale issue is closed
|
4
|
+
daysUntilClose: 7
|
5
|
+
# Issues with these labels will never be considered stale
|
6
|
+
exemptLabels:
|
7
|
+
- important
|
8
|
+
# Label to use when marking an issue as stale
|
9
|
+
staleLabel: stale
|
10
|
+
# Comment to post when marking an issue as stale. Set to `false` to disable
|
11
|
+
markComment: >
|
12
|
+
This issue has been automatically marked as stale because it has not had
|
13
|
+
recent activity. It will be closed if no further activity occurs. Thank you
|
14
|
+
for your contributions.
|
15
|
+
# Comment to post when closing a stale issue. Set to `false` to disable
|
16
|
+
closeComment: >
|
17
|
+
This issue has been automatically closed due to inactivity.
|
18
|
+
Please open a new issue if it's still valid.
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# xcmonkey changelog
|
2
|
+
|
3
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
4
|
+
|
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
|
+
|
20
|
+
## [1.2.0](https://github.com/alteral/xcmonkey/releases/tag/1.2.0)
|
21
|
+
|
22
|
+
_January 23, 2023_
|
23
|
+
|
24
|
+
### ✅ Added
|
25
|
+
|
26
|
+
- Force keep xcmonkey in the target app
|
27
|
+
|
28
|
+
## [1.1.0](https://github.com/alteral/xcmonkey/releases/tag/1.1.0)
|
29
|
+
|
30
|
+
_January 20, 2023_
|
31
|
+
|
32
|
+
### ✅ Added
|
33
|
+
|
34
|
+
- Support [fastlane](https://github.com/fastlane/fastlane) integration
|
35
|
+
|
36
|
+
## [1.0.0](https://github.com/alteral/xcmonkey/releases/tag/1.0.0)
|
37
|
+
|
38
|
+
_January 09, 2023_
|
39
|
+
|
40
|
+
### ✅ Added
|
41
|
+
|
42
|
+
- xcmonkey `test`, `repeat` and `describe` commands
|
data/Gemfile
CHANGED
@@ -4,3 +4,17 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in xcmonkey.gemspec
|
6
6
|
gemspec
|
7
|
+
|
8
|
+
gem 'bundler'
|
9
|
+
gem 'fasterer', '0.9.0'
|
10
|
+
gem 'fastlane'
|
11
|
+
gem 'rake'
|
12
|
+
gem 'rspec'
|
13
|
+
gem 'rspec_junit_formatter'
|
14
|
+
gem 'rubocop', '1.44.0'
|
15
|
+
gem 'rubocop-performance'
|
16
|
+
gem 'rubocop-rake', '0.6.0'
|
17
|
+
gem 'rubocop-require_tools'
|
18
|
+
gem 'rubocop-rspec', '2.15.0'
|
19
|
+
gem 'simplecov'
|
20
|
+
gem 'solargraph'
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ gem 'xcmonkey'
|
|
39
39
|
### To run a stress test
|
40
40
|
|
41
41
|
```bash
|
42
|
-
xcmonkey test --
|
42
|
+
$ xcmonkey test --event-count 100 --bundle-id "com.apple.Maps" --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
|
43
43
|
|
44
44
|
12:44:19.343: Device info: {
|
45
45
|
"name": "iPhone 14 Pro",
|
@@ -97,6 +97,26 @@ xcmonkey repeat --session-path "./xcmonkey-session.json"
|
|
97
97
|
xcmonkey describe -x 20 -y 625 --udid "413EA256-CFFB-4312-94A6-12592BEE4CBA"
|
98
98
|
```
|
99
99
|
|
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
|
+
|
100
120
|
## [fastlane](https://github.com/fastlane/fastlane) integration
|
101
121
|
|
102
122
|
To run *xcmonkey* from *fastlane*, add the following code to your `Fastfile`:
|
@@ -106,9 +126,9 @@ require 'xcmonkey'
|
|
106
126
|
|
107
127
|
lane :test do
|
108
128
|
Xcmonkey.new(
|
109
|
-
|
129
|
+
event_count: 100,
|
110
130
|
bundle_id: 'com.apple.Maps',
|
111
|
-
|
131
|
+
udid: '413EA256-CFFB-4312-94A6-12592BEE4CBA'
|
112
132
|
).run
|
113
133
|
end
|
114
134
|
```
|
data/bin/xcmonkey
CHANGED
@@ -17,16 +17,26 @@ class Xcmonkey
|
|
17
17
|
c.description = 'Runs monkey test'
|
18
18
|
c.option('-u', '--udid STRING', String, 'Set device UDID')
|
19
19
|
c.option('-b', '--bundle-id STRING', String, 'Set target bundle identifier')
|
20
|
-
c.option('-
|
21
|
-
c.option('-
|
22
|
-
c.option('
|
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`')
|
23
28
|
c.action do |_, options|
|
24
29
|
params = {
|
25
30
|
udid: options.udid,
|
26
31
|
bundle_id: options.bundle_id,
|
27
|
-
|
32
|
+
event_count: options.event_count,
|
33
|
+
throttle: options.throttle,
|
28
34
|
session_path: options.session_path,
|
29
|
-
|
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
|
30
40
|
}
|
31
41
|
Xcmonkey.new(params).run
|
32
42
|
end
|
data/fastlane/Fastfile
CHANGED
@@ -8,9 +8,9 @@ lane :release do
|
|
8
8
|
set_github_release(
|
9
9
|
repository_name: 'alteral/xcmonkey',
|
10
10
|
api_token: ENV.fetch("GITHUB_TOKEN", nil),
|
11
|
-
name:
|
12
|
-
tag_name:
|
13
|
-
description: "
|
11
|
+
name: version,
|
12
|
+
tag_name: version,
|
13
|
+
description: "See [CHANGELOG.md](https://github.com/alteral/xcmonkey/blob/main/CHANGELOG.md##{version.delete('.')}})",
|
14
14
|
commitish: git_branch,
|
15
15
|
upload_assets: [gem_path]
|
16
16
|
)
|
data/lib/xcmonkey/driver.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
class Driver
|
2
|
-
attr_accessor :udid, :bundle_id, :
|
2
|
+
attr_accessor :udid, :bundle_id, :disable_simulator_keyboard, :event_count, :session_path, :session_actions, :ignore_crashes, :throttle
|
3
3
|
|
4
4
|
def initialize(params)
|
5
5
|
self.udid = params[:udid]
|
6
|
+
self.throttle = params[:throttle]
|
6
7
|
self.bundle_id = params[:bundle_id]
|
7
|
-
self.
|
8
|
+
self.event_count = params[:event_count]
|
8
9
|
self.session_path = params[:session_path]
|
9
|
-
self.
|
10
|
+
self.ignore_crashes = params[:ignore_crashes]
|
11
|
+
self.disable_simulator_keyboard = params[:disable_simulator_keyboard]
|
10
12
|
self.session_actions = params[:session_actions]
|
11
13
|
@session = { params: params, actions: [] }
|
12
14
|
ensure_driver_installed
|
@@ -16,16 +18,15 @@ class Driver
|
|
16
18
|
puts
|
17
19
|
ensure_device_exists
|
18
20
|
ensure_app_installed
|
19
|
-
terminate_app
|
20
|
-
|
21
|
-
|
21
|
+
terminate_app(bundle_id)
|
22
|
+
launch_app(target_bundle_id: bundle_id, wait_for_state_update: true)
|
23
|
+
@running_apps = list_running_apps
|
22
24
|
end
|
23
25
|
|
24
26
|
def monkey_test(gestures)
|
25
27
|
monkey_test_precondition
|
26
|
-
|
27
|
-
|
28
|
-
while Time.now < current_time + session_duration
|
28
|
+
event_count.times do |counter|
|
29
|
+
app_elements = describe_ui.shuffle
|
29
30
|
el1_coordinates = central_coordinates(app_elements.first)
|
30
31
|
el2_coordinates = central_coordinates(app_elements.last)
|
31
32
|
case gestures.sample
|
@@ -52,18 +53,14 @@ class Driver
|
|
52
53
|
else
|
53
54
|
next
|
54
55
|
end
|
55
|
-
|
56
|
-
next unless app_elements.include?(@home_tracker)
|
57
|
-
|
58
|
-
save_session
|
59
|
-
Logger.error('App lost')
|
56
|
+
checkup(counter)
|
60
57
|
end
|
61
58
|
save_session
|
62
59
|
end
|
63
60
|
|
64
61
|
def repeat_monkey_test
|
65
62
|
monkey_test_precondition
|
66
|
-
session_actions.
|
63
|
+
session_actions.each_with_index do |action, counter|
|
67
64
|
case action['type']
|
68
65
|
when 'tap'
|
69
66
|
tap(coordinates: { x: action['x'], y: action['y'] })
|
@@ -78,13 +75,16 @@ class Driver
|
|
78
75
|
else
|
79
76
|
next
|
80
77
|
end
|
81
|
-
|
78
|
+
checkup(counter)
|
82
79
|
end
|
83
80
|
end
|
84
81
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
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
|
86
|
+
end
|
87
|
+
check_speed_limit
|
88
88
|
end
|
89
89
|
|
90
90
|
def describe_ui
|
@@ -97,13 +97,13 @@ class Driver
|
|
97
97
|
point_info
|
98
98
|
end
|
99
99
|
|
100
|
-
def launch_app
|
101
|
-
`idb launch --udid #{udid} #{
|
102
|
-
wait_until_app_launched
|
100
|
+
def launch_app(target_bundle_id:, wait_for_state_update: false)
|
101
|
+
`idb launch --udid #{udid} #{target_bundle_id}`
|
102
|
+
wait_until_app_launched(target_bundle_id) if wait_for_state_update
|
103
103
|
end
|
104
104
|
|
105
|
-
def terminate_app
|
106
|
-
`idb terminate --udid #{udid} #{
|
105
|
+
def terminate_app(target_bundle_id)
|
106
|
+
`idb terminate --udid #{udid} #{target_bundle_id} 2>/dev/null`
|
107
107
|
end
|
108
108
|
|
109
109
|
def boot_simulator
|
@@ -117,7 +117,7 @@ class Driver
|
|
117
117
|
|
118
118
|
def configure_simulator_keyboard
|
119
119
|
shutdown_simulator
|
120
|
-
keyboard_status =
|
120
|
+
keyboard_status = disable_simulator_keyboard ? 1 : 0
|
121
121
|
`defaults write com.apple.iphonesimulator ConnectHardwareKeyboard #{keyboard_status}`
|
122
122
|
end
|
123
123
|
|
@@ -130,6 +130,10 @@ class Driver
|
|
130
130
|
`idb list-apps --udid #{udid} --json`.split("\n").map! { |app| JSON.parse(app) }
|
131
131
|
end
|
132
132
|
|
133
|
+
def list_running_apps
|
134
|
+
list_apps.select { |app| app['process_state'] == 'Running' }
|
135
|
+
end
|
136
|
+
|
133
137
|
def ensure_app_installed
|
134
138
|
return if list_apps.any? { |app| app['bundle_id'] == bundle_id }
|
135
139
|
|
@@ -144,6 +148,9 @@ class Driver
|
|
144
148
|
if device['type'] == 'simulator'
|
145
149
|
configure_simulator_keyboard
|
146
150
|
boot_simulator
|
151
|
+
else
|
152
|
+
Logger.error('xcmonkey does not support real devices yet. ' \
|
153
|
+
'For more information see https://github.com/alteral/xcmonkey/issues/7')
|
147
154
|
end
|
148
155
|
end
|
149
156
|
|
@@ -160,10 +167,8 @@ class Driver
|
|
160
167
|
end
|
161
168
|
|
162
169
|
def swipe(start_coordinates:, end_coordinates:, duration:)
|
163
|
-
|
164
|
-
|
165
|
-
payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
|
166
|
-
)
|
170
|
+
payload = "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
|
171
|
+
Logger.info("Swipe (#{duration}s):", payload: payload)
|
167
172
|
unless session_actions
|
168
173
|
@session[:actions] << {
|
169
174
|
type: :swipe,
|
@@ -183,8 +188,8 @@ class Driver
|
|
183
188
|
x = (frame['x'] + (frame['width'] / 2)).abs.to_i
|
184
189
|
y = (frame['y'] + (frame['height'] / 2)).abs.to_i
|
185
190
|
{
|
186
|
-
x: x > screen_size[:width].to_i ?
|
187
|
-
y: y > screen_size[:height].to_i ?
|
191
|
+
x: x > screen_size[:width].to_i ? random_coordinates[:x] : x,
|
192
|
+
y: y > screen_size[:height].to_i ? random_coordinates[:y] : y
|
188
193
|
}
|
189
194
|
end
|
190
195
|
|
@@ -217,31 +222,67 @@ class Driver
|
|
217
222
|
end
|
218
223
|
|
219
224
|
def save_session
|
225
|
+
return if session_path.nil?
|
226
|
+
|
220
227
|
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
|
221
228
|
end
|
222
229
|
|
223
|
-
|
230
|
+
# This function takes ≈200ms
|
231
|
+
def track_running_apps
|
232
|
+
current_list_of_running_apps = list_running_apps
|
233
|
+
if @running_apps != current_list_of_running_apps
|
234
|
+
currently_running_bundle_ids = current_list_of_running_apps.map { |app| app['bundle_id'] }
|
235
|
+
previously_running_bundle_ids = @running_apps.map { |app| app['bundle_id'] }
|
236
|
+
new_apps = currently_running_bundle_ids - previously_running_bundle_ids
|
224
237
|
|
225
|
-
|
226
|
-
|
238
|
+
return if new_apps.empty?
|
239
|
+
|
240
|
+
launch_app(target_bundle_id: bundle_id)
|
241
|
+
|
242
|
+
new_apps.each do |id|
|
243
|
+
Logger.warn("Shutting down: #{id}")
|
244
|
+
terminate_app(id)
|
245
|
+
end
|
246
|
+
end
|
227
247
|
end
|
228
248
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
249
|
+
# This function takes ≈300ms
|
250
|
+
def detect_app_state_change
|
251
|
+
return unless detect_app_in_background
|
252
|
+
|
253
|
+
target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
|
254
|
+
|
255
|
+
if target_app_is_running || ignore_crashes
|
256
|
+
launch_app(target_bundle_id: bundle_id)
|
257
|
+
else
|
258
|
+
save_session
|
259
|
+
Logger.error("Target app has crashed or been terminated")
|
233
260
|
end
|
234
|
-
@home_tracker
|
235
261
|
end
|
236
262
|
|
237
|
-
def
|
263
|
+
def detect_app_in_background
|
264
|
+
current_app_label = describe_ui.detect { |el| el['type'] == 'Application' }['AXLabel']
|
265
|
+
current_app_label.nil? || current_app_label.strip.empty?
|
266
|
+
end
|
267
|
+
|
268
|
+
def check_speed_limit
|
269
|
+
sleep(throttle / 1000.0) if throttle.to_i > 0
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
def ensure_driver_installed
|
275
|
+
Logger.error("'idb' doesn't seem to be installed") if `which idb`.strip.empty?
|
276
|
+
end
|
277
|
+
|
278
|
+
def wait_until_app_launched(target_bundle_id)
|
238
279
|
app_is_running = false
|
239
280
|
current_time = Time.now
|
240
281
|
while !app_is_running && Time.now < current_time + 5
|
241
|
-
app_info = list_apps.detect { |app| app['bundle_id'] ==
|
282
|
+
app_info = list_apps.detect { |app| app['bundle_id'] == target_bundle_id }
|
242
283
|
app_is_running = app_info && app_info['process_state'] == 'Running'
|
243
284
|
end
|
244
|
-
Logger.error("Can't run the app #{
|
285
|
+
Logger.error("Can't run the app #{target_bundle_id}") unless app_is_running
|
245
286
|
Logger.info('App info:', payload: JSON.pretty_generate(app_info))
|
246
287
|
end
|
247
288
|
end
|
data/lib/xcmonkey/repeater.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Repeater
|
2
|
-
attr_accessor :udid, :bundle_id, :
|
2
|
+
attr_accessor :udid, :bundle_id, :disable_simulator_keyboard, :ignore_crashes, :actions, :throttle
|
3
3
|
|
4
4
|
def initialize(params)
|
5
5
|
validate_session(params[:session_path])
|
@@ -8,8 +8,10 @@ class Repeater
|
|
8
8
|
def run
|
9
9
|
params = {
|
10
10
|
udid: udid,
|
11
|
+
throttle: throttle,
|
11
12
|
bundle_id: bundle_id,
|
12
|
-
|
13
|
+
ignore_crashes: ignore_crashes,
|
14
|
+
disable_simulator_keyboard: disable_simulator_keyboard,
|
13
15
|
session_actions: actions
|
14
16
|
}
|
15
17
|
Driver.new(params).repeat_monkey_test
|
@@ -34,6 +36,10 @@ class Repeater
|
|
34
36
|
self.bundle_id = session['params']['bundle_id']
|
35
37
|
Logger.error('Provided session is not valid: `bundle_id` should not be `nil`') if bundle_id.nil?
|
36
38
|
|
37
|
-
self.
|
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']
|
38
44
|
end
|
39
45
|
end
|
data/lib/xcmonkey/version.rb
CHANGED
data/lib/xcmonkey.rb
CHANGED
@@ -7,14 +7,15 @@ require_relative 'xcmonkey/logger'
|
|
7
7
|
require_relative 'xcmonkey/driver'
|
8
8
|
|
9
9
|
class Xcmonkey
|
10
|
-
attr_accessor :driver
|
10
|
+
attr_accessor :params, :driver
|
11
11
|
|
12
12
|
def initialize(params)
|
13
|
-
params[:
|
14
|
-
params[:
|
15
|
-
params[:
|
16
|
-
|
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
|
17
17
|
self.driver = Driver.new(params)
|
18
|
+
ensure_required_params
|
18
19
|
end
|
19
20
|
|
20
21
|
def run
|
@@ -22,21 +23,21 @@ class Xcmonkey
|
|
22
23
|
end
|
23
24
|
|
24
25
|
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]
|
28
29
|
taps + swipes + presses
|
29
30
|
end
|
30
31
|
|
31
|
-
def ensure_required_params
|
32
|
+
def ensure_required_params
|
32
33
|
Logger.error('UDID should be provided') if params[:udid].nil?
|
33
34
|
|
34
35
|
Logger.error('Bundle identifier should be provided') if params[:bundle_id].nil?
|
35
36
|
|
36
|
-
Logger.error('Session path should be a directory') if params[:session_path]
|
37
|
+
Logger.error('Session path should be a directory') if params[:session_path] && !File.directory?(params[:session_path])
|
37
38
|
|
38
|
-
if params[:
|
39
|
-
Logger.error('
|
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')
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
data/spec/describer_spec.rb
CHANGED
@@ -2,15 +2,17 @@ describe Describer do
|
|
2
2
|
let(:udid) { `xcrun simctl list | grep " iPhone 14 Pro Max"`.split("\n")[0].split('(')[1].split(')')[0] }
|
3
3
|
let(:driver) { Driver.new(udid: udid) }
|
4
4
|
|
5
|
-
|
5
|
+
before do
|
6
6
|
allow(Logger).to receive(:info)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'verifies that point can be described (integer)' do
|
7
10
|
driver.boot_simulator
|
8
11
|
point_info = described_class.new(udid: udid, x: 10, y: 10).run
|
9
12
|
expect(point_info).not_to be_empty
|
10
13
|
end
|
11
14
|
|
12
15
|
it 'verifies that point can be described (string)' do
|
13
|
-
allow(Logger).to receive(:info)
|
14
16
|
driver.boot_simulator
|
15
17
|
point_info = described_class.new(udid: udid, x: '10', y: '10').run
|
16
18
|
expect(point_info).not_to be_empty
|
data/spec/driver_spec.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
describe Driver do
|
2
2
|
let(:udid) { `xcrun simctl list | grep " iPhone 14 Pro Max"`.split("\n")[0].split('(')[1].split(')')[0] }
|
3
3
|
let(:bundle_id) { 'com.apple.Maps' }
|
4
|
-
let(:driver) { described_class.new(udid: udid, bundle_id: bundle_id
|
4
|
+
let(:driver) { described_class.new(udid: udid, bundle_id: bundle_id) }
|
5
5
|
let(:driver_with_session) { described_class.new(udid: udid, bundle_id: bundle_id, session_actions: [{ type: 'tap', x: 0, y: 0 }]) }
|
6
6
|
|
7
|
+
before do
|
8
|
+
allow(Logger).to receive(:info)
|
9
|
+
end
|
10
|
+
|
7
11
|
it 'verifies that sumulator was booted' do
|
8
12
|
error_message = "Failed to boot #{udid}"
|
9
13
|
expect(Logger).not_to receive(:error).with(error_message, payload: nil)
|
@@ -16,18 +20,6 @@ describe Driver do
|
|
16
20
|
expect(ui).not_to be_empty
|
17
21
|
end
|
18
22
|
|
19
|
-
it 'verifies that home screen can be opened' do
|
20
|
-
driver.boot_simulator
|
21
|
-
home_tracker = driver.open_home_screen(with_tracker: true)
|
22
|
-
expect(home_tracker).not_to be_empty
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'verifies that home screen can be opened without tracker' do
|
26
|
-
driver.boot_simulator
|
27
|
-
home_tracker = driver.open_home_screen(with_tracker: false)
|
28
|
-
expect(home_tracker).to be_nil
|
29
|
-
end
|
30
|
-
|
31
23
|
it 'verifies that list of targets can be showed' do
|
32
24
|
list_targets = driver.list_targets
|
33
25
|
expect(list_targets).not_to be_empty
|
@@ -56,9 +48,7 @@ describe Driver do
|
|
56
48
|
end
|
57
49
|
|
58
50
|
it 'verifies that device exists' do
|
59
|
-
payload = driver.list_targets.detect { |target| target['udid'] == udid }
|
60
51
|
expect(Logger).not_to receive(:error)
|
61
|
-
expect(Logger).to receive(:info).with('Device info:', payload: JSON.pretty_generate(payload))
|
62
52
|
expect(driver).to receive(:boot_simulator)
|
63
53
|
expect(driver).to receive(:configure_simulator_keyboard)
|
64
54
|
expect { driver.ensure_device_exists }.not_to raise_error
|
@@ -119,7 +109,7 @@ describe Driver do
|
|
119
109
|
|
120
110
|
it 'verifies that simulator keyboard can be enabled' do
|
121
111
|
allow(driver).to receive(:is_simulator_keyboard_enabled?).and_return(false)
|
122
|
-
driver = described_class.new(udid: udid, bundle_id: bundle_id,
|
112
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, disable_simulator_keyboard: false)
|
123
113
|
expect(driver).to receive(:shutdown_simulator)
|
124
114
|
driver.configure_simulator_keyboard
|
125
115
|
keyboard_state = `defaults read com.apple.iphonesimulator`.split("\n").grep(/ConnectHardwareKeyboard/)
|
@@ -129,7 +119,7 @@ describe Driver do
|
|
129
119
|
|
130
120
|
it 'verifies that simulator keyboard can be disabled' do
|
131
121
|
allow(driver).to receive(:is_simulator_keyboard_enabled?).and_return(true)
|
132
|
-
driver = described_class.new(udid: udid, bundle_id: bundle_id,
|
122
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, disable_simulator_keyboard: true)
|
133
123
|
expect(driver).to receive(:shutdown_simulator)
|
134
124
|
driver.configure_simulator_keyboard
|
135
125
|
keyboard_state = `defaults read com.apple.iphonesimulator`.split("\n").grep(/ConnectHardwareKeyboard/)
|
@@ -137,18 +127,25 @@ describe Driver do
|
|
137
127
|
expect(keyboard_state.first).to include('1')
|
138
128
|
end
|
139
129
|
|
140
|
-
it 'verifies that app can be launched' do
|
130
|
+
it 'verifies that app can be launched with waiting' do
|
131
|
+
expect(Logger).not_to receive(:error)
|
132
|
+
expect(driver).to receive(:wait_until_app_launched)
|
133
|
+
driver.boot_simulator
|
134
|
+
driver.terminate_app(bundle_id)
|
135
|
+
expect { driver.launch_app(target_bundle_id: bundle_id, wait_for_state_update: true) }.not_to raise_error
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'verifies that app can be launched without waiting' do
|
141
139
|
expect(Logger).not_to receive(:error)
|
142
|
-
expect(
|
140
|
+
expect(driver).not_to receive(:wait_until_app_launched)
|
143
141
|
driver.boot_simulator
|
144
|
-
driver.terminate_app
|
145
|
-
expect { driver.launch_app }.not_to raise_error
|
142
|
+
driver.terminate_app(bundle_id)
|
143
|
+
expect { driver.launch_app(target_bundle_id: bundle_id) }.not_to raise_error
|
146
144
|
end
|
147
145
|
|
148
146
|
it 'verifies tap in new session' do
|
149
147
|
driver.boot_simulator
|
150
148
|
coordinates = { x: 1, y: 1 }
|
151
|
-
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
152
149
|
driver.tap(coordinates: coordinates)
|
153
150
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
154
151
|
end
|
@@ -156,7 +153,6 @@ describe Driver do
|
|
156
153
|
it 'verifies tap in old session' do
|
157
154
|
driver_with_session.boot_simulator
|
158
155
|
coordinates = { x: 1, y: 1 }
|
159
|
-
expect(Logger).to receive(:info).with('Tap:', payload: JSON.pretty_generate(coordinates))
|
160
156
|
driver_with_session.tap(coordinates: coordinates)
|
161
157
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
162
158
|
end
|
@@ -165,7 +161,6 @@ describe Driver do
|
|
165
161
|
driver.boot_simulator
|
166
162
|
duration = 0.5
|
167
163
|
coordinates = { x: 1, y: 1 }
|
168
|
-
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
169
164
|
driver.press(coordinates: coordinates, duration: duration)
|
170
165
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
171
166
|
end
|
@@ -174,7 +169,6 @@ describe Driver do
|
|
174
169
|
driver_with_session.boot_simulator
|
175
170
|
duration = 0.5
|
176
171
|
coordinates = { x: 1, y: 1 }
|
177
|
-
expect(Logger).to receive(:info).with("Press (#{duration}s):", payload: JSON.pretty_generate(coordinates))
|
178
172
|
driver_with_session.press(coordinates: coordinates, duration: duration)
|
179
173
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
180
174
|
end
|
@@ -184,7 +178,6 @@ describe Driver do
|
|
184
178
|
duration = 0.5
|
185
179
|
start_coordinates = { x: 1, y: 1 }
|
186
180
|
end_coordinates = { x: 2, y: 2 }
|
187
|
-
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
188
181
|
driver.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
189
182
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
190
183
|
end
|
@@ -194,28 +187,33 @@ describe Driver do
|
|
194
187
|
duration = 0.5
|
195
188
|
start_coordinates = { x: 1, y: 1 }
|
196
189
|
end_coordinates = { x: 2, y: 2 }
|
197
|
-
expect(Logger).to receive(:info).with("Swipe (#{duration}s):", payload: "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}")
|
198
190
|
driver_with_session.swipe(start_coordinates: start_coordinates, end_coordinates: end_coordinates, duration: duration)
|
199
191
|
expect(driver_with_session.instance_variable_get(:@session)[:actions]).to be_empty
|
200
192
|
end
|
201
193
|
|
202
194
|
it 'verifies that session can be saved' do
|
195
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd)
|
203
196
|
expect(File).to receive(:write)
|
204
197
|
driver.instance_variable_set(:@session, { params: {}, actions: [] })
|
205
198
|
driver.save_session
|
206
199
|
end
|
207
200
|
|
201
|
+
it "verifies that session won't be saved if path is not provided" do
|
202
|
+
expect(File).not_to receive(:write)
|
203
|
+
driver.save_session
|
204
|
+
end
|
205
|
+
|
208
206
|
it 'verifies that monkey_test_precondition works fine' do
|
209
207
|
driver.monkey_test_precondition
|
210
208
|
app_info = driver.list_apps.detect { |app| app['bundle_id'] == bundle_id }
|
211
209
|
app_is_running = app_info && app_info['process_state'] == 'Running'
|
212
210
|
expect(app_is_running).to be(true)
|
211
|
+
expect(driver.instance_variable_get(:@running_apps)).not_to be_nil
|
213
212
|
end
|
214
213
|
|
215
214
|
it 'verifies that monkey_test works fine' do
|
216
|
-
params = { udid: udid, bundle_id: bundle_id,
|
215
|
+
params = { udid: udid, bundle_id: bundle_id, event_count: 1, session_path: Dir.pwd }
|
217
216
|
driver = described_class.new(params)
|
218
|
-
expect(driver).to receive(:monkey_test_precondition)
|
219
217
|
driver.monkey_test(Xcmonkey.new(params).gestures)
|
220
218
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
221
219
|
end
|
@@ -227,8 +225,6 @@ describe Driver do
|
|
227
225
|
{ 'type' => 'swipe', 'x' => 12, 'y' => 12, 'endX' => 15, 'endY' => 15, 'duration' => 0.3 }
|
228
226
|
]
|
229
227
|
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_actions: session_actions)
|
230
|
-
allow(Logger).to receive(:info).twice
|
231
|
-
expect(driver).to receive(:monkey_test_precondition)
|
232
228
|
expect(driver).to receive(:tap).with(coordinates: { x: 10, y: 10 })
|
233
229
|
expect(driver).to receive(:press).with(coordinates: { x: 11, y: 11 }, duration: 1.4)
|
234
230
|
expect(driver).to receive(:swipe).with(start_coordinates: { x: 12, y: 12 }, end_coordinates: { x: 15, y: 15 }, duration: 0.3)
|
@@ -238,7 +234,6 @@ describe Driver do
|
|
238
234
|
|
239
235
|
it 'verifies that unknown actions does not break repeat_monkey_test' do
|
240
236
|
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_actions: [{ 'type' => 'test', 'x' => 10, 'y' => 10 }])
|
241
|
-
allow(Logger).to receive(:info).twice
|
242
237
|
expect(driver).to receive(:monkey_test_precondition)
|
243
238
|
expect(driver).not_to receive(:tap)
|
244
239
|
expect(driver).not_to receive(:press)
|
@@ -247,6 +242,90 @@ describe Driver do
|
|
247
242
|
expect(driver.instance_variable_get(:@session)[:actions]).to be_empty
|
248
243
|
end
|
249
244
|
|
245
|
+
it 'verifies that running apps are tracked' do
|
246
|
+
new_app_bundle_id = 'com.apple.Preferences'
|
247
|
+
driver.terminate_app(new_app_bundle_id)
|
248
|
+
driver.monkey_test_precondition
|
249
|
+
driver.launch_app(target_bundle_id: new_app_bundle_id, wait_for_state_update: true)
|
250
|
+
expect(driver).to receive(:launch_app).with(target_bundle_id: bundle_id)
|
251
|
+
expect(driver).to receive(:terminate_app).with(new_app_bundle_id)
|
252
|
+
driver.track_running_apps
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'verifies that running apps can be determined' do
|
256
|
+
driver.terminate_app(bundle_id)
|
257
|
+
sum = driver.list_running_apps.size
|
258
|
+
driver.launch_app(target_bundle_id: bundle_id)
|
259
|
+
expect(driver.list_running_apps.size).to eq(sum + 1)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'verifies that app state change can be determined' do
|
263
|
+
driver.launch_app(target_bundle_id: bundle_id)
|
264
|
+
allow(driver).to receive(:detect_app_in_background).and_return(true)
|
265
|
+
expect(driver).not_to receive(:save_session)
|
266
|
+
expect(driver).to receive(:launch_app)
|
267
|
+
expect { driver.detect_app_state_change }.not_to raise_error
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'verifies that background is the invalid app state' do
|
271
|
+
driver.terminate_app(bundle_id)
|
272
|
+
expect(driver).to receive(:save_session)
|
273
|
+
expect { driver.detect_app_state_change }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'verifies that foreground is the valid app state' do
|
277
|
+
driver.launch_app(target_bundle_id: bundle_id, wait_for_state_update: true)
|
278
|
+
expect { driver.detect_app_state_change }.not_to raise_error
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'verifies that app crashes can be ignored' do
|
282
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd, ignore_crashes: true)
|
283
|
+
driver.terminate_app(bundle_id)
|
284
|
+
expect(driver).not_to receive(:save_session)
|
285
|
+
expect(driver).to receive(:launch_app)
|
286
|
+
expect { driver.detect_app_state_change }.not_to raise_error
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'verifies that background state can be determined' do
|
290
|
+
driver.terminate_app(bundle_id)
|
291
|
+
expect(driver.detect_app_in_background).to be(true)
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'verifies that foregroung state can be determined' do
|
295
|
+
driver.monkey_test_precondition
|
296
|
+
expect(driver.detect_app_in_background).to be(false)
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'verifies that xcmonkey behaves as expected on real devices' do
|
300
|
+
udid = '1234-5678'
|
301
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id)
|
302
|
+
allow(driver).to receive(:list_targets).and_return([{ 'udid' => udid, 'type' => 'device' }])
|
303
|
+
expect { driver.ensure_device_exists }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'verifies that test can be slowed down' do
|
307
|
+
throttle = 1000
|
308
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd, throttle: throttle)
|
309
|
+
expect(driver).to receive(:sleep).with(throttle / 1000.0)
|
310
|
+
driver.check_speed_limit
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'verifies that test ignores throttle by default' do
|
314
|
+
expect(driver).not_to receive(:sleep)
|
315
|
+
driver.check_speed_limit
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'verifies that running apps are tracked on second entry with throttle' do
|
319
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd, throttle: 1)
|
320
|
+
expect(driver).to receive(:track_running_apps)
|
321
|
+
driver.checkup(1)
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'verifies that running apps are not tracked on second entry without throttle' do
|
325
|
+
expect(driver).not_to receive(:track_running_apps)
|
326
|
+
driver.checkup(1)
|
327
|
+
end
|
328
|
+
|
250
329
|
it 'verifies that simulator was not booted' do
|
251
330
|
driver.shutdown_simulator
|
252
331
|
error_message = "Failed to boot #{udid}"
|
data/spec/repeater_spec.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
describe Repeater do
|
2
2
|
let(:session_path) { 'test/path/session.json' }
|
3
|
-
let(:session_file_content_full) { '{ "params": {"udid": "0", "bundle_id": "0", "
|
3
|
+
let(:session_file_content_full) { '{ "params": {"udid": "0", "bundle_id": "0", "disable_simulator_keyboard": false}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
4
4
|
let(:session_file_content_without_params) { '{ "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
5
|
-
let(:session_file_content_with_empty_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "
|
6
|
-
let(:session_file_content_without_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "
|
7
|
-
let(:session_file_content_without_bundle_id) { '{ "params": {"udid": "0", "
|
8
|
-
let(:session_file_content_without_udid) { '{ "params": {"bundle_id": "0", "
|
5
|
+
let(:session_file_content_with_empty_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "disable_simulator_keyboard": false}, "actions": [] }' }
|
6
|
+
let(:session_file_content_without_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "disable_simulator_keyboard": false} }' }
|
7
|
+
let(:session_file_content_without_bundle_id) { '{ "params": {"udid": "0", "disable_simulator_keyboard": false}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
8
|
+
let(:session_file_content_without_udid) { '{ "params": {"bundle_id": "0", "disable_simulator_keyboard": false}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
|
9
9
|
|
10
10
|
it 'verifies that session cannot be validated without params' do
|
11
11
|
allow(File).to receive(:exist?).and_return(true)
|
data/spec/xcmonkey_spec.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
describe Xcmonkey do
|
2
|
-
let(:params) { { udid: '123', bundle_id: 'example.com.app',
|
3
|
-
let(:
|
2
|
+
let(:params) { { udid: '123', bundle_id: 'example.com.app', event_count: 10, session_path: Dir.pwd } }
|
3
|
+
let(:event_count_error_msg) { 'Event count must be Integer and not less than 1' }
|
4
|
+
|
5
|
+
before do
|
6
|
+
allow(Logger).to receive(:info)
|
7
|
+
end
|
4
8
|
|
5
9
|
it 'verifies gestures' do
|
6
10
|
gestures = described_class.new(params).gestures
|
@@ -10,6 +14,30 @@ describe Xcmonkey do
|
|
10
14
|
expect(gestures) =~ presses + taps + swipes
|
11
15
|
end
|
12
16
|
|
17
|
+
it 'verifies gestures without taps' do
|
18
|
+
params[:exclude_taps] = true
|
19
|
+
gestures = described_class.new(params).gestures
|
20
|
+
swipes = [:precise_swipe, :blind_swipe] * 5
|
21
|
+
presses = [:precise_press, :blind_press]
|
22
|
+
expect(gestures) =~ presses + swipes
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'verifies gestures without swipes' do
|
26
|
+
params[:exclude_swipes] = true
|
27
|
+
gestures = described_class.new(params).gestures
|
28
|
+
taps = [:precise_tap, :blind_tap] * 10
|
29
|
+
presses = [:precise_press, :blind_press]
|
30
|
+
expect(gestures) =~ presses + taps
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'verifies gestures without presses' do
|
34
|
+
params[:exclude_presses] = true
|
35
|
+
gestures = described_class.new(params).gestures
|
36
|
+
taps = [:precise_tap, :blind_tap] * 10
|
37
|
+
swipes = [:precise_swipe, :blind_swipe] * 5
|
38
|
+
expect(gestures) =~ swipes + taps
|
39
|
+
end
|
40
|
+
|
13
41
|
it 'verifies required params' do
|
14
42
|
expect(Logger).not_to receive(:error)
|
15
43
|
described_class.new(params)
|
@@ -27,21 +55,21 @@ describe Xcmonkey do
|
|
27
55
|
described_class.new(params)
|
28
56
|
end
|
29
57
|
|
30
|
-
it 'verifies `
|
31
|
-
params[:
|
58
|
+
it 'verifies `event_count` param is optional' do
|
59
|
+
params[:event_count] = nil
|
32
60
|
expect(Logger).not_to receive(:error)
|
33
61
|
described_class.new(params)
|
34
62
|
end
|
35
63
|
|
36
|
-
it 'verifies `
|
37
|
-
params[:
|
38
|
-
expect(Logger).to receive(:error).with(
|
64
|
+
it 'verifies `event_count` param cannot be equal to zero' do
|
65
|
+
params[:event_count] = 0
|
66
|
+
expect(Logger).to receive(:error).with(event_count_error_msg)
|
39
67
|
described_class.new(params)
|
40
68
|
end
|
41
69
|
|
42
|
-
it 'verifies `
|
43
|
-
params[:
|
44
|
-
expect(Logger).to receive(:error).with(
|
70
|
+
it 'verifies `event_count` param cannot be negative' do
|
71
|
+
params[:event_count] = -1
|
72
|
+
expect(Logger).to receive(:error).with(event_count_error_msg)
|
45
73
|
described_class.new(params)
|
46
74
|
end
|
47
75
|
|
data/xcmonkey.gemspec
CHANGED
@@ -1,42 +1,32 @@
|
|
1
1
|
lib = File.expand_path('lib', __dir__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
3
|
+
require 'xcmonkey/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'xcmonkey'
|
7
7
|
spec.version = Xcmonkey::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['alteral']
|
9
|
+
spec.email = ['a.alterpesotskiy@mail.ru']
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
11
|
+
spec.summary = 'xcmonkey is a tool for doing randomised UI testing of iOS apps'
|
12
|
+
spec.homepage = 'https://github.com/alteral/xcmonkey'
|
13
|
+
spec.license = 'MIT'
|
14
14
|
|
15
15
|
if spec.respond_to?(:metadata)
|
16
|
-
spec.metadata[
|
16
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
17
17
|
else
|
18
|
-
raise
|
18
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
19
19
|
end
|
20
20
|
|
21
|
-
spec.bindir =
|
22
|
-
spec.executables = [
|
21
|
+
spec.bindir = 'bin'
|
22
|
+
spec.executables = ['xcmonkey']
|
23
23
|
spec.files = `git ls-files -z`.split("\x0")
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
26
|
spec.required_ruby_version = '>= 2.4'
|
27
|
-
|
28
|
-
spec.
|
29
|
-
spec.
|
30
|
-
|
31
|
-
spec.
|
32
|
-
spec.add_development_dependency('rspec_junit_formatter')
|
33
|
-
spec.add_development_dependency('rubocop', '1.38')
|
34
|
-
spec.add_development_dependency('rubocop-performance')
|
35
|
-
spec.add_development_dependency('rubocop-rake', '0.6.0')
|
36
|
-
spec.add_development_dependency('rubocop-require_tools')
|
37
|
-
spec.add_development_dependency('rubocop-rspec', '2.15.0')
|
38
|
-
spec.add_development_dependency('simplecov')
|
39
|
-
spec.add_dependency("colorize", "~> 0.8.1")
|
40
|
-
spec.add_dependency("commander")
|
41
|
-
spec.metadata['rubygems_mfa_required'] = 'true'
|
27
|
+
|
28
|
+
spec.add_dependency('colorize', '~> 0.8.1')
|
29
|
+
spec.add_dependency('commander')
|
30
|
+
|
31
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
42
32
|
end
|
metadata
CHANGED
@@ -1,183 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcmonkey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alteral
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: fasterer
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - '='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 0.9.0
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - '='
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 0.9.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: fastlane
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rake
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rspec
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rspec_junit_formatter
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rubocop
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - '='
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '1.38'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - '='
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '1.38'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rubocop-performance
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rubocop-rake
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - '='
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: 0.6.0
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - '='
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: 0.6.0
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: rubocop-require_tools
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: rubocop-rspec
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - '='
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: 2.15.0
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - '='
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: 2.15.0
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: simplecov
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '0'
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
181
13
|
- !ruby/object:Gem::Dependency
|
182
14
|
name: colorize
|
183
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,10 +52,12 @@ files:
|
|
220
52
|
- ".github/ISSUE_TEMPLATE/feature-request.md"
|
221
53
|
- ".github/dependabot.yml"
|
222
54
|
- ".github/pull_request_template.md"
|
55
|
+
- ".github/stale.yml"
|
223
56
|
- ".github/workflows/test.yml"
|
224
57
|
- ".gitignore"
|
225
58
|
- ".rspec"
|
226
59
|
- ".rubocop.yml"
|
60
|
+
- CHANGELOG.md
|
227
61
|
- CODE_OF_CONDUCT.md
|
228
62
|
- Gemfile
|
229
63
|
- LICENSE
|