illuminator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/gem/README.md +37 -0
- data/gem/bin/illuminatorTestRunner.rb +22 -0
- data/gem/lib/illuminator.rb +171 -0
- data/gem/lib/illuminator/argument-parsing.rb +299 -0
- data/gem/lib/illuminator/automation-builder.rb +39 -0
- data/gem/lib/illuminator/automation-runner.rb +589 -0
- data/gem/lib/illuminator/build-artifacts.rb +118 -0
- data/gem/lib/illuminator/device-installer.rb +45 -0
- data/gem/lib/illuminator/host-utils.rb +42 -0
- data/gem/lib/illuminator/instruments-runner.rb +301 -0
- data/gem/lib/illuminator/javascript-runner.rb +98 -0
- data/gem/lib/illuminator/listeners/console-logger.rb +32 -0
- data/gem/lib/illuminator/listeners/full-output.rb +13 -0
- data/gem/lib/illuminator/listeners/instruments-listener.rb +22 -0
- data/gem/lib/illuminator/listeners/intermittent-failure-detector.rb +49 -0
- data/gem/lib/illuminator/listeners/pretty-output.rb +26 -0
- data/gem/lib/illuminator/listeners/saltinel-agent.rb +66 -0
- data/gem/lib/illuminator/listeners/saltinel-listener.rb +26 -0
- data/gem/lib/illuminator/listeners/start-detector.rb +52 -0
- data/gem/lib/illuminator/listeners/stop-detector.rb +46 -0
- data/gem/lib/illuminator/listeners/test-listener.rb +58 -0
- data/gem/lib/illuminator/listeners/trace-error-detector.rb +38 -0
- data/gem/lib/illuminator/options.rb +96 -0
- data/gem/lib/illuminator/resources/IlluminatorGeneratedEnvironment.erb +13 -0
- data/gem/lib/illuminator/resources/IlluminatorGeneratedRunnerForInstruments.erb +19 -0
- data/gem/lib/illuminator/test-definitions.rb +23 -0
- data/gem/lib/illuminator/test-suite.rb +155 -0
- data/gem/lib/illuminator/version.rb +3 -0
- data/gem/lib/illuminator/xcode-builder.rb +144 -0
- data/gem/lib/illuminator/xcode-utils.rb +219 -0
- data/gem/resources/BuildConfiguration.xcconfig +10 -0
- data/gem/resources/js/AppMap.js +767 -0
- data/gem/resources/js/Automator.js +1132 -0
- data/gem/resources/js/Base64.js +142 -0
- data/gem/resources/js/Bridge.js +102 -0
- data/gem/resources/js/Config.js +92 -0
- data/gem/resources/js/Extensions.js +2025 -0
- data/gem/resources/js/Illuminator.js +228 -0
- data/gem/resources/js/Preferences.js +24 -0
- data/gem/resources/scripts/UIAutomationBridge.rb +248 -0
- data/gem/resources/scripts/common.applescript +25 -0
- data/gem/resources/scripts/diff_png.sh +61 -0
- data/gem/resources/scripts/kill_all_sim_processes.sh +17 -0
- data/gem/resources/scripts/plist_to_json.sh +40 -0
- data/gem/resources/scripts/set_hardware_keyboard.applescript +0 -0
- metadata +225 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# Convenience functions for command-line actions done in Xcode
|
5
|
+
module Illuminator
|
6
|
+
class BuildArtifacts
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@_root = nil
|
11
|
+
@artifacts_have_been_created = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_root(dir_raw)
|
15
|
+
dir = Illuminator::HostUtils.realpath(dir_raw)
|
16
|
+
if @_root != dir and @artifacts_have_been_created
|
17
|
+
puts "Warning: changing BuildArtifacts root to '#{dir}' after creating artifacts in '#{@_root}'".red
|
18
|
+
end
|
19
|
+
@_root = dir
|
20
|
+
end
|
21
|
+
|
22
|
+
def _setup_and_use(dir, skip_setup)
|
23
|
+
raise TypeError, "The buildArtifact root directory is nil; perhaps it was not set" if @_root.nil?
|
24
|
+
unless skip_setup or File.directory?(dir)
|
25
|
+
FileUtils.mkdir_p dir
|
26
|
+
@artifacts_have_been_created = true
|
27
|
+
end
|
28
|
+
dir
|
29
|
+
end
|
30
|
+
|
31
|
+
################## Directories
|
32
|
+
|
33
|
+
def root(skip_setup = false)
|
34
|
+
_setup_and_use @_root, skip_setup
|
35
|
+
end
|
36
|
+
|
37
|
+
def xcode(skip_setup = false)
|
38
|
+
_setup_and_use "#{@_root}/xcode", skip_setup
|
39
|
+
end
|
40
|
+
|
41
|
+
def derived_data(skip_setup = false)
|
42
|
+
_setup_and_use "#{@_root}/xcodeDerivedData", skip_setup
|
43
|
+
end
|
44
|
+
|
45
|
+
def instruments(skip_setup = false)
|
46
|
+
_setup_and_use "#{@_root}/instruments", skip_setup
|
47
|
+
end
|
48
|
+
|
49
|
+
def crash_reports(skip_setup = false)
|
50
|
+
_setup_and_use "#{@_root}/crashReports", skip_setup
|
51
|
+
end
|
52
|
+
|
53
|
+
def object_files(skip_setup = false)
|
54
|
+
_setup_and_use "#{@_root}/objectFiles", skip_setup
|
55
|
+
end
|
56
|
+
|
57
|
+
def console(skip_setup = false)
|
58
|
+
_setup_and_use "#{@_root}/console", skip_setup
|
59
|
+
end
|
60
|
+
|
61
|
+
def ui_automation(skip_setup = false)
|
62
|
+
_setup_and_use "#{@_root}/UIAutomation-outputs", skip_setup
|
63
|
+
end
|
64
|
+
|
65
|
+
def state(skip_setup = false)
|
66
|
+
_setup_and_use "#{@_root}/Illuminator-state", skip_setup
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
################## FILES
|
71
|
+
|
72
|
+
def app_location(app_name = nil)
|
73
|
+
app_output_directory = xcode
|
74
|
+
if app_name.nil?
|
75
|
+
# assume that only one app exists and use that
|
76
|
+
return Dir["#{app_output_directory}/*.app"][0]
|
77
|
+
else
|
78
|
+
return "#{app_output_directory}/#{app_name}.app"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def xcpretty_report_file(skip_setup = false)
|
83
|
+
_setup_and_use "#{@_root}/xcpretty", skip_setup
|
84
|
+
"#{@_root}/xcpretty/report.xml"
|
85
|
+
end
|
86
|
+
|
87
|
+
def coverage_report_file(skip_setup = false)
|
88
|
+
_setup_and_use @_root, skip_setup
|
89
|
+
"#{@_root}/coverage.xml"
|
90
|
+
end
|
91
|
+
|
92
|
+
def illuminator_js_runner(skip_setup = false)
|
93
|
+
_setup_and_use @_root, skip_setup
|
94
|
+
"#{@_root}/IlluminatorGeneratedRunnerForInstruments.js"
|
95
|
+
end
|
96
|
+
|
97
|
+
def illuminator_js_environment(skip_setup = false)
|
98
|
+
_setup_and_use @_root, skip_setup
|
99
|
+
"#{@_root}/IlluminatorGeneratedEnvironment.js"
|
100
|
+
end
|
101
|
+
|
102
|
+
def illuminator_config_file(skip_setup = false)
|
103
|
+
_setup_and_use @_root, skip_setup
|
104
|
+
"#{@_root}/IlluminatorGeneratedConfig.json"
|
105
|
+
end
|
106
|
+
|
107
|
+
def junit_report_file(skip_setup = false)
|
108
|
+
_setup_and_use @_root, skip_setup
|
109
|
+
"#{@_root}/IlluminatorJUnitReport.xml"
|
110
|
+
end
|
111
|
+
|
112
|
+
def illuminator_rerun_failed_tests_settings(skip_setup = false)
|
113
|
+
_setup_and_use @_root, skip_setup
|
114
|
+
"#{@_root}/IlluminatorRerunFailedTestsSettings.json"
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
require_relative './host-utils'
|
4
|
+
|
5
|
+
# Wrapper for possible methods of installing an app on physical hardware
|
6
|
+
class DeviceInstaller
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
installers = ['ios-deploy']
|
11
|
+
|
12
|
+
@installed_installers = {}
|
13
|
+
installers.each { |exe| @installed_installers[exe] = Illuminator::HostUtils.which(exe) }
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def _install_using_ios_deploy(app_location, hardware_id)
|
18
|
+
# TODO: actually watch the output of this command
|
19
|
+
cmd = "#{@installed_installers['ios-deploy']} -b '#{app_location}' -i #{hardware_id} -r -n"
|
20
|
+
puts cmd.green
|
21
|
+
puts `#{cmd}`
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def install_on_device(app_location, hardware_id, specific_method = nil)
|
26
|
+
# if nothing is specified, just get the first one that exists
|
27
|
+
if specific_method.nil?
|
28
|
+
@installed_installers.each do |name, path|
|
29
|
+
specific_method = path
|
30
|
+
break unless specific_method.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# run the appropriate helper for doing this
|
35
|
+
puts "Installing #{app_location} on device #{hardware_id} using #{specific_method}"
|
36
|
+
case specific_method
|
37
|
+
when /ios-deploy$/
|
38
|
+
_install_using_ios_deploy(app_location, hardware_id)
|
39
|
+
else
|
40
|
+
puts "None of the following utilities for app installation appear to be installed: #{@installed_installers.keys.to_s}".red
|
41
|
+
raise NotImplementedError, "No app installation available with name " + specific_method.to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Illuminator
|
4
|
+
class HostUtils
|
5
|
+
|
6
|
+
# Cross-platform way of finding an executable in the $PATH.
|
7
|
+
# based on http://stackoverflow.com/a/5471032/2063546
|
8
|
+
#
|
9
|
+
# which('ruby') #=> /usr/bin/ruby
|
10
|
+
def self.which program
|
11
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
12
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
13
|
+
exts.each do |ext|
|
14
|
+
exe = File.join(path, "#{program}#{ext}")
|
15
|
+
return exe if File.executable?(exe) unless File.directory?(exe)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
return nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.save_json(hash_object, path)
|
22
|
+
f = File.open(path, 'w')
|
23
|
+
f << JSON.pretty_generate(hash_object)
|
24
|
+
f.close
|
25
|
+
end
|
26
|
+
|
27
|
+
# try to simplify the path, if it exists
|
28
|
+
def self.realpath(path)
|
29
|
+
epath = File.expand_path path
|
30
|
+
|
31
|
+
# use expanded path if regular one fails
|
32
|
+
path = epath unless (File.exists? path) or (File.directory? path)
|
33
|
+
|
34
|
+
# use given path if it doesn't exist (can't take a real path of a nonexistent path)
|
35
|
+
return path unless (File.exists? path) or (File.directory? path)
|
36
|
+
|
37
|
+
Pathname.new(path).realpath.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'colorize'
|
3
|
+
require 'pty'
|
4
|
+
|
5
|
+
require_relative './xcode-utils'
|
6
|
+
require_relative './build-artifacts'
|
7
|
+
require_relative 'listeners/start-detector'
|
8
|
+
require_relative 'listeners/stop-detector'
|
9
|
+
require_relative 'listeners/intermittent-failure-detector'
|
10
|
+
require_relative 'listeners/trace-error-detector'
|
11
|
+
|
12
|
+
####################################################################################################
|
13
|
+
# status
|
14
|
+
####################################################################################################
|
15
|
+
|
16
|
+
class ParsedInstrumentsMessage
|
17
|
+
|
18
|
+
attr_accessor :message
|
19
|
+
attr_accessor :full_line
|
20
|
+
attr_accessor :status
|
21
|
+
attr_accessor :date
|
22
|
+
attr_accessor :time
|
23
|
+
attr_accessor :tz
|
24
|
+
|
25
|
+
# parse lines in the form: 2014-10-20 20:43:41 +0000 Default: BLAH BLAH BLAH ACTUAL MESSAGE
|
26
|
+
def self.from_line (line)
|
27
|
+
parsed = line.match(/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) ([+-]\d{4}) ([^:]+): (.*)$/).to_a
|
28
|
+
_, date_string, time_string, tz_string, status_string, msg_string = parsed
|
29
|
+
|
30
|
+
message = ParsedInstrumentsMessage.new
|
31
|
+
message.full_line = line
|
32
|
+
message.message = msg_string
|
33
|
+
message.status = parse_status(status_string)
|
34
|
+
message.date = date_string
|
35
|
+
message.time = time_string
|
36
|
+
message.tz = tz_string
|
37
|
+
|
38
|
+
message
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_status(status)
|
42
|
+
case status
|
43
|
+
when /start/i then :start
|
44
|
+
when /stopped/i then :stopped
|
45
|
+
when /pass/i then :pass
|
46
|
+
when /fail/i then :fail
|
47
|
+
when /error/i then :error
|
48
|
+
when /warning/i then :warning
|
49
|
+
when /issue/i then :issue
|
50
|
+
when /default/i then :default
|
51
|
+
when /debug/i then :debug
|
52
|
+
else :unknown
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class InstrumentsExitStatus
|
60
|
+
attr_accessor :normal # whether the command finished under normal circumstances (eventually)
|
61
|
+
attr_accessor :fatal_error # whether the command finished in a way that restarting won't fix
|
62
|
+
attr_accessor :fatal_reason # a message about why the command can't be restarted
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
####################################################################################################
|
67
|
+
# command builder
|
68
|
+
####################################################################################################
|
69
|
+
|
70
|
+
class InstrumentsRunner
|
71
|
+
include StartDetectorEventSink
|
72
|
+
include StopDetectorEventSink
|
73
|
+
include IntermittentFailureDetectorEventSink
|
74
|
+
include TraceErrorDetectorEventSink
|
75
|
+
|
76
|
+
attr_accessor :app_location # the app to run
|
77
|
+
attr_accessor :hardware_id # hardware id specifier
|
78
|
+
attr_accessor :sim_device # sim device id specifier
|
79
|
+
attr_accessor :sim_language # sim language (unsupported currently)
|
80
|
+
attr_accessor :attempts # number of times to try running instruments before giving up
|
81
|
+
attr_accessor :startup_timeout # amount of time to wait for initial instruments output
|
82
|
+
attr_accessor :max_silence # if instruments goes silent for this long, we kill it.
|
83
|
+
|
84
|
+
attr_reader :started
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
@listeners = Hash.new
|
88
|
+
@attempts = 5
|
89
|
+
@startup_timeout = 30
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_listener (name, listener)
|
93
|
+
@listeners[name] = listener
|
94
|
+
end
|
95
|
+
|
96
|
+
def cleanup
|
97
|
+
dirs_to_remove = []
|
98
|
+
build_artifact_keys = [:instruments]
|
99
|
+
# get the directories without creating them (the 'true' arg), add them to our list
|
100
|
+
build_artifact_keys.each do |key|
|
101
|
+
dir = Illuminator::BuildArtifacts.instance.method(key).call(true)
|
102
|
+
dirs_to_remove << dir
|
103
|
+
end
|
104
|
+
|
105
|
+
# remove directories in the list
|
106
|
+
dirs_to_remove.each do |d|
|
107
|
+
dir = Illuminator::HostUtils.realpath d
|
108
|
+
puts "InstrumentsRunner cleanup: removing #{dir}"
|
109
|
+
FileUtils.rmtree dir
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def start_detector_triggered
|
114
|
+
@fully_started = true
|
115
|
+
end
|
116
|
+
|
117
|
+
def stop_detector_triggered(fatal, message)
|
118
|
+
if !fatal
|
119
|
+
@should_abort = true
|
120
|
+
else
|
121
|
+
@fatal_error = true
|
122
|
+
@fatal_reason = message
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def intermittent_failure_detector_triggered message
|
127
|
+
@fully_started = true
|
128
|
+
force_stop("Detected an intermittent failure condition - #{message}")
|
129
|
+
end
|
130
|
+
|
131
|
+
def trace_error_detector_triggered(fatal, message)
|
132
|
+
puts "Detected a trace error - #{message}".yellow
|
133
|
+
if fatal
|
134
|
+
@fatal_error = true
|
135
|
+
@fatal_reason = message
|
136
|
+
else
|
137
|
+
@should_reset_everything = true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def force_stop why
|
142
|
+
puts "\n #{why}".red
|
143
|
+
@should_abort = true
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def add_necessary_instruments_listeners(saltinel)
|
148
|
+
# add saltinel listener
|
149
|
+
start_detector = StartDetector.new(saltinel)
|
150
|
+
start_detector.event_sink = self
|
151
|
+
add_listener("start_detector", start_detector)
|
152
|
+
|
153
|
+
# add trace error listener
|
154
|
+
trace_detector = TraceErrorDetector.new
|
155
|
+
trace_detector.event_sink = self
|
156
|
+
add_listener("trace_error_detector", trace_detector)
|
157
|
+
|
158
|
+
# add fatal error listener
|
159
|
+
stop_detector = StopDetector.new
|
160
|
+
stop_detector.event_sink = self
|
161
|
+
add_listener("stop_detector", stop_detector)
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# Build the proper command and run it
|
166
|
+
def run_once saltinel
|
167
|
+
report_path = Illuminator::BuildArtifacts.instance.instruments
|
168
|
+
|
169
|
+
add_necessary_instruments_listeners(saltinel)
|
170
|
+
|
171
|
+
global_js_file = Illuminator::BuildArtifacts.instance.illuminator_js_runner
|
172
|
+
xcode_path = Illuminator::XcodeUtils.instance.get_xcode_path
|
173
|
+
template_path = Illuminator::XcodeUtils.instance.get_instruments_template_path
|
174
|
+
|
175
|
+
command = "env DEVELOPER_DIR='#{xcode_path}' /usr/bin/instruments"
|
176
|
+
if !@hardware_id.nil?
|
177
|
+
command << " -w '" + @hardware_id + "'"
|
178
|
+
elsif !@sim_device.nil?
|
179
|
+
command << " -w '" + @sim_device + "'"
|
180
|
+
end
|
181
|
+
|
182
|
+
command << " -t '#{template_path}' "
|
183
|
+
command << "'#{@app_location}'"
|
184
|
+
command << " -e UIASCRIPT '#{global_js_file}'"
|
185
|
+
command << " -e UIARESULTSPATH '#{report_path}'"
|
186
|
+
|
187
|
+
command << " #{@sim_language}" if @sim_language
|
188
|
+
|
189
|
+
directory = Dir.pwd
|
190
|
+
ret = nil
|
191
|
+
# change directories and successfully change back
|
192
|
+
begin
|
193
|
+
Dir.chdir(report_path)
|
194
|
+
ret = run_instruments_command command
|
195
|
+
ensure
|
196
|
+
Dir.chdir(directory)
|
197
|
+
end
|
198
|
+
return ret
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
# kill the instruments child process
|
203
|
+
def kill_instruments(r, w, pid)
|
204
|
+
puts "killing Instruments (pid #{pid})...".red
|
205
|
+
begin
|
206
|
+
Process.kill(9, pid)
|
207
|
+
w.close
|
208
|
+
r.close
|
209
|
+
Process.wait(pid)
|
210
|
+
rescue PTY::ChildExited
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
# Run an instruments command until it looks like it started successfully or failed non-intermittently
|
216
|
+
def run_instruments_command (command)
|
217
|
+
@fully_started = false
|
218
|
+
@should_abort = false
|
219
|
+
@fatal_error = false
|
220
|
+
@fatal_reason = nil
|
221
|
+
puts command.green
|
222
|
+
remaining_attempts = @attempts
|
223
|
+
|
224
|
+
# launch & re-launch instruments until it triggers the StartDetector
|
225
|
+
while (not @fatal_error) && (not @fully_started) && remaining_attempts > 0 do
|
226
|
+
@should_reset_everything = false
|
227
|
+
successful_run = true
|
228
|
+
remaining_attempts = remaining_attempts - 1
|
229
|
+
|
230
|
+
Illuminator::XcodeUtils.kill_all_instruments_processes # because sometimes they stick around and add up
|
231
|
+
|
232
|
+
puts "\nRelaunching instruments. #{remaining_attempts} retries left".red unless (remaining_attempts + 1) == @attempts
|
233
|
+
|
234
|
+
# spawn process and catch unexpected exits
|
235
|
+
begin
|
236
|
+
PTY.spawn(*command) do |r, w, pid|
|
237
|
+
silence_duration = 0
|
238
|
+
|
239
|
+
done_reading_output = false
|
240
|
+
# select on the output and send it to the listeners
|
241
|
+
while not done_reading_output do
|
242
|
+
if @should_abort || @fatal_error
|
243
|
+
successful_run = false
|
244
|
+
done_reading_output = true
|
245
|
+
kill_instruments(r, w, pid)
|
246
|
+
|
247
|
+
elsif @should_reset_everything
|
248
|
+
successful_run = false
|
249
|
+
done_reading_output = true
|
250
|
+
kill_instruments(r, w, pid)
|
251
|
+
unless @sim_device.nil?
|
252
|
+
Illuminator::XcodeUtils.kill_all_simulator_processes @sim_device
|
253
|
+
Illuminator::XcodeUtils.instance.reset_simulator @sim_device
|
254
|
+
end
|
255
|
+
|
256
|
+
elsif IO.select([r], nil, nil, @startup_timeout) then
|
257
|
+
line = r.readline.rstrip
|
258
|
+
@listeners.each { |_, listener| listener.receive(ParsedInstrumentsMessage.from_line(line)) }
|
259
|
+
elsif not @fully_started
|
260
|
+
successful_run = false
|
261
|
+
done_reading_output = true
|
262
|
+
puts "\n Timeout #{@startup_timeout} reached without any output - ".red
|
263
|
+
kill_instruments(r, w, pid)
|
264
|
+
puts "killing simulator processes...".red
|
265
|
+
Illuminator::XcodeUtils.kill_all_simulator_processes @sim_device
|
266
|
+
# TODO: might be necessary to delete any app crashes at this point
|
267
|
+
else
|
268
|
+
# We failed to get output for @startupTimeout, but that's probably OK since we've successfully started
|
269
|
+
# But we still enforce a maximum time spent without output
|
270
|
+
|
271
|
+
silence_duration += @startup_timeout # (the amount of time we waited for a select)
|
272
|
+
puts "Instruments seems to have started but has not produced output in #{@startup_timeout} seconds".yellow
|
273
|
+
if @max_silence < silence_duration
|
274
|
+
@should_abort = true
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
rescue EOFError
|
281
|
+
# normal termination
|
282
|
+
rescue Errno::ECHILD, Errno::EIO, PTY::ChildExited
|
283
|
+
STDERR.puts 'Instruments exited unexpectedly'
|
284
|
+
if @fully_started
|
285
|
+
successful_run = false
|
286
|
+
done_reading_output = true
|
287
|
+
end
|
288
|
+
ensure
|
289
|
+
@listeners.each { |_, listener| listener.on_automation_finished }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
exit_status = InstrumentsExitStatus.new
|
294
|
+
exit_status.normal = successful_run
|
295
|
+
exit_status.fatal_error = @fatal_error
|
296
|
+
exit_status.fatal_reason = @fatal_reason
|
297
|
+
|
298
|
+
return exit_status
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|