xcmonkey 1.2.0 → 1.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c93c94403332c085a393877d88d354b2736788f6e52d118eb8b9faa70c91b1e5
4
- data.tar.gz: ee0f3bfcd014151b4821a6df50f601205581ebd480b5626e33588bda2928981a
3
+ metadata.gz: 4120e453b4d4dd2de48025fdaa6f9574fc4d2f26291f0978d75ce0d80bb077cf
4
+ data.tar.gz: ec6a5c2523b295e4b7dbbfda8a6daca0994a668ce35f9155a60ccbb804b58099
5
5
  SHA512:
6
- metadata.gz: 0ab88db343b0e83363130a9e9c8ed965acbf9d58f56d597b301ff12da4df9d4e79b08c3b2284b870a65c9bfc3d05a8ba9df0d19409631620a28836c9d5785814
7
- data.tar.gz: fe46af5c215273be9210f3c880617457a12c6063e0bd1901d6abbd10750f10935b77a4797ff374926c3f1cac6b1ef79032f210fb705e05917146e4c64cede97c
6
+ metadata.gz: d776c2255920be8f49cb71186e98c4bef54ecbf71f8bf49873811b066b53d087e64349fce289e7ce335781813bd0182b866042e9193ef94e2c9f24b695d256df
7
+ data.tar.gz: 14273049c8e7ef2b58fa23fe526e4cefefa6e0fd0e09a998f0311c6ede270d84faeae1073d02cee2c75447cb726f5bafda5daab4e0edff18d168ddb2e0ca1899
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,50 @@
1
+ # xcmonkey changelog
2
+
3
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
+
5
+ ## [1.3.1](https://github.com/alteral/xcmonkey/releases/tag/1.3.1)
6
+
7
+ _February 08, 2023_
8
+
9
+ ### ✅ Added
10
+
11
+ - Terminate app after test
12
+
13
+ ## [1.3.0](https://github.com/alteral/xcmonkey/releases/tag/1.3.0)
14
+
15
+ _January 29, 2023_
16
+
17
+ ### 🔄 Changed
18
+
19
+ - `duration` option is renamed to `event-count`
20
+
21
+ ### ✅ Added
22
+
23
+ - New test options that allow to
24
+ - specify gestures to exclude from the test
25
+ - set up throttle between events
26
+ - ignore app crashes
27
+
28
+ ## [1.2.0](https://github.com/alteral/xcmonkey/releases/tag/1.2.0)
29
+
30
+ _January 23, 2023_
31
+
32
+ ### ✅ Added
33
+
34
+ - Force keep xcmonkey in the target app
35
+
36
+ ## [1.1.0](https://github.com/alteral/xcmonkey/releases/tag/1.1.0)
37
+
38
+ _January 20, 2023_
39
+
40
+ ### ✅ Added
41
+
42
+ - Support [fastlane](https://github.com/fastlane/fastlane) integration
43
+
44
+ ## [1.0.0](https://github.com/alteral/xcmonkey/releases/tag/1.0.0)
45
+
46
+ _January 09, 2023_
47
+
48
+ ### ✅ Added
49
+
50
+ - 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.1'
15
+ gem 'rubocop-performance'
16
+ gem 'rubocop-rake', '0.6.0'
17
+ gem 'rubocop-require_tools'
18
+ gem 'rubocop-rspec', '2.18.1'
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 --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"
43
43
 
44
44
  12:44:19.343: Device info: {
45
45
  "name": "iPhone 14 Pro",
@@ -97,20 +97,42 @@ 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` | Disable simulator keyboard | `false` |
117
+ | **Debugging** | `--ignore-crashes` | Ignore app crashes | `false` |
118
+ | | `--throttle <milliseconds>` | Fixed delay between events | `0` |
119
+
100
120
  ## [fastlane](https://github.com/fastlane/fastlane) integration
101
121
 
102
- To run *xcmonkey* from *fastlane*, add the following code to your `Fastfile`:
122
+ To get started with *xcmonkey fastlane plugin*, add it to your project by running:
123
+
124
+ ```bash
125
+ fastlane add_plugin xcmonkey
126
+ ```
127
+
128
+ ### Usage
103
129
 
104
130
  ```ruby
105
- require 'xcmonkey'
106
-
107
- lane :test do
108
- Xcmonkey.new(
109
- duration: 100,
110
- bundle_id: 'com.apple.Maps',
111
- udid: '413EA256-CFFB-4312-94A6-12592BEE4CBA'
112
- ).run
113
- end
131
+ bundle_id = 'com.apple.Maps'
132
+ device = 'iPhone 14 (16.2)'
133
+ sim = FastlaneCore::Simulator.all.detect { |d| device == "#{d.name} (#{d.os_version})") }
134
+
135
+ xcmonkey(udid: sim.udid, bundle_id: bundle_id)
114
136
  ```
115
137
 
116
138
  ## Code of Conduct
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('-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', 'Ignore app crashes. Defaults to `false`')
27
+ c.option('--disable-simulator-keyboard', 'Disable simulator keyboard. 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
- duration: options.duration,
32
+ event_count: options.event_count,
33
+ throttle: options.throttle,
28
34
  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
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: "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('.')})",
14
14
  commitish: git_branch,
15
15
  upload_assets: [gem_path]
16
16
  )
@@ -1,12 +1,14 @@
1
1
  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
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.session_duration = params[:duration]
8
+ self.event_count = params[:event_count]
8
9
  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]
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
- 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
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,15 @@ class Driver
53
53
  else
54
54
  next
55
55
  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)
60
57
  end
61
58
  save_session
59
+ terminate_app(bundle_id)
62
60
  end
63
61
 
64
62
  def repeat_monkey_test
65
63
  monkey_test_precondition
66
- counter = 0
67
- session_actions.each do |action|
64
+ session_actions.each_with_index do |action, counter|
68
65
  case action['type']
69
66
  when 'tap'
70
67
  tap(coordinates: { x: action['x'], y: action['y'] })
@@ -79,10 +76,17 @@ class Driver
79
76
  else
80
77
  next
81
78
  end
82
- detect_app_state_change
83
- track_running_apps if counter % 5 == 0
84
- counter += 1
79
+ checkup(counter)
80
+ end
81
+ terminate_app(bundle_id)
82
+ end
83
+
84
+ def checkup(counter)
85
+ detect_app_state_change
86
+ if counter % 5 == 0 || throttle.to_i > 0 # Track running apps after every 5th action
87
+ track_running_apps # (unless `throttle` was provided) to speed up the test
85
88
  end
89
+ check_speed_limit
86
90
  end
87
91
 
88
92
  def describe_ui
@@ -115,7 +119,7 @@ class Driver
115
119
 
116
120
  def configure_simulator_keyboard
117
121
  shutdown_simulator
118
- keyboard_status = enable_simulator_keyboard ? 0 : 1
122
+ keyboard_status = disable_simulator_keyboard ? 1 : 0
119
123
  `defaults write com.apple.iphonesimulator ConnectHardwareKeyboard #{keyboard_status}`
120
124
  end
121
125
 
@@ -165,10 +169,8 @@ class Driver
165
169
  end
166
170
 
167
171
  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
- )
172
+ payload = "#{JSON.pretty_generate(start_coordinates)} => #{JSON.pretty_generate(end_coordinates)}"
173
+ Logger.info("Swipe (#{duration}s):", payload: payload)
172
174
  unless session_actions
173
175
  @session[:actions] << {
174
176
  type: :swipe,
@@ -188,8 +190,8 @@ class Driver
188
190
  x = (frame['x'] + (frame['width'] / 2)).abs.to_i
189
191
  y = (frame['y'] + (frame['height'] / 2)).abs.to_i
190
192
  {
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
193
+ x: x > screen_size[:width].to_i ? random_coordinates[:x] : x,
194
+ y: y > screen_size[:height].to_i ? random_coordinates[:y] : y
193
195
  }
194
196
  end
195
197
 
@@ -222,6 +224,8 @@ class Driver
222
224
  end
223
225
 
224
226
  def save_session
227
+ return if session_path.nil?
228
+
225
229
  File.write("#{session_path}/xcmonkey-session.json", JSON.pretty_generate(@session))
226
230
  end
227
231
 
@@ -236,6 +240,7 @@ class Driver
236
240
  return if new_apps.empty?
237
241
 
238
242
  launch_app(target_bundle_id: bundle_id)
243
+
239
244
  new_apps.each do |id|
240
245
  Logger.warn("Shutting down: #{id}")
241
246
  terminate_app(id)
@@ -249,7 +254,7 @@ class Driver
249
254
 
250
255
  target_app_is_running = list_running_apps.any? { |app| app['bundle_id'] == bundle_id }
251
256
 
252
- if target_app_is_running
257
+ if target_app_is_running || ignore_crashes
253
258
  launch_app(target_bundle_id: bundle_id)
254
259
  else
255
260
  save_session
@@ -262,6 +267,10 @@ class Driver
262
267
  current_app_label.nil? || current_app_label.strip.empty?
263
268
  end
264
269
 
270
+ def check_speed_limit
271
+ sleep(throttle / 1000.0) if throttle.to_i > 0
272
+ end
273
+
265
274
  private
266
275
 
267
276
  def ensure_driver_installed
@@ -1,5 +1,5 @@
1
1
  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
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
- enable_simulator_keyboard: enable_simulator_keyboard,
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.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']
38
44
  end
39
45
  end
@@ -1,3 +1,3 @@
1
1
  class Xcmonkey
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.1'
3
3
  end
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[: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
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(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].nil? || !File.directory?(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[: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')
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, session_path: Dir.pwd) }
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, enable_simulator_keyboard: true)
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, enable_simulator_keyboard: false)
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, duration: 1, session_path: Dir.pwd }
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,14 +278,17 @@ describe Driver do
272
278
  expect { driver.detect_app_state_change }.not_to raise_error
273
279
  end
274
280
 
275
- it 'verifies that background state can be determined' do
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)
276
283
  driver.terminate_app(bundle_id)
277
- expect(driver.detect_app_in_background).to be(true)
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
278
287
  end
279
288
 
280
- it 'verifies that foregroung state can be determined' do
281
- driver.monkey_test_precondition
282
- expect(driver.detect_app_in_background).to be(false)
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)
283
292
  end
284
293
 
285
294
  it 'verifies that xcmonkey behaves as expected on real devices' do
@@ -289,6 +298,31 @@ describe Driver do
289
298
  expect { driver.ensure_device_exists }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) }
290
299
  end
291
300
 
301
+ it 'verifies that test can be slowed down' do
302
+ throttle = 1000
303
+ driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd, throttle: throttle)
304
+ expect(driver).to receive(:sleep).with(throttle / 1000.0)
305
+ driver.check_speed_limit
306
+ end
307
+
308
+ it 'verifies that test ignores throttle by default' do
309
+ expect(driver).not_to receive(:sleep)
310
+ driver.check_speed_limit
311
+ end
312
+
313
+ it 'verifies that running apps are tracked on second entry with throttle' do
314
+ driver = described_class.new(udid: udid, bundle_id: bundle_id, session_path: Dir.pwd, throttle: 1)
315
+ driver.launch_app(target_bundle_id: bundle_id)
316
+ expect(driver).to receive(:track_running_apps)
317
+ driver.checkup(1)
318
+ end
319
+
320
+ it 'verifies that running apps are not tracked on second entry without throttle' do
321
+ driver.launch_app(target_bundle_id: bundle_id)
322
+ expect(driver).not_to receive(:track_running_apps)
323
+ driver.checkup(1)
324
+ end
325
+
292
326
  it 'verifies that simulator was not booted' do
293
327
  driver.shutdown_simulator
294
328
  error_message = "Failed to boot #{udid}"
@@ -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", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 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", "enable_simulator_keyboard": true}, "actions": [] }' }
6
- let(:session_file_content_without_actions) { '{ "params": {"udid": "0", "bundle_id": "0", "enable_simulator_keyboard": true} }' }
7
- let(:session_file_content_without_bundle_id) { '{ "params": {"udid": "0", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 0 }] }' }
8
- let(:session_file_content_without_udid) { '{ "params": {"bundle_id": "0", "enable_simulator_keyboard": true}, "actions": [{ "type": "tap", "x": 0, "y": 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)
@@ -1,6 +1,6 @@
1
1
  describe Xcmonkey do
2
- let(:params) { { udid: '123', bundle_id: 'example.com.app', duration: 10, session_path: Dir.pwd } }
3
- let(:duration_error_msg) { 'Duration must be Integer and not less than 1 second' }
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 `duration` param is optional' do
35
- params[:duration] = nil
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 `duration` param cannot be equal to zero' do
41
- params[:duration] = 0
42
- expect(Logger).to receive(:error).with(duration_error_msg)
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 `duration` param cannot be negative' do
47
- params[:duration] = -1
48
- expect(Logger).to receive(:error).with(duration_error_msg)
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 "xcmonkey/version"
3
+ require 'xcmonkey/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "xcmonkey"
6
+ spec.name = 'xcmonkey'
7
7
  spec.version = Xcmonkey::VERSION
8
- spec.authors = ["alteral"]
9
- spec.email = ["a.alterpesotskiy@mail.ru"]
8
+ spec.authors = ['alteral']
9
+ spec.email = ['a.alterpesotskiy@mail.ru']
10
10
 
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"
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["allowed_push_host"] = "https://rubygems.org"
16
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
17
17
  else
18
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
18
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
19
19
  end
20
20
 
21
- spec.bindir = "bin"
22
- spec.executables = ["xcmonkey"]
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
- spec.add_development_dependency('bundler')
28
- spec.add_development_dependency('fasterer', '0.9.0')
29
- spec.add_development_dependency('fastlane')
30
- spec.add_development_dependency('rake')
31
- spec.add_development_dependency('rspec')
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.2.0
4
+ version: 1.3.1
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-23 00:00:00.000000000 Z
11
+ date: 2023-02-08 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