fastlane 2.54.0.beta.20170822010003 → 2.54.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/deliver/lib/deliver/loader.rb +29 -3
- data/deliver/lib/deliver/upload_metadata.rb +18 -0
- data/fastlane/lib/fastlane/actions/opt_out_crash_reporting.rb +1 -1
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/lib/fastlane_core.rb +1 -0
- data/fastlane_core/lib/fastlane_core/crash_reporter/crash_reporter.rb +1 -1
- data/fastlane_core/lib/fastlane_core/device_manager.rb +12 -12
- data/fastlane_core/lib/fastlane_core/test_parser.rb +145 -0
- data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
- data/pilot/lib/pilot/tester_manager.rb +8 -1
- data/sigh/lib/sigh/runner.rb +27 -18
- data/snapshot/README.md +3 -1
- data/snapshot/lib/assets/SnapshotHelper.swift +57 -35
- data/snapshot/lib/assets/SnapshotHelperXcode8.swift +173 -0
- data/snapshot/lib/snapshot.rb +6 -0
- data/snapshot/lib/snapshot/collector.rb +37 -11
- data/snapshot/lib/snapshot/detect_values.rb +4 -1
- data/snapshot/lib/snapshot/fixes/simulator_zoom_fix.rb +1 -1
- data/snapshot/lib/snapshot/options.rb +5 -0
- data/snapshot/lib/snapshot/reports_generator.rb +34 -3
- data/snapshot/lib/snapshot/runner.rb +30 -215
- data/snapshot/lib/snapshot/setup.rb +9 -3
- data/snapshot/lib/snapshot/simulator_launchers/launcher_configuration.rb +53 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +203 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +129 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_xcode_8.rb +110 -0
- data/snapshot/lib/snapshot/test_command_generator.rb +39 -107
- data/snapshot/lib/snapshot/test_command_generator_base.rb +94 -0
- data/snapshot/lib/snapshot/test_command_generator_xcode_8.rb +65 -0
- data/snapshot/lib/snapshot/update.rb +4 -2
- data/spaceship/lib/spaceship/portal/provisioning_profile.rb +5 -2
- data/spaceship/lib/spaceship/test_flight/client.rb +8 -0
- data/spaceship/lib/spaceship/test_flight/tester.rb +20 -4
- metadata +26 -17
@@ -9,15 +9,21 @@ module Snapshot
|
|
9
9
|
end
|
10
10
|
|
11
11
|
File.write(snapfile_path, File.read("#{Snapshot::ROOT}/lib/assets/SnapfileTemplate"))
|
12
|
-
|
12
|
+
snapshot_helper_filename = "SnapshotHelperXcode8.swift"
|
13
|
+
if Helper.xcode_at_least?("9.0")
|
14
|
+
snapshot_helper_filename = "SnapshotHelper.swift"
|
15
|
+
end
|
16
|
+
|
17
|
+
# ensure that upgrade is cause when going from 8 to 9
|
18
|
+
File.write(File.join(path, snapshot_helper_filename), File.read("#{Snapshot::ROOT}/lib/assets/#{snapshot_helper_filename}"))
|
13
19
|
|
14
|
-
puts "✅ Successfully created
|
20
|
+
puts "✅ Successfully created #{snapshot_helper_filename} '#{File.join(path, snapshot_helper_filename)}'".green
|
15
21
|
puts "✅ Successfully created new Snapfile at '#{snapfile_path}'".green
|
16
22
|
|
17
23
|
puts "-------------------------------------------------------".yellow
|
18
24
|
puts "Open your Xcode project and make sure to do the following:".yellow
|
19
25
|
puts "1) Add a new UI Test target to your project".yellow
|
20
|
-
puts "2) Add the ./fastlane
|
26
|
+
puts "2) Add the ./fastlane/#{snapshot_helper_filename} to your UI Test target".yellow
|
21
27
|
puts " You can move the file anywhere you want".yellow
|
22
28
|
puts "3) Call `setupSnapshot(app)` when launching your app".yellow
|
23
29
|
puts ""
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
module Snapshot
|
3
|
+
class SimulatorLauncherConfiguration
|
4
|
+
# both
|
5
|
+
attr_accessor :languages
|
6
|
+
attr_accessor :devices
|
7
|
+
attr_accessor :add_photos
|
8
|
+
attr_accessor :add_videos
|
9
|
+
attr_accessor :clean
|
10
|
+
attr_accessor :erase_simulator
|
11
|
+
attr_accessor :localize_simulator
|
12
|
+
attr_accessor :reinstall_app
|
13
|
+
attr_accessor :app_identifier
|
14
|
+
|
15
|
+
# xcode 8
|
16
|
+
attr_accessor :number_of_retries
|
17
|
+
attr_accessor :stop_after_first_error
|
18
|
+
attr_accessor :output_simulator_logs
|
19
|
+
|
20
|
+
# runner
|
21
|
+
attr_accessor :launch_args_set
|
22
|
+
attr_accessor :output_directory
|
23
|
+
|
24
|
+
# xcode 9
|
25
|
+
attr_accessor :concurrent_simulators
|
26
|
+
alias concurrent_simulators? concurrent_simulators
|
27
|
+
|
28
|
+
def initialize(snapshot_config: nil)
|
29
|
+
@languages = snapshot_config[:languages]
|
30
|
+
@devices = snapshot_config[:devices]
|
31
|
+
@add_photos = snapshot_config[:add_photos]
|
32
|
+
@add_videos = snapshot_config[:add_videos]
|
33
|
+
@clean = snapshot_config[:clean]
|
34
|
+
@erase_simulator = snapshot_config[:erase_simulator]
|
35
|
+
@localize_simulator = snapshot_config[:localize_simulator]
|
36
|
+
@reinstall_app = snapshot_config[:reinstall_app]
|
37
|
+
@app_identifier = snapshot_config[:app_identifier]
|
38
|
+
@number_of_retries = snapshot_config[:number_of_retries]
|
39
|
+
@stop_after_first_error = snapshot_config[:stop_after_first_error]
|
40
|
+
@output_simulator_logs = snapshot_config[:output_simulator_logs]
|
41
|
+
@output_directory = snapshot_config[:output_directory]
|
42
|
+
@concurrent_simulators = snapshot_config[:concurrent_simulators]
|
43
|
+
|
44
|
+
launch_arguments = Array(snapshot_config[:launch_arguments])
|
45
|
+
# if more than 1 set of arguments, use a tuple with an index
|
46
|
+
if launch_arguments.count == 1
|
47
|
+
@launch_args_set = [launch_arguments]
|
48
|
+
else
|
49
|
+
@launch_args_set = launch_arguments.map.with_index { |e, i| [i, e] }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'snapshot/simulator_launchers/simulator_launcher_base'
|
2
|
+
|
3
|
+
module Snapshot
|
4
|
+
class SimulatorLauncher < SimulatorLauncherBase
|
5
|
+
# With Xcode 9's ability to run tests on multiple concurrent simulators,
|
6
|
+
# this method sets the maximum number of simulators to run simultaneously
|
7
|
+
# to avoid overloading your machine.
|
8
|
+
def default_number_of_simultaneous_simulators
|
9
|
+
cpu_count = CPUInspector.cpu_count
|
10
|
+
if cpu_count <= 2
|
11
|
+
return cpu_count
|
12
|
+
end
|
13
|
+
|
14
|
+
return cpu_count - 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def take_screenshots_simultaneously
|
18
|
+
languages_finished = {}
|
19
|
+
launcher_config.launch_args_set.each do |launch_args|
|
20
|
+
launcher_config.languages.each_with_index do |language, language_index|
|
21
|
+
locale = nil
|
22
|
+
if language.kind_of?(Array)
|
23
|
+
locale = language[1]
|
24
|
+
language = language[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Clear logs so subsequent xcodebuild executions dont append to old ones
|
28
|
+
log_path = xcodebuild_log_path(language: language, locale: locale)
|
29
|
+
File.delete(log_path) if File.exist?(log_path)
|
30
|
+
|
31
|
+
# Break up the array of devices into chunks that can
|
32
|
+
# be run simultaneously.
|
33
|
+
if launcher_config.concurrent_simulators?
|
34
|
+
device_batches = launcher_config.devices.each_slice(default_number_of_simultaneous_simulators).to_a
|
35
|
+
else
|
36
|
+
# Put each device in it's own array to run tests one at a time
|
37
|
+
device_batches = launcher_config.devices.map { |d| [d] }
|
38
|
+
end
|
39
|
+
|
40
|
+
device_batches.each do |devices|
|
41
|
+
languages_finished[language] = launch_simultaneously(devices, language, locale, launch_args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
launcher_config.devices.each_with_object({}) do |device, results_hash|
|
46
|
+
results_hash[device] = languages_finished
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def launch_simultaneously(devices, language, locale, launch_arguments)
|
51
|
+
prepare_for_launch(language, locale, launch_arguments)
|
52
|
+
|
53
|
+
add_media(devices, :photo, launcher_config.add_photos) if launcher_config.add_photos
|
54
|
+
add_media(devices, :video, launcher_config.add_videos) if launcher_config.add_videos
|
55
|
+
|
56
|
+
command = TestCommandGenerator.generate(
|
57
|
+
devices: devices,
|
58
|
+
language: language,
|
59
|
+
locale: locale,
|
60
|
+
log_path: xcodebuild_log_path(language: language, locale: locale)
|
61
|
+
)
|
62
|
+
|
63
|
+
UI.important("Running snapshot on: #{devices.join(', ')}")
|
64
|
+
|
65
|
+
execute(command: command, language: language, locale: locale, launch_args: launch_arguments, devices: devices)
|
66
|
+
|
67
|
+
return copy_screenshots(language: language, locale: locale, launch_args: launch_arguments)
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute(retries = 0, command: nil, language: nil, locale: nil, launch_args: nil, devices: nil)
|
71
|
+
prefix_hash = [
|
72
|
+
{
|
73
|
+
prefix: "Running Tests: ",
|
74
|
+
block: proc do |value|
|
75
|
+
value.include?("Touching")
|
76
|
+
end
|
77
|
+
}
|
78
|
+
]
|
79
|
+
FastlaneCore::CommandExecutor.execute(command: command,
|
80
|
+
print_all: true,
|
81
|
+
print_command: true,
|
82
|
+
prefix: prefix_hash,
|
83
|
+
loading: "Loading...",
|
84
|
+
error: proc do |output, return_code|
|
85
|
+
self.collected_errors.concat(failed_devices.map do |device, messages|
|
86
|
+
"#{device}: #{messages.join(', ')}"
|
87
|
+
end)
|
88
|
+
|
89
|
+
cleanup_after_failure(devices, language, locale, launch_args, return_code)
|
90
|
+
|
91
|
+
# no exception raised... that means we need to retry
|
92
|
+
UI.error "Caught error... #{return_code}"
|
93
|
+
|
94
|
+
self.current_number_of_retries_due_to_failing_simulator += 1
|
95
|
+
if self.current_number_of_retries_due_to_failing_simulator < 20 && return_code != 65
|
96
|
+
# If the return code is not 65, we should assume its a simulator failure and retry
|
97
|
+
launch_simultaneously(devices, language, locale, launch_args)
|
98
|
+
elsif retries < launcher_config.number_of_retries
|
99
|
+
# If there are retries remaining, run the tests again
|
100
|
+
retry_tests(retries, command, language, locale, launch_args, devices)
|
101
|
+
else
|
102
|
+
# It's important to raise an error, as we don't want to collect the screenshots
|
103
|
+
UI.crash!("Too many errors... no more retries...") if launcher_config.stop_after_first_error
|
104
|
+
end
|
105
|
+
end)
|
106
|
+
end
|
107
|
+
|
108
|
+
def cleanup_after_failure(devices, language, locale, launch_args, return_code)
|
109
|
+
copy_screenshots(language: language, locale: locale, launch_args: launch_args)
|
110
|
+
|
111
|
+
UI.important("Tests failed while running on: #{devices.join(', ')}")
|
112
|
+
UI.important("For more detail about the test failures, check the logs here:")
|
113
|
+
UI.important(xcodebuild_log_path(language: language, locale: locale))
|
114
|
+
UI.important(" ")
|
115
|
+
UI.important("You can also find the test result data here:")
|
116
|
+
UI.important(test_results_path)
|
117
|
+
UI.important(" ")
|
118
|
+
UI.important("You can find the incomplete screenshots here:")
|
119
|
+
UI.important(SCREENSHOTS_DIR)
|
120
|
+
UI.important(launcher_config.output_directory)
|
121
|
+
end
|
122
|
+
|
123
|
+
def retry_tests(retries, command, language, locale, launch_args, devices)
|
124
|
+
UI.important("Retrying on devices: #{devices.join(', ')}")
|
125
|
+
UI.important("Number of retries remaining: #{launcher_config.number_of_retries - retries - 1}")
|
126
|
+
execute(retries + 1, command: command, language: language, locale: locale, launch_args: launch_args, devices: devices)
|
127
|
+
end
|
128
|
+
|
129
|
+
def copy_screenshots(language: nil, locale: nil, launch_args: nil)
|
130
|
+
raw_output = File.read(xcodebuild_log_path(language: language, locale: locale))
|
131
|
+
dir_name = locale || language
|
132
|
+
return Collector.fetch_screenshots(raw_output, dir_name, '', launch_args.first)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_results_path
|
136
|
+
derived_data_path = TestCommandGenerator.derived_data_path
|
137
|
+
return File.join(derived_data_path, 'Logs/Test')
|
138
|
+
end
|
139
|
+
|
140
|
+
# This method returns a hash of { device name => [failure messages] }
|
141
|
+
# {
|
142
|
+
# 'iPhone 7': [], # this empty array indicates success
|
143
|
+
# 'iPhone 7 Plus': ["No tests were executed"],
|
144
|
+
# 'iPad Air': ["Launch session expired", "Array out of bounds"]
|
145
|
+
# }
|
146
|
+
def failed_devices
|
147
|
+
test_summaries = Dir["#{test_results_path}/*_TestSummaries.plist"]
|
148
|
+
test_summaries.each_with_object({}) do |plist, hash|
|
149
|
+
summary = FastlaneCore::TestParser.new(plist)
|
150
|
+
name = summary.data.first[:run_destination_name]
|
151
|
+
if summary.data.first[:number_of_tests] == 0
|
152
|
+
hash[name] = ["No tests were executed"]
|
153
|
+
else
|
154
|
+
tests = Array(summary.data.first[:tests])
|
155
|
+
hash[name] = tests.map { |test| test[:failures].map { |failure| failure[:failure_message] } }.flatten
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def xcodebuild_log_path(language: nil, locale: nil)
|
161
|
+
name_components = [Snapshot.project.app_name, Snapshot.config[:scheme]]
|
162
|
+
|
163
|
+
if Snapshot.config[:namespace_log_files]
|
164
|
+
name_components << launcher_config.devices.join('-') if launcher_config.devices.count >= 1
|
165
|
+
name_components << language if language
|
166
|
+
name_components << locale if locale
|
167
|
+
end
|
168
|
+
|
169
|
+
file_name = "#{name_components.join('-')}.log"
|
170
|
+
|
171
|
+
containing = File.expand_path(Snapshot.config[:buildlog_path])
|
172
|
+
FileUtils.mkdir_p(containing)
|
173
|
+
|
174
|
+
return File.join(containing, file_name)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class CPUInspector
|
179
|
+
def self.hwprefs_available?
|
180
|
+
`which hwprefs` != ''
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.cpu_count
|
184
|
+
@cpu_count ||=
|
185
|
+
case RUBY_PLATFORM
|
186
|
+
when /darwin9/
|
187
|
+
`hwprefs cpu_count`.to_i
|
188
|
+
when /darwin10/
|
189
|
+
(hwprefs_available? ? `hwprefs thread_count` : `sysctl -n hw.physicalcpu_max`).to_i
|
190
|
+
when /linux/
|
191
|
+
UI.user_error!("We detected that you are running snapshot on Linux, but snapshot is only supported on macOS")
|
192
|
+
when /freebsd/
|
193
|
+
UI.user_error!("We detected that you are running snapshot on FreeBSD, but snapshot is only supported on macOS")
|
194
|
+
else
|
195
|
+
if RbConfig::CONFIG['host_os'] =~ /darwin/
|
196
|
+
(hwprefs_available? ? `hwprefs thread_count` : `sysctl -n hw.physicalcpu_max`).to_i
|
197
|
+
else
|
198
|
+
UI.crash!("Cannot find the machine's processor count.")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Snapshot
|
2
|
+
class SimulatorLauncherBase
|
3
|
+
attr_accessor :collected_errors
|
4
|
+
|
5
|
+
# The number of times we failed on launching the simulator... sigh
|
6
|
+
attr_accessor :current_number_of_retries_due_to_failing_simulator
|
7
|
+
attr_accessor :launcher_config
|
8
|
+
|
9
|
+
def initialize(launcher_configuration: nil)
|
10
|
+
@launcher_config = launcher_configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def collected_errors
|
14
|
+
@collected_errors ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_number_of_retries_due_to_failing_simulator
|
18
|
+
@current_number_of_retries_due_to_failing_simulator || 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def prepare_for_launch(language, locale, launch_arguments)
|
22
|
+
screenshots_path = TestCommandGenerator.derived_data_path
|
23
|
+
FileUtils.rm_rf(File.join(screenshots_path, "Logs"))
|
24
|
+
FileUtils.rm_rf(screenshots_path) if launcher_config.clean
|
25
|
+
FileUtils.mkdir_p(screenshots_path)
|
26
|
+
|
27
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
28
|
+
FileUtils.mkdir_p(SCREENSHOTS_DIR)
|
29
|
+
|
30
|
+
File.write(File.join(CACHE_DIR, "language.txt"), language)
|
31
|
+
File.write(File.join(CACHE_DIR, "locale.txt"), locale || "")
|
32
|
+
File.write(File.join(CACHE_DIR, "snapshot-launch_arguments.txt"), launch_arguments.last)
|
33
|
+
|
34
|
+
prepare_simulators_for_launch(language: language, locale: locale)
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepare_simulators_for_launch(language: nil, locale: nil)
|
38
|
+
# Kill and shutdown all currently running simulators so that the following settings
|
39
|
+
# changes will be picked up when they are started again.
|
40
|
+
Snapshot.kill_simulator # because of https://github.com/fastlane/snapshot/issues/337
|
41
|
+
`xcrun simctl shutdown booted &> /dev/null`
|
42
|
+
|
43
|
+
Fixes::SimulatorZoomFix.patch
|
44
|
+
Fixes::HardwareKeyboardFix.patch
|
45
|
+
|
46
|
+
devices = launcher_config.devices || []
|
47
|
+
devices.each do |type|
|
48
|
+
if launcher_config.erase_simulator || launcher_config.localize_simulator
|
49
|
+
erase_simulator(type)
|
50
|
+
if launcher_config.localize_simulator
|
51
|
+
localize_simulator(type, language, locale)
|
52
|
+
end
|
53
|
+
elsif launcher_config.reinstall_app
|
54
|
+
# no need to reinstall if device has been erased
|
55
|
+
uninstall_app(type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# pass an array of device types
|
61
|
+
def add_media(device_types, media_type, paths)
|
62
|
+
media_type = media_type.to_s
|
63
|
+
|
64
|
+
device_types.each do |device_type|
|
65
|
+
UI.verbose "Adding #{media_type}s to #{device_type}..."
|
66
|
+
device_udid = TestCommandGenerator.device_udid(device_type)
|
67
|
+
|
68
|
+
UI.message "Launch Simulator #{device_type}"
|
69
|
+
Helper.backticks("xcrun instruments -w #{device_udid} &> /dev/null")
|
70
|
+
|
71
|
+
paths.each do |path|
|
72
|
+
UI.message "Adding '#{path}'"
|
73
|
+
Helper.backticks("xcrun simctl add#{media_type} #{device_udid} #{path.shellescape} &> /dev/null")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def uninstall_app(device_type)
|
79
|
+
UI.verbose "Uninstalling app '#{launcher_config.app_identifier}' from #{device_type}..."
|
80
|
+
launcher_config.app_identifier ||= UI.input("App Identifier: ")
|
81
|
+
device_udid = TestCommandGenerator.device_udid(device_type)
|
82
|
+
|
83
|
+
UI.message "Launch Simulator #{device_type}"
|
84
|
+
Helper.backticks("xcrun instruments -w #{device_udid} &> /dev/null")
|
85
|
+
|
86
|
+
UI.message "Uninstall application #{launcher_config.app_identifier}"
|
87
|
+
Helper.backticks("xcrun simctl uninstall #{device_udid} #{launcher_config.app_identifier} &> /dev/null")
|
88
|
+
end
|
89
|
+
|
90
|
+
def erase_simulator(device_type)
|
91
|
+
UI.verbose("Erasing #{device_type}...")
|
92
|
+
device_udid = TestCommandGenerator.device_udid(device_type)
|
93
|
+
|
94
|
+
UI.important("Erasing #{device_type}...")
|
95
|
+
|
96
|
+
`xcrun simctl erase #{device_udid} &> /dev/null`
|
97
|
+
end
|
98
|
+
|
99
|
+
def localize_simulator(device_type, language, locale)
|
100
|
+
device_udid = TestCommandGenerator.device_udid(device_type)
|
101
|
+
if device_udid
|
102
|
+
locale ||= language.sub("-", "_")
|
103
|
+
plist = {
|
104
|
+
AppleLocale: locale,
|
105
|
+
AppleLanguages: [language]
|
106
|
+
}
|
107
|
+
UI.message "Localizing #{device_type} (AppleLocale=#{locale} AppleLanguages=[#{language}])"
|
108
|
+
plist_path = "#{ENV['HOME']}/Library/Developer/CoreSimulator/Devices/#{device_udid}/data/Library/Preferences/.GlobalPreferences.plist"
|
109
|
+
File.write(plist_path, Plist::Emit.dump(plist))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def copy_simulator_logs(device_names, language, locale, launch_arguments)
|
114
|
+
return unless launcher_config.output_simulator_logs
|
115
|
+
|
116
|
+
detected_language = locale || language
|
117
|
+
language_folder = File.join(launcher_config.output_directory, detected_language)
|
118
|
+
|
119
|
+
device_names.each do |device_name|
|
120
|
+
device = TestCommandGeneratorBase.find_device(device_name)
|
121
|
+
components = [launch_arguments].delete_if { |a| a.to_s.length == 0 }
|
122
|
+
|
123
|
+
UI.header("Collecting system logs #{device_name} - #{language}")
|
124
|
+
log_identity = Digest::MD5.hexdigest(components.join("-"))
|
125
|
+
FastlaneCore::Simulator.copy_logs(device, log_identity, language_folder)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'snapshot/simulator_launchers/simulator_launcher_base'
|
2
|
+
|
3
|
+
module Snapshot
|
4
|
+
class SimulatorLauncherXcode8 < SimulatorLauncherBase
|
5
|
+
def take_screenshots_one_simulator_at_a_time
|
6
|
+
results = {} # collect all the results for a nice table
|
7
|
+
launcher_config.devices.each_with_index do |device, device_index|
|
8
|
+
# launch_args_set always has at at least 1 item (could be "")
|
9
|
+
launcher_config.launch_args_set.each do |launch_args|
|
10
|
+
launcher_config.languages.each_with_index do |language, language_index|
|
11
|
+
locale = nil
|
12
|
+
if language.kind_of?(Array)
|
13
|
+
locale = language[1]
|
14
|
+
language = language[0]
|
15
|
+
end
|
16
|
+
results[device] ||= {}
|
17
|
+
|
18
|
+
current_run = device_index * launcher_config.languages.count + language_index + 1
|
19
|
+
number_of_runs = launcher_config.languages.count * launcher_config.devices.count
|
20
|
+
UI.message("snapshot run #{current_run} of #{number_of_runs}")
|
21
|
+
results[device][language] = run_for_device_and_language(language, locale, device, launch_args)
|
22
|
+
copy_simulator_logs([device], language, locale, launch_args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
# This is its own method so that it can re-try if the tests fail randomly
|
30
|
+
# @return true/false depending on if the tests succeeded
|
31
|
+
def run_for_device_and_language(language, locale, device, launch_arguments, retries = 0)
|
32
|
+
return launch_one_at_a_time(language, locale, device, launch_arguments)
|
33
|
+
rescue => ex
|
34
|
+
UI.error ex.to_s # show the reason for failure to the user, but still maybe retry
|
35
|
+
|
36
|
+
if retries < launcher_config.number_of_retries
|
37
|
+
UI.important "Tests failed, re-trying #{retries + 1} out of #{launcher_config.number_of_retries + 1} times"
|
38
|
+
run_for_device_and_language(language, locale, device, launch_arguments, retries + 1)
|
39
|
+
else
|
40
|
+
UI.error "Backtrace:\n\t#{ex.backtrace.join("\n\t")}" if FastlaneCore::Globals.verbose?
|
41
|
+
self.collected_errors << ex
|
42
|
+
raise ex if launcher_config.stop_after_first_error
|
43
|
+
return false # for the results
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns true if it succeeded
|
48
|
+
def launch_one_at_a_time(language, locale, device_type, launch_arguments)
|
49
|
+
prepare_for_launch(language, locale, launch_arguments)
|
50
|
+
|
51
|
+
add_media([device_type], :photo, launcher_config.add_photos) if launcher_config.add_photos
|
52
|
+
add_media([device_type], :video, launcher_config.add_videos) if launcher_config.add_videos
|
53
|
+
|
54
|
+
open_simulator_for_device(device_type)
|
55
|
+
|
56
|
+
command = TestCommandGeneratorXcode8.generate(device_type: device_type, language: language, locale: locale)
|
57
|
+
|
58
|
+
if locale
|
59
|
+
UI.header("#{device_type} - #{language} (#{locale})")
|
60
|
+
else
|
61
|
+
UI.header("#{device_type} - #{language}")
|
62
|
+
end
|
63
|
+
|
64
|
+
execute(command: command, language: language, locale: locale, device_type: device_type, launch_args: launch_arguments)
|
65
|
+
|
66
|
+
raw_output = File.read(TestCommandGeneratorXcode8.xcodebuild_log_path(device_type: device_type, language: language, locale: locale))
|
67
|
+
|
68
|
+
dir_name = locale || language
|
69
|
+
|
70
|
+
return Collector.fetch_screenshots(raw_output, dir_name, device_type, launch_arguments.first)
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute(command: nil, language: nil, locale: nil, device_type: nil, launch_args: nil)
|
74
|
+
prefix_hash = [
|
75
|
+
{
|
76
|
+
prefix: "Running Tests: ",
|
77
|
+
block: proc do |value|
|
78
|
+
value.include?("Touching")
|
79
|
+
end
|
80
|
+
}
|
81
|
+
]
|
82
|
+
FastlaneCore::CommandExecutor.execute(command: command,
|
83
|
+
print_all: true,
|
84
|
+
print_command: true,
|
85
|
+
prefix: prefix_hash,
|
86
|
+
loading: "Loading...",
|
87
|
+
error: proc do |output, return_code|
|
88
|
+
ErrorHandler.handle_test_error(output, return_code)
|
89
|
+
|
90
|
+
# no exception raised... that means we need to retry
|
91
|
+
UI.error "Caught error... #{return_code}"
|
92
|
+
|
93
|
+
self.current_number_of_retries_due_to_failing_simulator += 1
|
94
|
+
if self.current_number_of_retries_due_to_failing_simulator < 20
|
95
|
+
launch_one_at_a_time(language, locale, device_type, launch_arguments)
|
96
|
+
else
|
97
|
+
# It's important to raise an error, as we don't want to collect the screenshots
|
98
|
+
UI.crash!("Too many errors... no more retries...")
|
99
|
+
end
|
100
|
+
end)
|
101
|
+
end
|
102
|
+
|
103
|
+
def open_simulator_for_device(device_name)
|
104
|
+
return unless FastlaneCore::Env.truthy?('FASTLANE_EXPLICIT_OPEN_SIMULATOR')
|
105
|
+
|
106
|
+
device = TestCommandGeneratorBase.find_device(device_name)
|
107
|
+
FastlaneCore::Simulator.launch(device) if device
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|