run_loop 2.1.6 → 2.1.7
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/lib/run_loop.rb +1 -0
- data/lib/run_loop/app.rb +12 -8
- data/lib/run_loop/cli/cli.rb +2 -2
- data/lib/run_loop/cli/simctl.rb +0 -5
- data/lib/run_loop/core.rb +2 -1
- data/lib/run_loop/core_simulator.rb +56 -106
- data/lib/run_loop/device.rb +23 -41
- data/lib/run_loop/device_agent/Frameworks.zip +0 -0
- data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/bin/iOSDeviceManager +0 -0
- data/lib/run_loop/device_agent/ios_device_manager.rb +11 -2
- data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/xcodebuild.rb +7 -2
- data/lib/run_loop/environment.rb +2 -2
- data/lib/run_loop/instruments.rb +4 -1
- data/lib/run_loop/otool.rb +40 -24
- data/lib/run_loop/physical_device/ios_device_manager.rb +89 -0
- data/lib/run_loop/regex.rb +1 -1
- data/lib/run_loop/shell.rb +38 -4
- data/lib/run_loop/sim_control.rb +6 -2
- data/lib/run_loop/simctl.rb +298 -5
- data/lib/run_loop/version.rb +1 -1
- data/lib/run_loop/xcuitest.rb +168 -22
- metadata +3 -2
Binary file
|
Binary file
|
Binary file
|
@@ -95,21 +95,30 @@ but binary does not exist at that path.
|
|
95
95
|
"-t", runner.tester,
|
96
96
|
"-d", device.udid]
|
97
97
|
|
98
|
+
code_sign_identity = RunLoop::Environment.code_sign_identity
|
99
|
+
if !code_sign_identity
|
100
|
+
code_sign_identity = "iPhone Developer"
|
101
|
+
end
|
102
|
+
|
98
103
|
if device.physical_device?
|
99
104
|
args << "-c"
|
100
|
-
args <<
|
105
|
+
args << code_sign_identity
|
101
106
|
end
|
102
107
|
|
103
108
|
log_file = IOSDeviceManager.log_file
|
104
109
|
FileUtils.rm_rf(log_file)
|
105
110
|
FileUtils.touch(log_file)
|
106
111
|
|
112
|
+
env = {
|
113
|
+
"CLOBBER" => "1"
|
114
|
+
}
|
115
|
+
|
107
116
|
options = {:out => log_file, :err => log_file}
|
108
117
|
RunLoop.log_unix_cmd("#{cmd} #{args.join(" ")} >& #{log_file}")
|
109
118
|
|
110
119
|
# Gotta keep the ios_device_manager process alive or the connection
|
111
120
|
# to testmanagerd will fail.
|
112
|
-
pid = Process.spawn(cmd, *args, options)
|
121
|
+
pid = Process.spawn(env, cmd, *args, options)
|
113
122
|
Process.detach(pid)
|
114
123
|
|
115
124
|
if device.simulator?
|
Binary file
|
@@ -61,7 +61,8 @@ module RunLoop
|
|
61
61
|
# @!visibility private
|
62
62
|
def xcodebuild
|
63
63
|
env = {
|
64
|
-
"COMMAND_LINE_BUILD" => "1"
|
64
|
+
"COMMAND_LINE_BUILD" => "1",
|
65
|
+
"CLOBBER" => "1"
|
65
66
|
}
|
66
67
|
|
67
68
|
args = [
|
@@ -72,7 +73,11 @@ module RunLoop
|
|
72
73
|
"-config", "Debug",
|
73
74
|
"-destination",
|
74
75
|
"id=#{device.udid}",
|
75
|
-
"
|
76
|
+
"CLANG_ENABLE_CODE_COVERAGE=YES",
|
77
|
+
"GCC_GENERATE_TEST_COVERAGE_FILES=NO",
|
78
|
+
"GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=NO",
|
79
|
+
# Scheme setting.
|
80
|
+
"-enableCodeCoverage", "YES",
|
76
81
|
"test"
|
77
82
|
]
|
78
83
|
|
data/lib/run_loop/environment.rb
CHANGED
@@ -163,8 +163,8 @@ module RunLoop
|
|
163
163
|
end
|
164
164
|
|
165
165
|
# Returns the value of CODESIGN_IDENTITY
|
166
|
-
def self.
|
167
|
-
value = ENV["
|
166
|
+
def self.code_sign_identity
|
167
|
+
value = ENV["CODE_SIGN_IDENTITY"]
|
168
168
|
if !value || value == ""
|
169
169
|
nil
|
170
170
|
else
|
data/lib/run_loop/instruments.rb
CHANGED
@@ -158,10 +158,13 @@ module RunLoop
|
|
158
158
|
# @todo Should this raise errors?
|
159
159
|
# @todo Is this jruby compatible?
|
160
160
|
def spawn(automation_template, options, log_file)
|
161
|
+
env = {
|
162
|
+
"CLOBBER" => "1"
|
163
|
+
}
|
161
164
|
splat_args = spawn_arguments(automation_template, options)
|
162
165
|
logger = options[:logger]
|
163
166
|
RunLoop::Logging.log_debug(logger, "xcrun #{splat_args.join(' ')} >& #{log_file}")
|
164
|
-
pid = Process.spawn('xcrun', *splat_args, {:out => log_file, :err => log_file})
|
167
|
+
pid = Process.spawn(env, 'xcrun', *splat_args, {:out => log_file, :err => log_file})
|
165
168
|
Process.detach(pid)
|
166
169
|
pid.to_i
|
167
170
|
end
|
data/lib/run_loop/otool.rb
CHANGED
@@ -5,26 +5,14 @@ module RunLoop
|
|
5
5
|
class Otool
|
6
6
|
|
7
7
|
# @!visibility private
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(path)
|
12
|
-
@path = path
|
13
|
-
|
14
|
-
if !Otool.valid_path?(path)
|
15
|
-
raise ArgumentError,
|
16
|
-
%Q{File:
|
17
|
-
|
18
|
-
#{path}
|
19
|
-
|
20
|
-
must exist and not be a directory.
|
21
|
-
}
|
22
|
-
end
|
8
|
+
# @param [RunLoop::Xcode] xcode An instance of Xcode
|
9
|
+
def initialize(xcode)
|
10
|
+
@xcode = xcode
|
23
11
|
end
|
24
12
|
|
25
13
|
# @!visibility private
|
26
14
|
def to_s
|
27
|
-
"#<OTOOL: #{
|
15
|
+
"#<OTOOL: Xcode #{xcode.version.to_s}>"
|
28
16
|
end
|
29
17
|
|
30
18
|
# @!visibility private
|
@@ -33,15 +21,19 @@ must exist and not be a directory.
|
|
33
21
|
end
|
34
22
|
|
35
23
|
# @!visibility private
|
36
|
-
def executable?
|
37
|
-
!
|
24
|
+
def executable?(path)
|
25
|
+
expect_valid_path!(path)
|
26
|
+
!arch_info(path)[/is not an object file/, 0]
|
38
27
|
end
|
39
28
|
|
40
29
|
private
|
41
30
|
|
42
31
|
# @!visibility private
|
43
|
-
|
44
|
-
|
32
|
+
attr_reader :xcode, :command_name
|
33
|
+
|
34
|
+
# @!visibility private
|
35
|
+
def arch_info(path)
|
36
|
+
args = [command_name, "-hv", "-arch", "all", path]
|
45
37
|
opts = { :log_cmd => false }
|
46
38
|
|
47
39
|
hash = xcrun.run_command_in_context(args, opts)
|
@@ -60,17 +52,41 @@ exited #{hash[:exit_status]} with the following output:
|
|
60
52
|
}
|
61
53
|
end
|
62
54
|
|
63
|
-
|
55
|
+
hash[:out]
|
64
56
|
end
|
65
57
|
|
66
58
|
# @!visibility private
|
67
|
-
def
|
68
|
-
File.exist?(path) && !File.directory?(path)
|
59
|
+
def expect_valid_path!(path)
|
60
|
+
return true if File.exist?(path) && !File.directory?(path)
|
61
|
+
raise ArgumentError, %Q[
|
62
|
+
File:
|
63
|
+
|
64
|
+
#{path}
|
65
|
+
|
66
|
+
must exist and not be a directory.
|
67
|
+
|
68
|
+
]
|
69
69
|
end
|
70
70
|
|
71
71
|
# @!visibility private
|
72
72
|
def xcrun
|
73
|
-
RunLoop::Xcrun.new
|
73
|
+
@xcrun ||= RunLoop::Xcrun.new
|
74
|
+
end
|
75
|
+
|
76
|
+
# @!visibility private
|
77
|
+
def xcode
|
78
|
+
@xcode
|
79
|
+
end
|
80
|
+
|
81
|
+
# @!visibility private
|
82
|
+
def command_name
|
83
|
+
@command_name ||= begin
|
84
|
+
if xcode.version_gte_8?
|
85
|
+
"otool-classic"
|
86
|
+
else
|
87
|
+
"otool"
|
88
|
+
end
|
89
|
+
end
|
74
90
|
end
|
75
91
|
end
|
76
92
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
|
2
|
+
module RunLoop
|
3
|
+
module PhysicalDevice
|
4
|
+
|
5
|
+
require "run_loop/physical_device/life_cycle"
|
6
|
+
class IOSDeviceManager < RunLoop::PhysicalDevice::LifeCycle
|
7
|
+
|
8
|
+
# Is the tool installed?
|
9
|
+
def self.tool_is_installed?
|
10
|
+
File.exist?(IOSDeviceManager.executable_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Path to tool.
|
14
|
+
def self.executable_path
|
15
|
+
RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(device)
|
19
|
+
super(device)
|
20
|
+
|
21
|
+
# Expands the Frameworks.zip if necessary.
|
22
|
+
RunLoop::DeviceAgent::Frameworks.instance.install
|
23
|
+
end
|
24
|
+
|
25
|
+
def app_installed?(bundle_id)
|
26
|
+
args = [
|
27
|
+
IOSDeviceManager.executable_path,
|
28
|
+
"is_installed",
|
29
|
+
"-d", device.udid,
|
30
|
+
"-b", bundle_id
|
31
|
+
]
|
32
|
+
|
33
|
+
options = { :log_cmd => true }
|
34
|
+
hash = run_shell_command(args, options)
|
35
|
+
|
36
|
+
# TODO: error reporting
|
37
|
+
hash[:exit_status] == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def install_app(app_or_ipa)
|
41
|
+
app = app_or_ipa
|
42
|
+
if is_ipa?(app)
|
43
|
+
app = app_or_ipa.app
|
44
|
+
end
|
45
|
+
|
46
|
+
code_sign_identity = RunLoop::Environment.code_sign_identity
|
47
|
+
if !code_sign_identity
|
48
|
+
code_sign_identity = "iPhone Developer"
|
49
|
+
end
|
50
|
+
|
51
|
+
args = [
|
52
|
+
IOSDeviceManager.executable_path,
|
53
|
+
"install",
|
54
|
+
"-d", device.udid,
|
55
|
+
"-a", app.path,
|
56
|
+
"-c", code_sign_identity
|
57
|
+
]
|
58
|
+
|
59
|
+
options = { :log_cmd => true }
|
60
|
+
hash = run_shell_command(args, options)
|
61
|
+
|
62
|
+
# TODO: error reporting
|
63
|
+
if hash[:exit_status] == 0
|
64
|
+
true
|
65
|
+
else
|
66
|
+
puts hash[:out]
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def uninstall_app(bundle_id)
|
72
|
+
return true if !app_installed?(bundle_id)
|
73
|
+
|
74
|
+
args = [
|
75
|
+
IOSDeviceManager.executable_path,
|
76
|
+
"uninstall",
|
77
|
+
"-d", device.udid,
|
78
|
+
"-b", bundle_id
|
79
|
+
]
|
80
|
+
|
81
|
+
options = { :log_cmd => true }
|
82
|
+
hash = run_shell_command(args, options)
|
83
|
+
|
84
|
+
# TODO: error reporting
|
85
|
+
hash[:exit_status] == 0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/run_loop/regex.rb
CHANGED
data/lib/run_loop/shell.rb
CHANGED
@@ -26,6 +26,16 @@ module RunLoop
|
|
26
26
|
# Raised when shell command times out.
|
27
27
|
class TimeoutError < RuntimeError; end
|
28
28
|
|
29
|
+
def self.run_shell_command(args, options={})
|
30
|
+
shell = Class.new do
|
31
|
+
include RunLoop::Shell
|
32
|
+
def to_s; "#<Anonymous Shell>"; end
|
33
|
+
def inspect; to_s; end
|
34
|
+
end.new
|
35
|
+
|
36
|
+
shell.run_shell_command(args, options)
|
37
|
+
end
|
38
|
+
|
29
39
|
def run_shell_command(args, options={})
|
30
40
|
|
31
41
|
merged_options = DEFAULT_OPTIONS.merge(options)
|
@@ -85,19 +95,43 @@ executing this command:
|
|
85
95
|
}
|
86
96
|
end
|
87
97
|
|
98
|
+
now = Time.now
|
99
|
+
|
88
100
|
if hash[:exit_status].nil?
|
89
|
-
elapsed = "%0.2f" % (
|
90
|
-
|
91
|
-
|
101
|
+
elapsed = "%0.2f" % (now - start_time)
|
102
|
+
|
103
|
+
if timeout_exceeded?(start_time, timeout)
|
104
|
+
raise TimeoutError,
|
105
|
+
%Q[
|
106
|
+
Timed out after #{elapsed} seconds executing
|
92
107
|
|
93
108
|
#{cmd}
|
94
109
|
|
95
110
|
with a timeout of #{timeout}
|
96
|
-
|
111
|
+
]
|
112
|
+
else
|
113
|
+
raise Error,
|
114
|
+
%Q[
|
115
|
+
There was an error executing:
|
116
|
+
|
117
|
+
#{cmd}
|
118
|
+
|
119
|
+
The command generated this output:
|
120
|
+
|
121
|
+
#{hash[:out]}
|
122
|
+
]
|
123
|
+
|
124
|
+
end
|
97
125
|
end
|
98
126
|
|
99
127
|
hash
|
100
128
|
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def timeout_exceeded?(start_time, timeout)
|
133
|
+
Time.now > start_time + timeout
|
134
|
+
end
|
101
135
|
end
|
102
136
|
end
|
103
137
|
|
data/lib/run_loop/sim_control.rb
CHANGED
@@ -1136,7 +1136,9 @@ module RunLoop
|
|
1136
1136
|
# base sdk version.
|
1137
1137
|
# @see #simctl_list
|
1138
1138
|
def simctl_list_devices
|
1139
|
-
|
1139
|
+
# Ensure correct CoreSimulator service is installed.
|
1140
|
+
RunLoop::Simctl.new
|
1141
|
+
args = ["simctl", 'list', 'devices']
|
1140
1142
|
hash = xcrun.run_command_in_context(args)
|
1141
1143
|
|
1142
1144
|
current_sdk = nil
|
@@ -1219,7 +1221,9 @@ module RunLoop
|
|
1219
1221
|
#
|
1220
1222
|
# @see #simctl_list
|
1221
1223
|
def simctl_list_runtimes
|
1222
|
-
|
1224
|
+
# Ensure correct CoreSimulator service is installed.
|
1225
|
+
RunLoop::Simctl.new
|
1226
|
+
args = ["simctl", 'list', 'runtimes']
|
1223
1227
|
hash = xcrun.run_command_in_context(args)
|
1224
1228
|
|
1225
1229
|
# Ex.
|
data/lib/run_loop/simctl.rb
CHANGED
@@ -15,10 +15,17 @@ module RunLoop
|
|
15
15
|
# @!visibility private
|
16
16
|
SIMCTL_PLIST_DIR = lambda {
|
17
17
|
dirname = File.dirname(__FILE__)
|
18
|
-
joined = File.join(dirname,
|
18
|
+
joined = File.join(dirname, "..", "..", "plists", "simctl")
|
19
19
|
File.expand_path(joined)
|
20
20
|
}.call
|
21
21
|
|
22
|
+
# @!visibility private
|
23
|
+
SIM_STATES = {
|
24
|
+
"Shutdown" => 1,
|
25
|
+
"Shutting Down" => 2,
|
26
|
+
"Booted" => 3,
|
27
|
+
}.freeze
|
28
|
+
|
22
29
|
# @!visibility private
|
23
30
|
def self.uia_automation_plist
|
24
31
|
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomation.plist')
|
@@ -29,6 +36,28 @@ module RunLoop
|
|
29
36
|
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomationPlugIn.plist')
|
30
37
|
end
|
31
38
|
|
39
|
+
# @!visibility private
|
40
|
+
def self.ensure_valid_core_simulator_service
|
41
|
+
require "run_loop/shell"
|
42
|
+
args = ["xcrun", "simctl", "help"]
|
43
|
+
|
44
|
+
max_tries = 3
|
45
|
+
3.times do |try|
|
46
|
+
hash = {}
|
47
|
+
begin
|
48
|
+
hash = Shell.run_shell_command(args)
|
49
|
+
if hash[:exit_status] != 0
|
50
|
+
RunLoop.log_debug("Invalid CoreSimulator service for active Xcode: try #{try + 1} of #{max_tries}")
|
51
|
+
else
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
rescue RunLoop::Shell::Error => _
|
55
|
+
RunLoop.log_debug("Invalid CoreSimulator service for active Xcode, retrying #{try + 1} of #{max_tries}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
32
61
|
# @!visibility private
|
33
62
|
attr_reader :device
|
34
63
|
|
@@ -37,6 +66,7 @@ module RunLoop
|
|
37
66
|
@ios_devices = []
|
38
67
|
@tvos_devices = []
|
39
68
|
@watchos_devices = []
|
69
|
+
Simctl.ensure_valid_core_simulator_service
|
40
70
|
end
|
41
71
|
|
42
72
|
# @!visibility private
|
@@ -76,7 +106,7 @@ module RunLoop
|
|
76
106
|
def app_container(device, bundle_id)
|
77
107
|
return nil if !xcode.version_gte_7?
|
78
108
|
cmd = ["simctl", "get_app_container", device.udid, bundle_id]
|
79
|
-
hash =
|
109
|
+
hash = shell_out_with_xcrun(cmd, DEFAULTS)
|
80
110
|
|
81
111
|
exit_status = hash[:exit_status]
|
82
112
|
if exit_status != 0
|
@@ -86,6 +116,253 @@ module RunLoop
|
|
86
116
|
end
|
87
117
|
end
|
88
118
|
|
119
|
+
# @!visibility private
|
120
|
+
def simulator_state_as_int(device)
|
121
|
+
plist = device.simulator_device_plist
|
122
|
+
pbuddy.plist_read("state", plist).to_i
|
123
|
+
end
|
124
|
+
|
125
|
+
# @!visibility private
|
126
|
+
def simulator_state_as_string(device)
|
127
|
+
string_for_sim_state(simulator_state_as_int(device))
|
128
|
+
end
|
129
|
+
|
130
|
+
# @!visibility private
|
131
|
+
def shutdown(device)
|
132
|
+
if simulator_state_as_int(device) == SIM_STATES["Shutdown"]
|
133
|
+
RunLoop.log_debug("Simulator is already shutdown")
|
134
|
+
true
|
135
|
+
else
|
136
|
+
cmd = ["simctl", "shutdown", device.udid]
|
137
|
+
hash = shell_out_with_xcrun(cmd, DEFAULTS)
|
138
|
+
|
139
|
+
exit_status = hash[:exit_status]
|
140
|
+
if exit_status != 0
|
141
|
+
|
142
|
+
if simulator_state_as_int(device) == SIM_STATES["Shutdown"]
|
143
|
+
RunLoop.log_debug("simctl shutdown called when state is 'Shutdown'; ignoring error")
|
144
|
+
else
|
145
|
+
raise RuntimeError,
|
146
|
+
%Q[Could not shutdown the simulator:
|
147
|
+
|
148
|
+
command: xcrun #{cmd.join(" ")}
|
149
|
+
simulator: #{device}
|
150
|
+
|
151
|
+
#{hash[:out]}
|
152
|
+
|
153
|
+
This usually means your CoreSimulator processes need to be restarted.
|
154
|
+
|
155
|
+
You can restart the CoreSimulator processes with this command:
|
156
|
+
|
157
|
+
$ bundle exec run-loop simctl manage-processes
|
158
|
+
|
159
|
+
]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# @!visibility private
|
167
|
+
#
|
168
|
+
# Waiting for anything but 'Shutdown' is not advised. The simulator reports
|
169
|
+
# that it is "Booted" long before it is ready to receive commands.
|
170
|
+
#
|
171
|
+
# Waiting for 'Shutdown' is required for erasing the simulator and launching
|
172
|
+
# launching the simulator with iOSDeviceManager.
|
173
|
+
def wait_for_shutdown(device, timeout, delay)
|
174
|
+
now = Time.now
|
175
|
+
poll_until = now + timeout
|
176
|
+
in_state = false
|
177
|
+
|
178
|
+
state = nil
|
179
|
+
|
180
|
+
while Time.now < poll_until
|
181
|
+
state = simulator_state_as_int(device)
|
182
|
+
in_state = state == SIM_STATES["Shutdown"]
|
183
|
+
break if in_state
|
184
|
+
sleep delay if delay != 0
|
185
|
+
end
|
186
|
+
|
187
|
+
elapsed = Time.now - now
|
188
|
+
RunLoop.log_debug("Waited for #{elapsed} seconds for device to have state: 'Shutdown'.")
|
189
|
+
|
190
|
+
unless in_state
|
191
|
+
string = string_for_sim_state(state)
|
192
|
+
raise "Expected 'Shutdown' state but found '#{string}' after waiting for #{elapsed} seconds."
|
193
|
+
end
|
194
|
+
in_state
|
195
|
+
end
|
196
|
+
|
197
|
+
# @!visibility private
|
198
|
+
# Erases the simulator.
|
199
|
+
#
|
200
|
+
# @param [RunLoop::Device] device The simulator to erase.
|
201
|
+
# @param [Numeric] wait_timeout How long to wait for the simulator to have
|
202
|
+
# state "Shutdown"; passed to #wait_for_shutdown.
|
203
|
+
# @param [Numeric] wait_delay How long to wait between calls to
|
204
|
+
# #simulator_state_as_int while waiting for the simulator have to state "Shutdown";
|
205
|
+
# passed to #wait_for_shutdown
|
206
|
+
def erase(device, wait_timeout, wait_delay)
|
207
|
+
require "run_loop/core_simulator"
|
208
|
+
CoreSimulator.quit_simulator
|
209
|
+
|
210
|
+
shutdown(device)
|
211
|
+
wait_for_shutdown(device, wait_timeout, wait_delay)
|
212
|
+
|
213
|
+
cmd = ["simctl", "erase", device.udid]
|
214
|
+
hash = shell_out_with_xcrun(cmd, DEFAULTS)
|
215
|
+
|
216
|
+
exit_status = hash[:exit_status]
|
217
|
+
if exit_status != 0
|
218
|
+
raise RuntimeError,
|
219
|
+
%Q[Could not erase the simulator:
|
220
|
+
|
221
|
+
command: xcrun #{cmd.join(" ")}
|
222
|
+
simulator: #{device}
|
223
|
+
|
224
|
+
#{hash[:out]}
|
225
|
+
|
226
|
+
This usually means your CoreSimulator processes need to be restarted.
|
227
|
+
|
228
|
+
You can restart the CoreSimulator processes with this command:
|
229
|
+
|
230
|
+
$ bundle exec run-loop simctl manage-processes
|
231
|
+
|
232
|
+
]
|
233
|
+
end
|
234
|
+
true
|
235
|
+
end
|
236
|
+
|
237
|
+
# @!visibility private
|
238
|
+
#
|
239
|
+
# Launches the app on on the device.
|
240
|
+
#
|
241
|
+
# Caller is responsible for the following:
|
242
|
+
#
|
243
|
+
# 1. Launching the simulator.
|
244
|
+
# 2. Installing the application.
|
245
|
+
#
|
246
|
+
# No checks are made.
|
247
|
+
#
|
248
|
+
# @param [RunLoop::Device] device The simulator to launch on.
|
249
|
+
# @param [RunLoop::App] app The app to launch.
|
250
|
+
# @param [Numeric] timeout How long to wait for simctl to complete.
|
251
|
+
def launch(device, app, timeout)
|
252
|
+
cmd = ["simctl", "launch", device.udid, app.bundle_identifier]
|
253
|
+
options = DEFAULTS.dup
|
254
|
+
options[:timeout] = timeout
|
255
|
+
|
256
|
+
hash = shell_out_with_xcrun(cmd, options)
|
257
|
+
|
258
|
+
exit_status = hash[:exit_status]
|
259
|
+
if exit_status != 0
|
260
|
+
raise RuntimeError,
|
261
|
+
%Q[Could not launch app on simulator:
|
262
|
+
|
263
|
+
command: xcrun #{cmd.join(" ")}
|
264
|
+
simulator: #{device}
|
265
|
+
app: #{app}
|
266
|
+
|
267
|
+
#{hash[:out]}
|
268
|
+
|
269
|
+
This usually means your CoreSimulator processes need to be restarted.
|
270
|
+
|
271
|
+
You can restart the CoreSimulator processes with this command:
|
272
|
+
|
273
|
+
$ bundle exec run-loop simctl manage-processes
|
274
|
+
|
275
|
+
]
|
276
|
+
end
|
277
|
+
true
|
278
|
+
end
|
279
|
+
|
280
|
+
# @!visibility private
|
281
|
+
#
|
282
|
+
# Launches the app on on the device.
|
283
|
+
#
|
284
|
+
# Caller is responsible for the following:
|
285
|
+
#
|
286
|
+
# 1. Launching the simulator.
|
287
|
+
# 2. That the application is installed; simctl uninstall will fail if app
|
288
|
+
# is installed.
|
289
|
+
#
|
290
|
+
# No checks are made.
|
291
|
+
#
|
292
|
+
# @param [RunLoop::Device] device The simulator to launch on.
|
293
|
+
# @param [RunLoop::App] app The app to launch.
|
294
|
+
# @param [Numeric] timeout How long to wait for simctl to complete.
|
295
|
+
def uninstall(device, app, timeout)
|
296
|
+
cmd = ["simctl", "uninstall", device.udid, app.bundle_identifier]
|
297
|
+
options = DEFAULTS.dup
|
298
|
+
options[:timeout] = timeout
|
299
|
+
|
300
|
+
hash = shell_out_with_xcrun(cmd, options)
|
301
|
+
|
302
|
+
exit_status = hash[:exit_status]
|
303
|
+
if exit_status != 0
|
304
|
+
raise RuntimeError,
|
305
|
+
%Q[Could not uninstall app from simulator:
|
306
|
+
|
307
|
+
command: xcrun #{cmd.join(" ")}
|
308
|
+
simulator: #{device}
|
309
|
+
app: #{app}
|
310
|
+
|
311
|
+
#{hash[:out]}
|
312
|
+
|
313
|
+
This usually means your CoreSimulator processes need to be restarted.
|
314
|
+
|
315
|
+
You can restart the CoreSimulator processes with this command:
|
316
|
+
|
317
|
+
$ bundle exec run-loop simctl manage-processes
|
318
|
+
|
319
|
+
]
|
320
|
+
end
|
321
|
+
true
|
322
|
+
end
|
323
|
+
|
324
|
+
# @!visibility private
|
325
|
+
#
|
326
|
+
# Launches the app on on the device.
|
327
|
+
#
|
328
|
+
# Caller is responsible for the following:
|
329
|
+
#
|
330
|
+
# 1. Launching the simulator.
|
331
|
+
#
|
332
|
+
# No checks are made.
|
333
|
+
#
|
334
|
+
# @param [RunLoop::Device] device The simulator to launch on.
|
335
|
+
# @param [RunLoop::App] app The app to launch.
|
336
|
+
# @param [Numeric] timeout How long to wait for simctl to complete.
|
337
|
+
def install(device, app, timeout)
|
338
|
+
cmd = ["simctl", "install", device.udid, app.path]
|
339
|
+
options = DEFAULTS.dup
|
340
|
+
options[:timeout] = timeout
|
341
|
+
|
342
|
+
hash = shell_out_with_xcrun(cmd, options)
|
343
|
+
|
344
|
+
exit_status = hash[:exit_status]
|
345
|
+
if exit_status != 0
|
346
|
+
raise RuntimeError,
|
347
|
+
%Q[Could not install app on simulator:
|
348
|
+
|
349
|
+
command: xcrun #{cmd.join(" ")}
|
350
|
+
simulator: #{device}
|
351
|
+
app: #{app}
|
352
|
+
|
353
|
+
#{hash[:out]}
|
354
|
+
|
355
|
+
This usually means your CoreSimulator processes need to be restarted.
|
356
|
+
|
357
|
+
You can restart the CoreSimulator processes with this command:
|
358
|
+
|
359
|
+
$ bundle exec run-loop simctl manage-processes
|
360
|
+
|
361
|
+
]
|
362
|
+
end
|
363
|
+
true
|
364
|
+
end
|
365
|
+
|
89
366
|
# @!visibility private
|
90
367
|
#
|
91
368
|
# SimControl compatibility
|
@@ -110,10 +387,26 @@ module RunLoop
|
|
110
387
|
private
|
111
388
|
|
112
389
|
# @!visibility private
|
113
|
-
attr_reader :ios_devices, :tvos_devices, :watchos_devices
|
390
|
+
attr_reader :ios_devices, :tvos_devices, :watchos_devices, :pbuddy
|
391
|
+
|
392
|
+
# @!visibility private
|
393
|
+
def pbuddy
|
394
|
+
@pbuddy ||= RunLoop::PlistBuddy.new
|
395
|
+
end
|
396
|
+
|
397
|
+
# @!visibility private
|
398
|
+
def string_for_sim_state(integer)
|
399
|
+
SIM_STATES.each do |key, value|
|
400
|
+
if value == integer
|
401
|
+
return key
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
raise ArgumentError, "Could not find state for #{integer}"
|
406
|
+
end
|
114
407
|
|
115
408
|
# @!visibility private
|
116
|
-
def
|
409
|
+
def shell_out_with_xcrun(array, options)
|
117
410
|
merged = DEFAULTS.merge(options)
|
118
411
|
xcrun.run_command_in_context(array, merged)
|
119
412
|
end
|
@@ -143,7 +436,7 @@ module RunLoop
|
|
143
436
|
@watchos_devices = []
|
144
437
|
|
145
438
|
cmd = ["simctl", "list", "devices", "--json"]
|
146
|
-
hash =
|
439
|
+
hash = shell_out_with_xcrun(cmd, DEFAULTS)
|
147
440
|
|
148
441
|
out = hash[:out]
|
149
442
|
exit_status = hash[:exit_status]
|