run_loop 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37ad2fc838d709c0132334cf1a24b88822664e9b
4
- data.tar.gz: 818e85d9a7480820bdc7737cf99ed1d42b3aef1a
3
+ metadata.gz: 66f671a88ff045375fc68e725a7718dd22a99769
4
+ data.tar.gz: 1c9ea08167d06e4a168af8b14a731a5448ea16ca
5
5
  SHA512:
6
- metadata.gz: 12670d8f9a3490d292280e03adaf8d74343ed5af14568e257d8333a2314cb4602c4f566dd0ea55318daaaf1d26e7f207a3db10c0e8d8486bf701eefbab8ac8aa
7
- data.tar.gz: 7fff1abf53e185e59ef0b92c8b851b59c0581300ce5d1f1a851b3e0bfeaad2a26df3dbc476141b1deff84ff4a4974e14102f7f44c869e82ba1c2de695cad05be
6
+ metadata.gz: f4e9da22b7d00d2228663a0009decf562004e5def1e68245179fef574c9e9e0ea94375e92473cd37007fb13d6c477cce06e1346caa56dd7350871fed6e3b3ebb
7
+ data.tar.gz: 39095dd3bbe9f8b6b67bf3e51c081568513ad7dcdcde681332faec200bbd9bb527fa19c17959172744285a4e7bf539cd7bab028696ea0644bf64db466ea68e68
data/lib/run_loop.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'run_loop/directory'
1
2
  require 'run_loop/environment'
2
3
  require 'run_loop/logging'
3
4
  require 'run_loop/process_terminator'
@@ -18,6 +19,7 @@ require 'run_loop/host_cache'
18
19
  require 'run_loop/patches/awesome_print'
19
20
  require 'run_loop/patches/retriable'
20
21
  require 'run_loop/simctl/bridge'
22
+ require 'run_loop/simctl/plists'
21
23
 
22
24
  module RunLoop
23
25
 
@@ -2,6 +2,7 @@ require 'thor'
2
2
  require 'run_loop'
3
3
  require 'run_loop/cli/errors'
4
4
  require 'run_loop/cli/instruments'
5
+ require 'run_loop/cli/simctl'
5
6
 
6
7
  trap 'SIGINT' do
7
8
  puts 'Trapped SIGINT - exiting'
@@ -27,6 +28,9 @@ module RunLoop
27
28
  desc 'instruments', "Interact with Xcode's command-line instruments"
28
29
  subcommand 'instruments', RunLoop::CLI::Instruments
29
30
 
31
+ desc 'simctl', "Interact with Xcode's command-line simctl"
32
+ subcommand 'simctl', RunLoop::CLI::Simctl
33
+
30
34
  end
31
35
  end
32
36
  end
@@ -8,7 +8,7 @@ module RunLoop
8
8
 
9
9
  attr_accessor :signal
10
10
 
11
- desc 'instruments quit', 'Send a kill signal to all instruments processes.'
11
+ desc 'quit', 'Send a kill signal to all instruments processes.'
12
12
 
13
13
  method_option 'signal',
14
14
  :desc => 'The kill signal to send.',
@@ -39,7 +39,7 @@ module RunLoop
39
39
  end
40
40
 
41
41
 
42
- desc 'instruments launch [--app | [--ipa | --bundle-id]] [OPTIONS]', 'Launch an app with instruments.'
42
+ desc 'launch [--app | [--ipa | --bundle-id]] [OPTIONS]', 'Launch an app with instruments.'
43
43
 
44
44
  # This is the description we want, but Thor doesn't handle newlines well(?).
45
45
  # long_desc <<EOF
@@ -0,0 +1,200 @@
1
+ require 'thor'
2
+ require 'run_loop'
3
+ require 'run_loop/cli/errors'
4
+
5
+ module RunLoop
6
+ module CLI
7
+ class Simctl < Thor
8
+
9
+ attr_reader :sim_control
10
+
11
+ desc 'tail', 'Tail the log file of the booted simulator'
12
+ def tail
13
+ tail_booted
14
+ end
15
+
16
+ no_commands do
17
+ def tail_booted
18
+ device = booted_device
19
+ log_file = device.simulator_log_file_path
20
+ exec('tail', *['-F', log_file])
21
+ end
22
+ end
23
+
24
+ desc 'booted', 'Prints details about the booted simulator'
25
+ def booted
26
+ device = booted_device
27
+ if device.nil?
28
+ puts 'No simulator is booted.'
29
+ else
30
+ puts device
31
+ end
32
+ end
33
+
34
+ no_commands do
35
+ def sim_control
36
+ @sim_control ||= RunLoop::SimControl.new
37
+ end
38
+
39
+ def booted_device
40
+ sim_control.simulators.detect(nil) do |device|
41
+ device.state == 'Booted'
42
+ end
43
+ end
44
+ end
45
+
46
+ desc 'install --app [OPTIONS]', 'Installs an app on a device'
47
+
48
+ method_option 'app',
49
+ :desc => 'Path to a .app bundle to launch on simulator.',
50
+ :aliases => '-a',
51
+ :required => true,
52
+ :type => :string
53
+
54
+ method_option 'device',
55
+ :desc => 'The device UDID or simulator identifier.',
56
+ :aliases => '-d',
57
+ :required => false,
58
+ :type => :string
59
+
60
+ method_option 'force',
61
+ :desc => 'Force a re-install the existing app.',
62
+ :aliases => '-f',
63
+ :required => false,
64
+ :default => false,
65
+ :type => :boolean
66
+
67
+ method_option 'debug',
68
+ :desc => 'Enable debug logging.',
69
+ :aliases => '-v',
70
+ :required => false,
71
+ :default => false,
72
+ :type => :boolean
73
+
74
+ def install
75
+ debug = options[:debug]
76
+
77
+ if debug
78
+ ENV['DEBUG'] = '1'
79
+ end
80
+
81
+ debug_logging = RunLoop::Environment.debug?
82
+
83
+ device = expect_device(options)
84
+ app = expect_app(options, device)
85
+
86
+ bridge = RunLoop::Simctl::Bridge.new(device, app.path)
87
+
88
+ force_reinstall = options[:force]
89
+
90
+ before = Time.now
91
+
92
+ if bridge.app_is_installed?
93
+ if debug_logging
94
+ puts "App with bundle id '#{app.bundle_identifier}' is already installed."
95
+ end
96
+
97
+ if force_reinstall
98
+ if debug_logging
99
+ puts 'Will force a re-install.'
100
+ end
101
+ bridge.uninstall
102
+ bridge.install
103
+ else
104
+ new_digest = RunLoop::Directory.directory_digest(app.path)
105
+ if debug_logging
106
+ puts " New app has SHA: '#{new_digest}'."
107
+ end
108
+ installed_app_bundle = bridge.fetch_app_dir
109
+ old_digest = RunLoop::Directory.directory_digest(installed_app_bundle)
110
+ if debug_logging
111
+ puts "Installed app has SHA: '#{old_digest}'."
112
+ end
113
+ if new_digest != old_digest
114
+ if debug_logging
115
+ puts "Will re-install '#{app.bundle_identifier}' because the SHAs don't match."
116
+ end
117
+ bridge.uninstall
118
+ bridge.install
119
+ else
120
+ if debug_logging
121
+ puts "Will not re-install '#{app.bundle_identifier}' because the SHAs match."
122
+ end
123
+ end
124
+ end
125
+ else
126
+ bridge.install
127
+ end
128
+
129
+ if debug_logging
130
+ "Launching took #{Time.now-before} seconds"
131
+ puts "Installed '#{app.bundle_identifier}' on #{device} in #{Time.now-before} seconds."
132
+ end
133
+ end
134
+
135
+ no_commands do
136
+ def expect_device(options)
137
+ device_from_options = options[:device]
138
+ simulators = sim_control.simulators
139
+ if device_from_options.nil?
140
+ default_name = RunLoop::Core.default_simulator
141
+ device = simulators.detect do |sim|
142
+ sim.instruments_identifier == default_name
143
+ end
144
+
145
+ if device.nil?
146
+ raise RunLoop::CLI::ValidationError,
147
+ "Could not find a simulator with name that matches '#{device_from_options}'"
148
+ end
149
+ else
150
+ device = simulators.detect do |sim|
151
+ sim.udid == device_from_options ||
152
+ sim.instruments_identifier == device_from_options
153
+ end
154
+
155
+ if device.nil?
156
+ raise RunLoop::CLI::ValidationError,
157
+ "Could not find a simulator with name or UDID that matches '#{device_from_options}'"
158
+ end
159
+ end
160
+ device
161
+ end
162
+
163
+ def expect_app(options, device_obj)
164
+ app_bundle_path = options[:app]
165
+ unless File.exist?(app_bundle_path)
166
+ raise RunLoop::CLI::ValidationError, "Expected '#{app_bundle_path}' to exist."
167
+ end
168
+
169
+ unless File.directory?(app_bundle_path)
170
+ raise RunLoop::CLI::ValidationError,
171
+ "Expected '#{app_bundle_path}' to be a directory."
172
+ end
173
+
174
+ unless File.extname(app_bundle_path) == '.app'
175
+ raise RunLoop::CLI::ValidationError,
176
+ "Expected '#{app_bundle_path}' to end in .app."
177
+ end
178
+
179
+ app = RunLoop::App.new(app_bundle_path)
180
+
181
+ begin
182
+ app.bundle_identifier
183
+ app.executable_name
184
+ rescue RuntimeError => e
185
+ raise RunLoop::CLI::ValidationError, e.message
186
+ end
187
+
188
+ lipo = RunLoop::Lipo.new(app.path)
189
+ begin
190
+ lipo.expect_compatible_arch(device_obj)
191
+ rescue RunLoop::IncompatibleArchitecture => e
192
+ raise RunLoop::CLI::ValidationError, e.message
193
+ end
194
+
195
+ app
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
data/lib/run_loop/core.rb CHANGED
@@ -391,7 +391,9 @@ module RunLoop
391
391
  # @param [RunLoop::XCTools] xcode_tools Used to detect the current xcode
392
392
  # version.
393
393
  def self.default_simulator(xcode_tools=RunLoop::XCTools.new)
394
- if xcode_tools.xcode_version_gte_63?
394
+ if xcode_tools.xcode_version_gte_64?
395
+ 'iPhone 5s (8.4 Simulator)'
396
+ elsif xcode_tools.xcode_version_gte_63?
395
397
  'iPhone 5s (8.3 Simulator)'
396
398
  elsif xcode_tools.xcode_version_gte_62?
397
399
  'iPhone 5s (8.2 Simulator)'
@@ -8,6 +8,7 @@ module RunLoop
8
8
  attr_reader :simulator_root_dir
9
9
  attr_reader :simulator_accessibility_plist_path
10
10
  attr_reader :simulator_preferences_plist_path
11
+ attr_reader :simulator_log_file_path
11
12
 
12
13
  # Create a new device.
13
14
  #
@@ -32,6 +33,10 @@ module RunLoop
32
33
  end
33
34
  end
34
35
 
36
+ def to_s
37
+ "#{instruments_identifier} #{udid} #{instruction_set}"
38
+ end
39
+
35
40
  # Returns and instruments-ready device identifier that is a suitable value
36
41
  # for DEVICE_TARGET environment variable.
37
42
  #
@@ -111,8 +116,16 @@ module RunLoop
111
116
  }.call
112
117
  end
113
118
 
119
+ def simulator_log_file_path
120
+ @simulator_log_file_path ||= lambda {
121
+ return nil if physical_device?
122
+ File.join(CORE_SIMULATOR_LOGS_DIR, udid, 'system.log')
123
+ }.call
124
+ end
125
+
114
126
  private
115
127
 
116
128
  CORE_SIMULATOR_DEVICE_DIR = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
129
+ CORE_SIMULATOR_LOGS_DIR = File.expand_path('~/Library/Logs/CoreSimulator')
117
130
  end
118
131
  end
@@ -0,0 +1,51 @@
1
+ require 'digest'
2
+ require 'openssl'
3
+
4
+ module RunLoop
5
+
6
+ # Class for performing operations on directories.
7
+ class Directory
8
+
9
+ # Dir.glob ignores files that start with '.', but we often need to find
10
+ # dotted files and directories.
11
+ #
12
+ # Ruby 2.* does the right thing by ignoring '..' and '.'.
13
+ #
14
+ # Ruby < 2.0 includes '..' and '.' in results which causes problems for some
15
+ # of run-loop's internal methods. In particular `reset_app_sandbox`.
16
+ def self.recursive_glob_for_entries(base_dir)
17
+ Dir.glob("#{base_dir}/{**/.*,**/*}").select do |entry|
18
+ !(entry.end_with?('..') || entry.end_with?('.'))
19
+ end
20
+ end
21
+
22
+ # Computes the digest of directory.
23
+ #
24
+ # @param path A path to a directory.
25
+ # @raise ArgumentError When `path` is not a directory or path does not exist.
26
+ def self.directory_digest(path)
27
+
28
+ unless File.exist?(path)
29
+ raise ArgumentError, "Expected '#{path}' to exist"
30
+ end
31
+
32
+ unless File.directory?(path)
33
+ raise ArgumentError, "Expected '#{path}' to be a directory"
34
+ end
35
+
36
+ entries = self.recursive_glob_for_entries(path)
37
+
38
+ if entries.empty?
39
+ raise ArgumentError, "Expected a non-empty dir at '#{path}' found '#{entries}'"
40
+ end
41
+
42
+ sha = OpenSSL::Digest::SHA256.new
43
+ self.recursive_glob_for_entries(path).each do |file|
44
+ unless File.directory?(file)
45
+ sha << File.read(file)
46
+ end
47
+ end
48
+ sha.hexdigest
49
+ end
50
+ end
51
+ end
data/lib/run_loop/lipo.rb CHANGED
@@ -71,7 +71,7 @@ module RunLoop
71
71
  raise RunLoop::IncompatibleArchitecture,
72
72
  ['Binary at:',
73
73
  binary_path,
74
- "does not contain a compatible architecture for target device.",
74
+ 'does not contain a compatible architecture for target device.',
75
75
  "Expected '#{instruction_set}' but found #{arches}."].join("\n")
76
76
  end
77
77
  end
@@ -85,9 +85,9 @@ module RunLoop
85
85
  output = stdout.read.strip
86
86
  begin
87
87
  output.split(':')[-1].strip.split
88
- rescue Exception => e
88
+ rescue StandardError => e
89
89
  msg = ['Expected to be able to parse the output of lipo.',
90
- "cmd: 'lipo -info #{escaped_binary_path}'",
90
+ "cmd: 'lipo -info \"#{binary_path}\"'",
91
91
  "stdout: '#{output}'",
92
92
  "stderr: '#{stderr.read.strip}'",
93
93
  "exit code: '#{wait_thr.value}'",
@@ -102,6 +102,7 @@ module RunLoop
102
102
  # Caller is responsible for correctly escaping arguments.
103
103
  # For example, the caller must proper quote `"` paths to avoid errors
104
104
  # when dealing with paths that contain spaces.
105
+ # @todo #execute_lipo should take an [] of arguments
105
106
  def execute_lipo(argument)
106
107
  command = "xcrun lipo #{argument}"
107
108
  Open3.popen3(command) do |_, stdout, stderr, wait_thr|
@@ -1,4 +1,5 @@
1
1
  require 'retriable'
2
+ require 'retriable/version'
2
3
 
3
4
  module RunLoop
4
5
  # A class to bridge the gap between retriable 1.x and 2.0.
@@ -22,3 +23,23 @@ module RunLoop
22
23
  end
23
24
  end
24
25
  end
26
+
27
+ # Only in retriable 1.4.0
28
+ unless Retriable.public_instance_methods.include?(:retriable)
29
+ require 'retriable/retry'
30
+ module Retriable
31
+ extend self
32
+
33
+ def retriable(opts = {}, &block)
34
+ raise LocalJumpError unless block_given?
35
+
36
+ Retry.new do |r|
37
+ r.tries = opts[:tries] if opts[:tries]
38
+ r.on = opts[:on] if opts[:on]
39
+ r.interval = opts[:interval] if opts[:interval]
40
+ r.timeout = opts[:timeout] if opts[:timeout]
41
+ r.on_retry = opts[:on_retry] if opts[:on_retry]
42
+ end.perform(&block)
43
+ end
44
+ end
45
+ end
@@ -1,3 +1,7 @@
1
+ require 'fileutils'
2
+ require 'open3'
3
+ #require 'retriable'
4
+
1
5
  module RunLoop::Simctl
2
6
 
3
7
  class SimctlError < StandardError
@@ -9,15 +13,19 @@ module RunLoop::Simctl
9
13
  #
10
14
  # TODO Some code is duplicated from sim_control.rb
11
15
  # TODO Reinstall if checksum does not match.
12
- # TODO Analyze terminate_core_simulator_processes
16
+ # TODO Analyze terminate_core_simulator_processes.
17
+ # TODO Figure out when CoreSimulator appears and does not appear.
13
18
  class Bridge
14
19
 
15
20
  attr_reader :device
16
21
  attr_reader :app
17
22
  attr_reader :sim_control
23
+ attr_reader :pbuddy
18
24
 
19
25
  def initialize(device, app_bundle_path)
20
26
 
27
+ @pbuddy = RunLoop::PlistBuddy.new
28
+
21
29
  @sim_control = RunLoop::SimControl.new
22
30
  @path_to_ios_sim_app_bundle = lambda {
23
31
  dev_dir = @sim_control.xctools.xcode_developer_dir
@@ -42,24 +50,96 @@ module RunLoop::Simctl
42
50
  terminate_core_simulator_processes
43
51
  end
44
52
 
45
- def simulator_app_dir
53
+ # @!visibility private
54
+ def is_sdk_8?
55
+ @is_sdk_8 ||= device.version >= RunLoop::Version.new('8.0')
56
+ end
57
+
58
+ def device_data_dir
59
+ @device_data_dir ||= File.join(CORE_SIMULATOR_DEVICE_DIR, device.udid, 'data')
60
+ end
61
+
62
+ def device_applications_dir
46
63
  @simulator_app_dir ||= lambda {
47
- device_dir = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
48
- if device.version < RunLoop::Version.new('8.0')
49
- File.join(device_dir, device.udid, 'data', 'Applications')
64
+ if is_sdk_8?
65
+ File.join(device_data_dir, 'Containers', 'Bundle', 'Application')
50
66
  else
51
- File.join(device_dir, device.udid, 'data', 'Containers', 'Bundle', 'Application')
67
+ File.join(device_data_dir, 'Applications')
52
68
  end
53
69
  }.call
54
70
  end
55
71
 
72
+ def app_data_dir
73
+ app_install_dir = fetch_app_dir
74
+ return nil if app_install_dir.nil?
75
+ if is_sdk_8?
76
+ containers_data_dir = File.join(device_data_dir, 'Containers', 'Data', 'Application')
77
+ apps = Dir.glob("#{containers_data_dir}/**/#{METADATA_PLIST}")
78
+ match = apps.detect do |metadata_plist|
79
+ pbuddy.plist_read('MCMMetadataIdentifier', metadata_plist) == app.bundle_identifier
80
+ end
81
+ if match
82
+ File.dirname(match)
83
+ else
84
+ nil
85
+ end
86
+ else
87
+ app_install_dir
88
+ end
89
+ end
90
+
91
+ def app_library_dir
92
+ base_dir = app_data_dir
93
+ if base_dir.nil?
94
+ nil
95
+ else
96
+ File.join(base_dir, 'Library')
97
+ end
98
+ end
99
+
100
+ def app_library_preferences_dir
101
+ base_dir = app_library_dir
102
+ if base_dir.nil?
103
+ nil
104
+ else
105
+ File.join(base_dir, 'Preferences')
106
+ end
107
+ end
108
+
109
+ def app_documents_dir
110
+ base_dir = app_data_dir
111
+ if base_dir.nil?
112
+ nil
113
+ else
114
+ File.join(base_dir, 'Documents')
115
+ end
116
+ end
117
+
118
+ def app_tmp_dir
119
+ base_dir = app_data_dir
120
+ if base_dir.nil?
121
+ nil
122
+ else
123
+ File.join(base_dir, 'tmp')
124
+ end
125
+ end
126
+
127
+ def reset_app_sandbox
128
+ return true if !app_is_installed?
129
+
130
+ shutdown
131
+
132
+ reset_app_sandbox_internal
133
+ end
134
+
56
135
  def update_device_state(options={})
57
136
  merged_options = UPDATE_DEVICE_STATE_OPTS.merge(options)
58
- debug_logging = RunLoop::Environment.debug?
59
137
 
60
138
  interval = merged_options[:interval]
61
139
  tries = merged_options[:tries]
62
140
 
141
+ debug_logging = RunLoop::Environment.debug?
142
+
63
143
  on_retry = Proc.new do |_, try, elapsed_time, next_interval|
64
144
  if debug_logging
65
145
  # Retriable 2.0
@@ -95,13 +175,30 @@ module RunLoop::Simctl
95
175
  end
96
176
 
97
177
  def terminate_core_simulator_processes
98
- ['SimulatorBridge', 'CoreSimulatorBridge', 'configd_sim', 'launchd_sim'].each do |name|
178
+ debug_logging = RunLoop::Environment.debug?
179
+ [
180
+ # Probably no.
181
+ #'com.apple.CoreSimulator.CoreSimulatorService',
182
+ #'com.apple.CoreSimulator.SimVerificationService',
183
+
184
+ # Yes.
185
+ 'SimulatorBridge',
186
+ 'configd_sim',
187
+ 'launchd_sim',
188
+
189
+ # Yes, but does not always appear.
190
+ 'CoreSimulatorBridge'
191
+ ].each do |name|
99
192
  pids = RunLoop::ProcessWaiter.new(name).pids
100
193
  pids.each do |pid|
101
- puts "Sending 'TERM' to #{name} '#{pid}'"
194
+ if debug_logging
195
+ puts "Sending 'TERM' to #{name} '#{pid}'"
196
+ end
102
197
  term = RunLoop::ProcessTerminator.new(pid, 'TERM', name)
103
198
  unless term.kill_process
104
- puts "Sending 'KILL' to #{name} '#{pid}'"
199
+ if debug_logging
200
+ puts "Sending 'KILL' to #{name} '#{pid}'"
201
+ end
105
202
  term = RunLoop::ProcessTerminator.new(pid, 'KILL', name)
106
203
  term.kill_process
107
204
  end
@@ -123,7 +220,10 @@ module RunLoop::Simctl
123
220
  sleep delay
124
221
  end
125
222
 
126
- puts "Waited for #{timeout} seconds for device to have state: '#{target_state}'."
223
+ if RunLoop::Environment.debug?
224
+ puts "Waited for #{timeout} seconds for device to have state: '#{target_state}'."
225
+ end
226
+
127
227
  unless in_state
128
228
  raise "Expected '#{target_state} but found '#{device.state}' after waiting."
129
229
  end
@@ -131,13 +231,16 @@ module RunLoop::Simctl
131
231
  end
132
232
 
133
233
  def app_is_installed?
134
- sim_app_dir = simulator_app_dir
135
- return false if !File.exist?(sim_app_dir)
136
- app_path = Dir.glob("#{sim_app_dir}/**/*.app").detect do |path|
234
+ !fetch_app_dir.nil?
235
+ end
236
+
237
+ # @!visibility private
238
+ def fetch_app_dir
239
+ sim_app_dir = device_applications_dir
240
+ return nil if !File.exist?(sim_app_dir)
241
+ Dir.glob("#{sim_app_dir}/**/*.app").detect(nil) do |path|
137
242
  RunLoop::App.new(path).bundle_identifier == app.bundle_identifier
138
243
  end
139
-
140
- !app_path.nil?
141
244
  end
142
245
 
143
246
  def wait_for_app_install
@@ -154,7 +257,9 @@ module RunLoop::Simctl
154
257
  sleep delay
155
258
  end
156
259
 
157
- puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to install."
260
+ if RunLoop::Environment.debug?
261
+ puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to install."
262
+ end
158
263
 
159
264
  unless is_installed
160
265
  raise "Expected app to be installed on #{device.instruments_identifier}"
@@ -177,7 +282,9 @@ module RunLoop::Simctl
177
282
  sleep delay
178
283
  end
179
284
 
180
- puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to uninstall."
285
+ if RunLoop::Environment.debug?
286
+ puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to uninstall."
287
+ end
181
288
 
182
289
  unless not_installed
183
290
  raise "Expected app to be installed on #{device.instruments_identifier}"
@@ -262,7 +369,9 @@ module RunLoop::Simctl
262
369
  args = ['open', '-a', @path_to_ios_sim_app_bundle, '--args', '-CurrentDeviceUDID', device.udid]
263
370
  pid = spawn('xcrun', *args)
264
371
  Process.detach(pid)
265
- RunLoop::ProcessWaiter.new('CoreSimulatorBridge', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
372
+
373
+ # @todo Does not always appear?
374
+ # RunLoop::ProcessWaiter.new('CoreSimulatorBridge', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
266
375
  RunLoop::ProcessWaiter.new('iOS Simulator', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
267
376
  RunLoop::ProcessWaiter.new('SimulatorBridge', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
268
377
  wait_for_device_state 'Booted'
@@ -315,11 +424,95 @@ module RunLoop::Simctl
315
424
 
316
425
  SIM_POST_LAUNCH_WAIT = RunLoop::Environment.sim_post_launch_wait || 1.0
317
426
 
427
+ METADATA_PLIST = '.com.apple.mobile_container_manager.metadata.plist'
428
+ CORE_SIMULATOR_DEVICE_DIR = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
429
+
318
430
  # @!visibility private
319
431
  def fetch_matching_device
320
432
  sim_control.simulators.detect do |sim|
321
433
  sim.udid == device.udid
322
434
  end
323
435
  end
436
+
437
+ # @!visibility private
438
+ def reset_app_sandbox_internal_shared
439
+ [app_documents_dir, app_tmp_dir].each do |dir|
440
+ FileUtils.rm_rf dir
441
+ FileUtils.mkdir dir
442
+ end
443
+ end
444
+
445
+ # @!visibility private
446
+ def reset_app_sandbox_internal_sdk_gte_8
447
+ lib_dir = app_library_dir
448
+ RunLoop::Directory.recursive_glob_for_entries(lib_dir).each do |entry|
449
+ if entry.include?('Preferences')
450
+ # nop
451
+ else
452
+ if File.exist?(entry)
453
+ FileUtils.rm_rf(entry)
454
+ end
455
+ end
456
+ end
457
+
458
+ prefs_dir = app_library_preferences_dir
459
+ protected = ['com.apple.UIAutomation.plist',
460
+ 'com.apple.UIAutomationPlugIn.plist']
461
+ RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
462
+ unless protected.include?(File.basename(entry))
463
+ if File.exist?(entry)
464
+ FileUtils.rm_rf entry
465
+ end
466
+ end
467
+ end
468
+ end
469
+
470
+ # @!visibility private
471
+ def reset_app_sandbox_internal_sdk_lt_8
472
+ prefs_dir = app_library_preferences_dir
473
+ RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
474
+ if entry.end_with?('.GlobalPreferences.plist') ||
475
+ entry.end_with?('com.apple.PeoplePicker.plist')
476
+ # nop
477
+ else
478
+ if File.exist?(entry)
479
+ FileUtils.rm_rf entry
480
+ end
481
+ end
482
+ end
483
+
484
+ # app preferences lives in device Library/Preferences
485
+ device_prefs_dir = File.join(app_data_dir, 'Library', 'Preferences')
486
+ app_prefs_plist = File.join(device_prefs_dir, "#{app.bundle_identifier}.plist")
487
+ if File.exist?(app_prefs_plist)
488
+ FileUtils.rm_rf(app_prefs_plist)
489
+ end
490
+ end
491
+
492
+ # @!visibility private
493
+ def reset_app_sandbox_internal
494
+ reset_app_sandbox_internal_shared
495
+
496
+ if is_sdk_8?
497
+ reset_app_sandbox_internal_sdk_gte_8
498
+ else
499
+ reset_app_sandbox_internal_sdk_lt_8
500
+ end
501
+ end
502
+
503
+ # @!visibility private
504
+ def app_uia_crash_logs
505
+ base_dir = app_data_dir
506
+ if base_dir.nil?
507
+ nil
508
+ else
509
+ dir = File.join(base_dir, 'Library', 'CrashReporter', 'UIALogs')
510
+ if Dir.exist?(dir)
511
+ Dir.glob("#{dir}/*.plist")
512
+ else
513
+ nil
514
+ end
515
+ end
516
+ end
324
517
  end
325
518
  end
@@ -0,0 +1,20 @@
1
+ module RunLoop
2
+ module Simctl
3
+ class Plists
4
+
5
+ SIMCTL_PLIST_DIR = lambda {
6
+ dirname = File.dirname(__FILE__)
7
+ joined = File.join(dirname, '..', '..', '..', 'plists', 'simctl')
8
+ File.expand_path(joined)
9
+ }.call
10
+
11
+ def self.uia_automation_plist
12
+ File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomation.plist')
13
+ end
14
+
15
+ def self.uia_automation_plugin_plist
16
+ File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomationPlugIn.plist')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = '1.3.1'
2
+ VERSION = '1.3.2'
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
@@ -15,6 +15,14 @@ module RunLoop
15
15
  # @todo Refactor instruments related code to instruments class.
16
16
  class XCTools
17
17
 
18
+ # Returns a version instance for `Xcode 6.4`; used to check for the
19
+ # availability of features and paths to various items on the filesystem.
20
+ #
21
+ # @return [RunLoop::Version] 6.3
22
+ def v64
23
+ @xc64 ||= RunLoop::Version.new('6.4')
24
+ end
25
+
18
26
  # Returns a version instance for `Xcode 6.3`; used to check for the
19
27
  # availability of features and paths to various items on the filesystem.
20
28
  #
@@ -63,9 +71,16 @@ module RunLoop
63
71
  @xc50 ||= RunLoop::Version.new('5.0')
64
72
  end
65
73
 
74
+ # Are we running Xcode 6.4 or above?
75
+ #
76
+ # @return [Boolean] `true` if the current Xcode version is >= 6.4
77
+ def xcode_version_gte_64?
78
+ @xcode_gte_64 ||= xcode_version >= v64
79
+ end
80
+
66
81
  # Are we running Xcode 6.3 or above?
67
82
  #
68
- # @return [Boolean] `true` if the current Xcode version is >= 6.2
83
+ # @return [Boolean] `true` if the current Xcode version is >= 6.3
69
84
  def xcode_version_gte_63?
70
85
  @xcode_gte_63 ||= xcode_version >= v63
71
86
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: run_loop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Krukow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-12 00:00:00.000000000 Z
11
+ date: 2015-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -287,8 +287,10 @@ files:
287
287
  - lib/run_loop/cli/cli.rb
288
288
  - lib/run_loop/cli/errors.rb
289
289
  - lib/run_loop/cli/instruments.rb
290
+ - lib/run_loop/cli/simctl.rb
290
291
  - lib/run_loop/core.rb
291
292
  - lib/run_loop/device.rb
293
+ - lib/run_loop/directory.rb
292
294
  - lib/run_loop/dylib_injector.rb
293
295
  - lib/run_loop/environment.rb
294
296
  - lib/run_loop/fifo.rb
@@ -304,8 +306,11 @@ files:
304
306
  - lib/run_loop/process_waiter.rb
305
307
  - lib/run_loop/sim_control.rb
306
308
  - lib/run_loop/simctl/bridge.rb
309
+ - lib/run_loop/simctl/plists.rb
307
310
  - lib/run_loop/version.rb
308
311
  - lib/run_loop/xctools.rb
312
+ - plists/simctl/com.apple.UIAutomation.plist
313
+ - plists/simctl/com.apple.UIAutomationPlugIn.plist
309
314
  - scripts/calabash_script_uia.js
310
315
  - scripts/json2-min.js
311
316
  - scripts/json2.js