xcmonkey 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/stale.yml +18 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile +14 -0
- data/README.md +22 -2
- data/bin/xcmonkey +15 -5
- data/fastlane/Fastfile +3 -3
- data/lib/xcmonkey/driver.rb +31 -24
- data/lib/xcmonkey/repeater.rb +9 -3
- data/lib/xcmonkey/version.rb +1 -1
- data/lib/xcmonkey.rb +13 -12
- data/spec/driver_spec.rb +41 -4
- data/spec/repeater_spec.rb +5 -5
- data/spec/xcmonkey_spec.rb +34 -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,7 +126,7 @@ 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
|
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
|
@@ -23,10 +25,8 @@ class Driver
|
|
23
25
|
|
24
26
|
def monkey_test(gestures)
|
25
27
|
monkey_test_precondition
|
26
|
-
|
27
|
-
|
28
|
-
counter = 0
|
29
|
-
while Time.now < current_time + session_duration
|
28
|
+
event_count.times do |counter|
|
29
|
+
app_elements = describe_ui.shuffle
|
30
30
|
el1_coordinates = central_coordinates(app_elements.first)
|
31
31
|
el2_coordinates = central_coordinates(app_elements.last)
|
32
32
|
case gestures.sample
|
@@ -53,18 +53,14 @@ class Driver
|
|
53
53
|
else
|
54
54
|
next
|
55
55
|
end
|
56
|
-
|
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)
|
60
57
|
end
|
61
58
|
save_session
|
62
59
|
end
|
63
60
|
|
64
61
|
def repeat_monkey_test
|
65
62
|
monkey_test_precondition
|
66
|
-
|
67
|
-
session_actions.each do |action|
|
63
|
+
session_actions.each_with_index do |action, counter|
|
68
64
|
case action['type']
|
69
65
|
when 'tap'
|
70
66
|
tap(coordinates: { x: action['x'], y: action['y'] })
|
@@ -79,10 +75,16 @@ class Driver
|
|
79
75
|
else
|
80
76
|
next
|
81
77
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
85
86
|
end
|
87
|
+
check_speed_limit
|
86
88
|
end
|
87
89
|
|
88
90
|
def describe_ui
|
@@ -115,7 +117,7 @@ class Driver
|
|
115
117
|
|
116
118
|
def configure_simulator_keyboard
|
117
119
|
shutdown_simulator
|
118
|
-
keyboard_status =
|
120
|
+
keyboard_status = disable_simulator_keyboard ? 1 : 0
|
119
121
|
`defaults write com.apple.iphonesimulator ConnectHardwareKeyboard #{keyboard_status}`
|
120
122
|
end
|
121
123
|
|
@@ -165,10 +167,8 @@ class Driver
|
|
165
167
|
end
|
166
168
|
|
167
169
|
def swipe(start_coordinates:, end_coordinates:, duration:)
|
168
|
-
|
169
|
-
|
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)
|
172
172
|
unless session_actions
|
173
173
|
@session[:actions] << {
|
174
174
|
type: :swipe,
|
@@ -188,8 +188,8 @@ class Driver
|
|
188
188
|
x = (frame['x'] + (frame['width'] / 2)).abs.to_i
|
189
189
|
y = (frame['y'] + (frame['height'] / 2)).abs.to_i
|
190
190
|
{
|
191
|
-
x: x > screen_size[:width].to_i ?
|
192
|
-
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
|
193
193
|
}
|
194
194
|
end
|
195
195
|
|
@@ -222,6 +222,8 @@ class Driver
|
|
222
222
|
end
|
223
223
|
|
224
224
|
def save_session
|
225
|
+
return if session_path.nil?
|
226
|
+
|
225
227
|
File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
|
226
228
|
end
|
227
229
|
|
@@ -236,6 +238,7 @@ class Driver
|
|
236
238
|
return if new_apps.empty?
|
237
239
|
|
238
240
|
launch_app(target_bundle_id: bundle_id)
|
241
|
+
|
239
242
|
new_apps.each do |id|
|
240
243
|
Logger.warn("Shutting down: #{id}")
|
241
244
|
terminate_app(id)
|
@@ -249,7 +252,7 @@ class Driver
|
|
249
252
|
|
250
253
|
target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
|
251
254
|
|
252
|
-
if target_app_is_running
|
255
|
+
if target_app_is_running || ignore_crashes
|
253
256
|
launch_app(target_bundle_id: bundle_id)
|
254
257
|
else
|
255
258
|
save_session
|
@@ -262,6 +265,10 @@ class Driver
|
|
262
265
|
current_app_label.nil? || current_app_label.strip.empty?
|
263
266
|
end
|
264
267
|
|
268
|
+
def check_speed_limit
|
269
|
+
sleep(throttle / 1000.0) if throttle.to_i > 0
|
270
|
+
end
|
271
|
+
|
265
272
|
private
|
266
273
|
|
267
274
|
def ensure_driver_installed
|
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/driver_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
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
7
|
before do
|
@@ -109,7 +109,7 @@ describe Driver do
|
|
109
109
|
|
110
110
|
it 'verifies that simulator keyboard can be enabled' do
|
111
111
|
allow(driver).to receive(:is_simulator_keyboard_enabled?).and_return(false)
|
112
|
-
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)
|
113
113
|
expect(driver).to receive(:shutdown_simulator)
|
114
114
|
driver.configure_simulator_keyboard
|
115
115
|
keyboard_state = `defaults read com.apple.iphonesimulator`.split("\n").grep(/ConnectHardwareKeyboard/)
|
@@ -119,7 +119,7 @@ describe Driver do
|
|
119
119
|
|
120
120
|
it 'verifies that simulator keyboard can be disabled' do
|
121
121
|
allow(driver).to receive(:is_simulator_keyboard_enabled?).and_return(true)
|
122
|
-
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)
|
123
123
|
expect(driver).to receive(:shutdown_simulator)
|
124
124
|
driver.configure_simulator_keyboard
|
125
125
|
keyboard_state = `defaults read com.apple.iphonesimulator`.split("\n").grep(/ConnectHardwareKeyboard/)
|
@@ -192,11 +192,17 @@ describe Driver do
|
|
192
192
|
end
|
193
193
|
|
194
194
|
it 'verifies that session can be saved' do
|
195
|
+
driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd)
|
195
196
|
expect(File).to receive(:write)
|
196
197
|
driver.instance_variable_set(:@session, { params: {}, actions: [] })
|
197
198
|
driver.save_session
|
198
199
|
end
|
199
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
|
+
|
200
206
|
it 'verifies that monkey_test_precondition works fine' do
|
201
207
|
driver.monkey_test_precondition
|
202
208
|
app_info = driver.list_apps.detect { |app| app['bundle_id'] == bundle_id }
|
@@ -206,7 +212,7 @@ describe Driver do
|
|
206
212
|
end
|
207
213
|
|
208
214
|
it 'verifies that monkey_test works fine' do
|
209
|
-
params = { udid: udid, bundle_id: bundle_id,
|
215
|
+
params = { udid: udid, bundle_id: bundle_id, event_count: 1, session_path: Dir.pwd }
|
210
216
|
driver = described_class.new(params)
|
211
217
|
driver.monkey_test(Xcmonkey.new(params).gestures)
|
212
218
|
expect(driver.instance_variable_get(:@session)[:actions]).not_to be_empty
|
@@ -272,6 +278,14 @@ describe Driver do
|
|
272
278
|
expect { driver.detect_app_state_change }.not_to raise_error
|
273
279
|
end
|
274
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
|
+
|
275
289
|
it 'verifies that background state can be determined' do
|
276
290
|
driver.terminate_app(bundle_id)
|
277
291
|
expect(driver.detect_app_in_background).to be(true)
|
@@ -289,6 +303,29 @@ describe Driver do
|
|
289
303
|
expect { driver.ensure_device_exists }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
|
290
304
|
end
|
291
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
|
+
|
292
329
|
it 'verifies that simulator was not booted' do
|
293
330
|
driver.shutdown_simulator
|
294
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,6 @@
|
|
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
4
|
|
5
5
|
before do
|
6
6
|
allow(Logger).to receive(:info)
|
@@ -14,6 +14,30 @@ describe Xcmonkey do
|
|
14
14
|
expect(gestures) =~ presses + taps + swipes
|
15
15
|
end
|
16
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
|
+
|
17
41
|
it 'verifies required params' do
|
18
42
|
expect(Logger).not_to receive(:error)
|
19
43
|
described_class.new(params)
|
@@ -31,21 +55,21 @@ describe Xcmonkey do
|
|
31
55
|
described_class.new(params)
|
32
56
|
end
|
33
57
|
|
34
|
-
it 'verifies `
|
35
|
-
params[:
|
58
|
+
it 'verifies `event_count` param is optional' do
|
59
|
+
params[:event_count] = nil
|
36
60
|
expect(Logger).not_to receive(:error)
|
37
61
|
described_class.new(params)
|
38
62
|
end
|
39
63
|
|
40
|
-
it 'verifies `
|
41
|
-
params[:
|
42
|
-
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)
|
43
67
|
described_class.new(params)
|
44
68
|
end
|
45
69
|
|
46
|
-
it 'verifies `
|
47
|
-
params[:
|
48
|
-
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)
|
49
73
|
described_class.new(params)
|
50
74
|
end
|
51
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
|