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
@@ -0,0 +1,173 @@
|
|
1
|
+
//
|
2
|
+
// SnapshotHelperXcode8.swift
|
3
|
+
// Example
|
4
|
+
//
|
5
|
+
// Created by Felix Krause on 10/8/15.
|
6
|
+
// Copyright © 2015 Felix Krause. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
// -----------------------------------------------------
|
10
|
+
// IMPORTANT: When modifying this file, make sure to
|
11
|
+
// increment the version number at the very
|
12
|
+
// bottom of the file to notify users about
|
13
|
+
// the new SnapshotHelperXcode8.swift
|
14
|
+
// -----------------------------------------------------
|
15
|
+
|
16
|
+
import Foundation
|
17
|
+
import XCTest
|
18
|
+
|
19
|
+
var deviceLanguage = ""
|
20
|
+
var locale = ""
|
21
|
+
|
22
|
+
@available(*, deprecated, message: "use setupSnapshot: instead")
|
23
|
+
func setLanguage(_ app: XCUIApplication) {
|
24
|
+
setupSnapshot(app)
|
25
|
+
}
|
26
|
+
|
27
|
+
func setupSnapshot(_ app: XCUIApplication) {
|
28
|
+
Snapshot.setupSnapshot(app)
|
29
|
+
}
|
30
|
+
|
31
|
+
func snapshot(_ name: String, waitForLoadingIndicator: Bool = true) {
|
32
|
+
Snapshot.snapshot(name, waitForLoadingIndicator: waitForLoadingIndicator)
|
33
|
+
}
|
34
|
+
|
35
|
+
open class Snapshot: NSObject {
|
36
|
+
|
37
|
+
open class func setupSnapshot(_ app: XCUIApplication) {
|
38
|
+
setLanguage(app)
|
39
|
+
setLocale(app)
|
40
|
+
setLaunchArguments(app)
|
41
|
+
}
|
42
|
+
|
43
|
+
class func setLanguage(_ app: XCUIApplication) {
|
44
|
+
guard let prefix = pathPrefix() else {
|
45
|
+
return
|
46
|
+
}
|
47
|
+
|
48
|
+
let path = prefix.appendingPathComponent("language.txt")
|
49
|
+
|
50
|
+
do {
|
51
|
+
let trimCharacterSet = CharacterSet.whitespacesAndNewlines
|
52
|
+
deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
|
53
|
+
app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"]
|
54
|
+
} catch {
|
55
|
+
print("Couldn't detect/set language...")
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
class func setLocale(_ app: XCUIApplication) {
|
60
|
+
guard let prefix = pathPrefix() else {
|
61
|
+
return
|
62
|
+
}
|
63
|
+
|
64
|
+
let path = prefix.appendingPathComponent("locale.txt")
|
65
|
+
|
66
|
+
do {
|
67
|
+
let trimCharacterSet = CharacterSet.whitespacesAndNewlines
|
68
|
+
locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet)
|
69
|
+
} catch {
|
70
|
+
print("Couldn't detect/set locale...")
|
71
|
+
}
|
72
|
+
if locale.isEmpty {
|
73
|
+
locale = Locale(identifier: deviceLanguage).identifier
|
74
|
+
}
|
75
|
+
app.launchArguments += ["-AppleLocale", "\"\(locale)\""]
|
76
|
+
}
|
77
|
+
|
78
|
+
class func setLaunchArguments(_ app: XCUIApplication) {
|
79
|
+
guard let prefix = pathPrefix() else {
|
80
|
+
return
|
81
|
+
}
|
82
|
+
|
83
|
+
let path = prefix.appendingPathComponent("snapshot-launch_arguments.txt")
|
84
|
+
app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"]
|
85
|
+
|
86
|
+
do {
|
87
|
+
let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8)
|
88
|
+
let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: [])
|
89
|
+
let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location:0, length:launchArguments.characters.count))
|
90
|
+
let results = matches.map { result -> String in
|
91
|
+
(launchArguments as NSString).substring(with: result.range)
|
92
|
+
}
|
93
|
+
app.launchArguments += results
|
94
|
+
} catch {
|
95
|
+
print("Couldn't detect/set launch_arguments...")
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
open class func snapshot(_ name: String, waitForLoadingIndicator: Bool = true) {
|
100
|
+
if waitForLoadingIndicator {
|
101
|
+
waitForLoadingIndicatorToDisappear()
|
102
|
+
}
|
103
|
+
|
104
|
+
print("snapshot: \(name)") // more information about this, check out https://github.com/fastlane/fastlane/tree/master/snapshot#how-does-it-work
|
105
|
+
|
106
|
+
sleep(1) // Waiting for the animation to be finished (kind of)
|
107
|
+
|
108
|
+
#if os(tvOS)
|
109
|
+
XCUIApplication().childrenMatchingType(.Browser).count
|
110
|
+
#elseif os(OSX)
|
111
|
+
XCUIApplication().typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: [])
|
112
|
+
#else
|
113
|
+
XCUIDevice.shared().orientation = .unknown
|
114
|
+
#endif
|
115
|
+
}
|
116
|
+
|
117
|
+
class func waitForLoadingIndicatorToDisappear() {
|
118
|
+
#if os(tvOS)
|
119
|
+
return
|
120
|
+
#endif
|
121
|
+
|
122
|
+
let query = XCUIApplication().statusBars.children(matching: .other).element(boundBy: 1).children(matching: .other)
|
123
|
+
|
124
|
+
while (0..<query.count).map({ query.element(boundBy: $0) }).contains(where: { $0.isLoadingIndicator }) {
|
125
|
+
sleep(1)
|
126
|
+
print("Waiting for loading indicator to disappear...")
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
class func pathPrefix() -> URL? {
|
131
|
+
let homeDir: URL
|
132
|
+
// on OSX config is stored in /Users/<username>/Library
|
133
|
+
// and on iOS/tvOS/WatchOS it's in simulator's home dir
|
134
|
+
#if os(OSX)
|
135
|
+
guard let user = ProcessInfo().environment["USER"] else {
|
136
|
+
print("Couldn't find Snapshot configuration files - can't detect current user ")
|
137
|
+
return nil
|
138
|
+
}
|
139
|
+
|
140
|
+
guard let usersDir = FileManager.default.urls(for: .userDirectory, in: .localDomainMask).first else {
|
141
|
+
print("Couldn't find Snapshot configuration files - can't detect `Users` dir")
|
142
|
+
return nil
|
143
|
+
}
|
144
|
+
|
145
|
+
homeDir = usersDir.appendingPathComponent(user)
|
146
|
+
#else
|
147
|
+
guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else {
|
148
|
+
print("Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable.")
|
149
|
+
return nil
|
150
|
+
}
|
151
|
+
guard let homeDirUrl = URL(string: simulatorHostHome) else {
|
152
|
+
print("Can't prepare environment. Simulator home location is inaccessible. Does \(simulatorHostHome) exist?")
|
153
|
+
return nil
|
154
|
+
}
|
155
|
+
homeDir = URL(fileURLWithPath: homeDirUrl.path)
|
156
|
+
#endif
|
157
|
+
return homeDir.appendingPathComponent("Library/Caches/tools.fastlane")
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
extension XCUIElement {
|
162
|
+
var isLoadingIndicator: Bool {
|
163
|
+
let whiteListedLoaders = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"]
|
164
|
+
if whiteListedLoaders.contains(self.identifier) {
|
165
|
+
return false
|
166
|
+
}
|
167
|
+
return self.frame.size == CGSize(width: 10, height: 20)
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
// Please don't remove the lines below
|
172
|
+
// They are used to detect outdated configuration files
|
173
|
+
// SnapshotHelperXcode8Version [1.4]
|
data/snapshot/lib/snapshot.rb
CHANGED
@@ -6,12 +6,16 @@ require 'snapshot/screenshot_rotate'
|
|
6
6
|
require 'snapshot/dependency_checker'
|
7
7
|
require 'snapshot/latest_os_version'
|
8
8
|
require 'snapshot/test_command_generator'
|
9
|
+
require 'snapshot/test_command_generator_xcode_8'
|
9
10
|
require 'snapshot/error_handler'
|
10
11
|
require 'snapshot/collector'
|
11
12
|
require 'snapshot/options'
|
12
13
|
require 'snapshot/update'
|
13
14
|
require 'snapshot/fixes/simulator_zoom_fix'
|
14
15
|
require 'snapshot/fixes/hardware_keyboard_fix'
|
16
|
+
require 'snapshot/simulator_launchers/launcher_configuration'
|
17
|
+
require 'snapshot/simulator_launchers/simulator_launcher'
|
18
|
+
require 'snapshot/simulator_launchers/simulator_launcher_xcode_8'
|
15
19
|
|
16
20
|
require 'fastlane_core'
|
17
21
|
|
@@ -46,6 +50,8 @@ module Snapshot
|
|
46
50
|
UI = FastlaneCore::UI
|
47
51
|
ROOT = Pathname.new(File.expand_path('../..', __FILE__))
|
48
52
|
DESCRIPTION = "Automate taking localized screenshots of your iOS and tvOS apps on every device"
|
53
|
+
CACHE_DIR = File.join(Dir.home, "Library/Caches/tools.fastlane")
|
54
|
+
SCREENSHOTS_DIR = File.join(CACHE_DIR, 'screenshots')
|
49
55
|
|
50
56
|
Snapshot::DependencyChecker.check_dependencies
|
51
57
|
|
@@ -7,8 +7,18 @@ module Snapshot
|
|
7
7
|
containing = File.join(TestCommandGenerator.derived_data_path, "Logs", "Test")
|
8
8
|
attachments_path = File.join(containing, "Attachments")
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
language_folder = File.join(Snapshot.config[:output_directory], dir_name)
|
11
|
+
FileUtils.mkdir_p(language_folder)
|
12
|
+
|
13
|
+
# Xcode 9 introduced a new API to take screenshots which allows us
|
14
|
+
# to avoid parsing the generated plist file to find the screenshots
|
15
|
+
# and instead, we can save them to a known location to use later on.
|
16
|
+
if Helper.xcode_at_least?(9)
|
17
|
+
return collect_screenshots_for_language_folder(language_folder)
|
18
|
+
else
|
19
|
+
to_store = attachments(containing)
|
20
|
+
matches = output.scan(/snapshot: (.*)/)
|
21
|
+
end
|
12
22
|
|
13
23
|
if to_store.count == 0 && matches.count == 0
|
14
24
|
return false
|
@@ -22,25 +32,41 @@ module Snapshot
|
|
22
32
|
name = current[0]
|
23
33
|
filename = to_store[index]
|
24
34
|
|
25
|
-
language_folder = File.join(Snapshot.config[:output_directory], dir_name)
|
26
|
-
FileUtils.mkdir_p(language_folder)
|
27
|
-
|
28
35
|
device_name = device_type.delete(" ")
|
36
|
+
|
29
37
|
components = [launch_arguments_index].delete_if { |a| a.to_s.length == 0 }
|
30
38
|
screenshot_name = device_name + "-" + name + "-" + Digest::MD5.hexdigest(components.join("-")) + ".png"
|
31
39
|
output_path = File.join(language_folder, screenshot_name)
|
40
|
+
|
32
41
|
from_path = File.join(attachments_path, filename)
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
UI.success "Copying '#{output_path}'..."
|
37
|
-
end
|
38
|
-
FileUtils.cp(from_path, output_path)
|
42
|
+
|
43
|
+
copy(from_path, output_path)
|
39
44
|
end
|
45
|
+
return true
|
46
|
+
end
|
40
47
|
|
48
|
+
# Returns true if it succeeds
|
49
|
+
def self.collect_screenshots_for_language_folder(destination)
|
50
|
+
screenshots = Dir["#{SCREENSHOTS_DIR}/*.png"]
|
51
|
+
return false if screenshots.empty?
|
52
|
+
screenshots.each do |screenshot|
|
53
|
+
filename = File.basename(screenshot)
|
54
|
+
to_path = File.join(destination, filename)
|
55
|
+
copy(screenshot, to_path)
|
56
|
+
end
|
57
|
+
FileUtils.rm_rf(SCREENSHOTS_DIR)
|
41
58
|
return true
|
42
59
|
end
|
43
60
|
|
61
|
+
def self.copy(from_path, to_path)
|
62
|
+
if FastlaneCore::Globals.verbose?
|
63
|
+
UI.success "Copying file '#{from_path}' to '#{to_path}'..."
|
64
|
+
else
|
65
|
+
UI.success "Copying '#{to_path}'..."
|
66
|
+
end
|
67
|
+
FileUtils.cp(from_path, to_path)
|
68
|
+
end
|
69
|
+
|
44
70
|
def self.attachments(containing)
|
45
71
|
UI.message "Collecting screenshots..."
|
46
72
|
plist_path = Dir[File.join(containing, "*.plist")].last # we clean the folder before each run
|
@@ -41,6 +41,9 @@ module Snapshot
|
|
41
41
|
# In Xcode 8, we only need iPad Pro 9.7 inch, not the iPad Air
|
42
42
|
next if all_simulators.any? { |a| a.name.include?("9.7-inch") } && sim.name.include?("iPad Air")
|
43
43
|
|
44
|
+
# In Xcode 9, we only need one iPad Pro (12.9-inch)
|
45
|
+
next if sim.name.include?('iPad Pro (12.9-inch) (2nd generation)')
|
46
|
+
|
44
47
|
# Filter iPhones
|
45
48
|
# Full list: ["iPhone 4s", "iPhone 5", "iPhone 5s", "iPhone 6", "iPhone 6 Plus", "iPhone 6s", "iPhone 6s Plus"]
|
46
49
|
next if sim.name.include?("5s") # same screen resolution as iPhone 5
|
@@ -52,7 +55,7 @@ module Snapshot
|
|
52
55
|
config[:devices] << sim.name
|
53
56
|
end
|
54
57
|
elsif Snapshot.project.mac?
|
55
|
-
config[:devices]
|
58
|
+
config[:devices] = ["Mac"]
|
56
59
|
end
|
57
60
|
end
|
58
61
|
end
|
@@ -7,7 +7,7 @@ module Snapshot
|
|
7
7
|
|
8
8
|
class SimulatorZoomFix
|
9
9
|
def self.patch
|
10
|
-
UI.message "Patching '#{config_path}' to scale
|
10
|
+
UI.message "Patching all simulators '#{config_path}' to scale to 100%"
|
11
11
|
|
12
12
|
FastlaneCore::DeviceManager.simulators.each do |simulator|
|
13
13
|
simulator_name = simulator.name.tr("\s", "-")
|
@@ -181,6 +181,11 @@ module Snapshot
|
|
181
181
|
env_name: "SNAPSHOT_NAMESPACE_LOG_FILES",
|
182
182
|
description: "Separate the log files per device and per language",
|
183
183
|
optional: true,
|
184
|
+
is_string: false),
|
185
|
+
FastlaneCore::ConfigItem.new(key: :concurrent_simulators,
|
186
|
+
env_name: "SNAPSHOT_EXECUTE_CONCURRENT_SIMULATORS",
|
187
|
+
description: "Take snapshots on multiple simulators concurrently. Note: This option is only applicable when running against Xcode 9",
|
188
|
+
default_value: true,
|
184
189
|
is_string: false)
|
185
190
|
]
|
186
191
|
end
|
@@ -15,7 +15,6 @@ module Snapshot
|
|
15
15
|
Dir[File.join(language_folder, '*.png')].sort.each do |screenshot|
|
16
16
|
available_devices.each do |key_name, output_name|
|
17
17
|
next unless File.basename(screenshot).include?(key_name)
|
18
|
-
|
19
18
|
# This screenshot is from this device
|
20
19
|
@data[language] ||= {}
|
21
20
|
@data[language][output_name] ||= []
|
@@ -38,9 +37,9 @@ module Snapshot
|
|
38
37
|
system("open '#{export_path}'") unless Snapshot.config[:skip_open_summary]
|
39
38
|
end
|
40
39
|
|
41
|
-
def
|
40
|
+
def xcode_8_and_below_device_name_mappings
|
42
41
|
# The order IS important, since those names are used to check for include?
|
43
|
-
# and the iPhone 6 is
|
42
|
+
# and the iPhone 6 is included in the iPhone 6 Plus
|
44
43
|
{
|
45
44
|
'AppleTV1080p' => 'Apple TV',
|
46
45
|
'iPhone7Plus' => "iPhone7Plus (5.5-Inch)",
|
@@ -63,5 +62,37 @@ module Snapshot
|
|
63
62
|
'Mac' => "Mac"
|
64
63
|
}
|
65
64
|
end
|
65
|
+
|
66
|
+
def xcode_9_and_above_device_name_mappings
|
67
|
+
{
|
68
|
+
# snapshot in Xcode 9 saves screenshots with the SIMULATOR_DEVICE_NAME
|
69
|
+
# which includes spaces
|
70
|
+
'iPhone 7 Plus' => "iPhone 7 Plus (5.5-Inch)",
|
71
|
+
'iPhone 7' => "iPhone 7 (4.7-Inch)",
|
72
|
+
'iPhone 6s Plus' => "iPhone 6s Plus (5.5-Inch)",
|
73
|
+
'iPhone 6 Plus' => "iPhone 6 Plus (5.5-Inch)",
|
74
|
+
'iPhone 6s' => "iPhone 6s (4.7-Inch)",
|
75
|
+
'iPhone 6' => "iPhone 6 (4.7-Inch)",
|
76
|
+
'iPhone 5s' => "iPhone 5 (4-Inch)",
|
77
|
+
'iPhone SE' => "iPhone SE",
|
78
|
+
'iPad Air' => 'iPad Air',
|
79
|
+
'iPad Air 2' => 'iPad Air 2',
|
80
|
+
'iPad (5th generation)' => 'iPad (5th generation)',
|
81
|
+
'iPad Pro (9.7-inch)' => 'iPad Pro (9.7-inch)',
|
82
|
+
'iPad Pro (10.5-inch)' => 'iPad Pro (10.5-inch)',
|
83
|
+
'iPad Pro (12.9-inch) (2nd generation)' => 'iPad Pro (12.9-inch) (2nd generation)',
|
84
|
+
'iPad Pro (12.9-inch)' => 'iPad Pro (12.9-inch)',
|
85
|
+
'Apple TV 1080p' => 'Apple TV',
|
86
|
+
'Mac' => 'Mac'
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def available_devices
|
91
|
+
if Helper.xcode_at_least?("9.0")
|
92
|
+
return xcode_9_and_above_device_name_mappings
|
93
|
+
else
|
94
|
+
return xcode_8_and_below_device_name_mappings
|
95
|
+
end
|
96
|
+
end
|
66
97
|
end
|
67
98
|
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'shellwords'
|
2
2
|
require 'plist'
|
3
|
+
require 'os'
|
4
|
+
require 'thread'
|
3
5
|
|
4
6
|
module Snapshot
|
5
7
|
class Runner
|
6
|
-
# The number of times we failed on launching the simulator... sigh
|
7
|
-
attr_accessor :number_of_retries_due_to_failing_simulator
|
8
|
-
|
9
|
-
# All the errors we experience while running snapshot
|
10
|
-
attr_accessor :collected_errors
|
11
|
-
|
12
8
|
def work
|
13
9
|
if File.exist?("./fastlane/snapshot.js") or File.exist?("./snapshot.js")
|
14
10
|
UI.error "Found old snapshot configuration file 'snapshot.js'"
|
@@ -32,85 +28,30 @@ module Snapshot
|
|
32
28
|
|
33
29
|
UI.success "Building and running project - this might take some time..."
|
34
30
|
|
35
|
-
|
36
|
-
self.collected_errors = []
|
37
|
-
results = {} # collect all the results for a nice table
|
38
|
-
launch_arguments_set = config_launch_arguments
|
39
|
-
Snapshot.config[:devices].each_with_index do |device, device_index|
|
40
|
-
launch_arguments_set.each do |launch_arguments|
|
41
|
-
Snapshot.config[:languages].each_with_index do |language, language_index|
|
42
|
-
locale = nil
|
43
|
-
if language.kind_of?(Array)
|
44
|
-
locale = language[1]
|
45
|
-
language = language[0]
|
46
|
-
end
|
47
|
-
results[device] ||= {}
|
48
|
-
|
49
|
-
current_run = device_index * Snapshot.config[:languages].count + language_index + 1
|
50
|
-
number_of_runs = Snapshot.config[:languages].count * Snapshot.config[:devices].count
|
51
|
-
UI.message("snapshot run #{current_run} of #{number_of_runs}")
|
31
|
+
launcher_config = SimulatorLauncherConfiguration.new(snapshot_config: Snapshot.config)
|
52
32
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
33
|
+
if Helper.xcode_at_least?(9)
|
34
|
+
launcher = SimulatorLauncher.new(launcher_configuration: launcher_config)
|
35
|
+
results = launcher.take_screenshots_simultaneously
|
36
|
+
else
|
37
|
+
launcher = SimulatorLauncherXcode8.new(launcher_configuration: launcher_config)
|
38
|
+
results = launcher.take_screenshots_one_simulator_at_a_time
|
58
39
|
end
|
59
40
|
|
60
41
|
print_results(results)
|
61
42
|
|
62
|
-
UI.test_failure!(
|
43
|
+
UI.test_failure!(launcher.collected_errors.uniq.join('; ')) if launcher.collected_errors.count > 0
|
63
44
|
|
64
45
|
# Generate HTML report
|
65
46
|
ReportsGenerator.new.generate
|
66
47
|
|
67
48
|
# Clear the Derived Data
|
68
49
|
unless Snapshot.config[:derived_data_path]
|
69
|
-
|
50
|
+
# this should actually be launcher.derived_data_path
|
51
|
+
FileUtils.rm_rf(TestCommandGeneratorBase.derived_data_path)
|
70
52
|
end
|
71
53
|
end
|
72
54
|
|
73
|
-
# This is its own method so that it can re-try if the tests fail randomly
|
74
|
-
# @return true/false depending on if the tests succeeded
|
75
|
-
def run_for_device_and_language(language, locale, device, launch_arguments, retries = 0)
|
76
|
-
return launch(language, locale, device, launch_arguments)
|
77
|
-
rescue => ex
|
78
|
-
UI.error ex.to_s # show the reason for failure to the user, but still maybe retry
|
79
|
-
|
80
|
-
if retries < Snapshot.config[:number_of_retries]
|
81
|
-
UI.important "Tests failed, re-trying #{retries + 1} out of #{Snapshot.config[:number_of_retries] + 1} times"
|
82
|
-
run_for_device_and_language(language, locale, device, launch_arguments, retries + 1)
|
83
|
-
else
|
84
|
-
UI.error "Backtrace:\n\t#{ex.backtrace.join("\n\t")}" if FastlaneCore::Globals.verbose?
|
85
|
-
self.collected_errors << ex
|
86
|
-
raise ex if Snapshot.config[:stop_after_first_error]
|
87
|
-
return false # for the results
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def config_launch_arguments
|
92
|
-
launch_arguments = Array(Snapshot.config[:launch_arguments])
|
93
|
-
# if more than 1 set of arguments, use a tuple with an index
|
94
|
-
if launch_arguments.count == 1
|
95
|
-
[launch_arguments]
|
96
|
-
else
|
97
|
-
launch_arguments.map.with_index { |e, i| [i, e] }
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def copy_simulator_logs(device_name, language, locale, launch_arguments)
|
102
|
-
return unless Snapshot.config[:output_simulator_logs]
|
103
|
-
|
104
|
-
detected_language = locale || language
|
105
|
-
language_folder = File.join(Snapshot.config[:output_directory], detected_language)
|
106
|
-
device = TestCommandGenerator.find_device(device_name)
|
107
|
-
components = [launch_arguments].delete_if { |a| a.to_s.length == 0 }
|
108
|
-
|
109
|
-
UI.header("Collecting system logs #{device_name} - #{language}")
|
110
|
-
log_identity = Digest::MD5.hexdigest(components.join("-"))
|
111
|
-
FastlaneCore::Simulator.copy_logs(device, log_identity, language_folder)
|
112
|
-
end
|
113
|
-
|
114
55
|
def print_results(results)
|
115
56
|
return if results.count == 0
|
116
57
|
|
@@ -133,145 +74,6 @@ module Snapshot
|
|
133
74
|
puts ""
|
134
75
|
end
|
135
76
|
|
136
|
-
# Returns true if it succeeded
|
137
|
-
def launch(language, locale, device_type, launch_arguments)
|
138
|
-
screenshots_path = TestCommandGenerator.derived_data_path
|
139
|
-
FileUtils.rm_rf(File.join(screenshots_path, "Logs"))
|
140
|
-
FileUtils.rm_rf(screenshots_path) if Snapshot.config[:clean]
|
141
|
-
FileUtils.mkdir_p(screenshots_path)
|
142
|
-
|
143
|
-
prefix = File.join(Dir.home, "Library/Caches/tools.fastlane")
|
144
|
-
FileUtils.mkdir_p(prefix)
|
145
|
-
File.write(File.join(prefix, "language.txt"), language)
|
146
|
-
File.write(File.join(prefix, "locale.txt"), locale || "")
|
147
|
-
File.write(File.join(prefix, "snapshot-launch_arguments.txt"), launch_arguments.last)
|
148
|
-
|
149
|
-
unless device_type == "Mac"
|
150
|
-
# Kill and shutdown all currently running simulators so that the following settings
|
151
|
-
# changes will be picked up when they are started again.
|
152
|
-
Snapshot.kill_simulator # because of https://github.com/fastlane/snapshot/issues/337
|
153
|
-
`xcrun simctl shutdown booted &> /dev/null`
|
154
|
-
|
155
|
-
Fixes::SimulatorZoomFix.patch
|
156
|
-
Fixes::HardwareKeyboardFix.patch
|
157
|
-
|
158
|
-
if Snapshot.config[:erase_simulator] || Snapshot.config[:localize_simulator]
|
159
|
-
erase_simulator(device_type)
|
160
|
-
if Snapshot.config[:localize_simulator]
|
161
|
-
localize_simulator(device_type, language, locale)
|
162
|
-
end
|
163
|
-
elsif Snapshot.config[:reinstall_app]
|
164
|
-
# no need to reinstall if device has been erased
|
165
|
-
uninstall_app(device_type)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
add_media(device_type, :photo, Snapshot.config[:add_photos]) if Snapshot.config[:add_photos]
|
170
|
-
add_media(device_type, :video, Snapshot.config[:add_videos]) if Snapshot.config[:add_videos]
|
171
|
-
|
172
|
-
open_simulator_for_device(device_type)
|
173
|
-
|
174
|
-
command = TestCommandGenerator.generate(device_type: device_type, language: language, locale: locale)
|
175
|
-
|
176
|
-
if locale
|
177
|
-
UI.header("#{device_type} - #{language} (#{locale})")
|
178
|
-
else
|
179
|
-
UI.header("#{device_type} - #{language}")
|
180
|
-
end
|
181
|
-
|
182
|
-
prefix_hash = [
|
183
|
-
{
|
184
|
-
prefix: "Running Tests: ",
|
185
|
-
block: proc do |value|
|
186
|
-
value.include?("Touching")
|
187
|
-
end
|
188
|
-
}
|
189
|
-
]
|
190
|
-
|
191
|
-
FastlaneCore::CommandExecutor.execute(command: command,
|
192
|
-
print_all: true,
|
193
|
-
print_command: true,
|
194
|
-
prefix: prefix_hash,
|
195
|
-
loading: "Loading...",
|
196
|
-
error: proc do |output, return_code|
|
197
|
-
ErrorHandler.handle_test_error(output, return_code)
|
198
|
-
|
199
|
-
# no exception raised... that means we need to retry
|
200
|
-
UI.error "Caught error... #{return_code}"
|
201
|
-
|
202
|
-
self.number_of_retries_due_to_failing_simulator += 1
|
203
|
-
if self.number_of_retries_due_to_failing_simulator < 20
|
204
|
-
launch(language, locale, device_type, launch_arguments)
|
205
|
-
else
|
206
|
-
# It's important to raise an error, as we don't want to collect the screenshots
|
207
|
-
UI.crash!("Too many errors... no more retries...")
|
208
|
-
end
|
209
|
-
end)
|
210
|
-
|
211
|
-
raw_output = File.read(TestCommandGenerator.xcodebuild_log_path(device_type: device_type, language: language, locale: locale))
|
212
|
-
|
213
|
-
dir_name = locale || language
|
214
|
-
|
215
|
-
return Collector.fetch_screenshots(raw_output, dir_name, device_type, launch_arguments.first)
|
216
|
-
end
|
217
|
-
|
218
|
-
def open_simulator_for_device(device_name)
|
219
|
-
return unless FastlaneCore::Env.truthy?('FASTLANE_EXPLICIT_OPEN_SIMULATOR')
|
220
|
-
|
221
|
-
device = TestCommandGenerator.find_device(device_name)
|
222
|
-
FastlaneCore::Simulator.launch(device) if device
|
223
|
-
end
|
224
|
-
|
225
|
-
def uninstall_app(device_type)
|
226
|
-
UI.verbose "Uninstalling app '#{Snapshot.config[:app_identifier]}' from #{device_type}..."
|
227
|
-
Snapshot.config[:app_identifier] ||= UI.input("App Identifier: ")
|
228
|
-
device_udid = TestCommandGenerator.device_udid(device_type)
|
229
|
-
|
230
|
-
UI.message "Launch Simulator #{device_type}"
|
231
|
-
Helper.backticks("xcrun instruments -w #{device_udid} &> /dev/null")
|
232
|
-
|
233
|
-
UI.message "Uninstall application #{Snapshot.config[:app_identifier]}"
|
234
|
-
Helper.backticks("xcrun simctl uninstall #{device_udid} #{Snapshot.config[:app_identifier]} &> /dev/null")
|
235
|
-
end
|
236
|
-
|
237
|
-
def erase_simulator(device_type)
|
238
|
-
UI.verbose("Erasing #{device_type}...")
|
239
|
-
device_udid = TestCommandGenerator.device_udid(device_type)
|
240
|
-
|
241
|
-
UI.important("Erasing #{device_type}...")
|
242
|
-
|
243
|
-
`xcrun simctl erase #{device_udid} &> /dev/null`
|
244
|
-
end
|
245
|
-
|
246
|
-
def localize_simulator(device_type, language, locale)
|
247
|
-
device_udid = TestCommandGenerator.device_udid(device_type)
|
248
|
-
if device_udid
|
249
|
-
locale ||= language.sub("-", "_")
|
250
|
-
plist = {
|
251
|
-
AppleLocale: locale,
|
252
|
-
AppleLanguages: [language]
|
253
|
-
}
|
254
|
-
UI.message "Localizing #{device_type} (AppleLocale=#{locale} AppleLanguages=[#{language}])"
|
255
|
-
plist_path = "#{ENV['HOME']}/Library/Developer/CoreSimulator/Devices/#{device_udid}/data/Library/Preferences/.GlobalPreferences.plist"
|
256
|
-
File.write(plist_path, Plist::Emit.dump(plist))
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def add_media(device_type, media_type, paths)
|
261
|
-
media_type = media_type.to_s
|
262
|
-
|
263
|
-
UI.verbose "Adding #{media_type}s to #{device_type}..."
|
264
|
-
device_udid = TestCommandGenerator.device_udid(device_type)
|
265
|
-
|
266
|
-
UI.message "Launch Simulator #{device_type}"
|
267
|
-
Helper.backticks("xcrun instruments -w #{device_udid} &> /dev/null")
|
268
|
-
|
269
|
-
paths.each do |path|
|
270
|
-
UI.message "Adding '#{path}'"
|
271
|
-
Helper.backticks("xcrun simctl add#{media_type} #{device_udid} #{path.shellescape} &> /dev/null")
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
77
|
def clear_previous_screenshots
|
276
78
|
UI.important "Clearing previously generated screenshots"
|
277
79
|
path = File.join(Snapshot.config[:output_directory], "*", "*.png")
|
@@ -281,13 +83,26 @@ module Snapshot
|
|
281
83
|
end
|
282
84
|
end
|
283
85
|
|
284
|
-
|
86
|
+
# Depending on the Xcode version, the return value is different
|
87
|
+
def self.path_to_helper_file_from_gem
|
285
88
|
runner_dir = File.dirname(__FILE__)
|
286
|
-
bundled_helper = File.read File.expand_path('../assets/SnapshotHelper.swift', runner_dir)
|
287
|
-
current_version = bundled_helper.match(/\n.*SnapshotHelperVersion \[.+\]/)[0]
|
288
89
|
|
289
|
-
|
290
|
-
|
90
|
+
if Helper.xcode_at_least?("9.0")
|
91
|
+
return File.expand_path('../assets/SnapshotHelper.swift', runner_dir)
|
92
|
+
else
|
93
|
+
return File.expand_path('../assets/SnapshotHelperXcode8.swift', runner_dir)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def version_of_bundled_helper
|
98
|
+
asset_path = self.class.path_to_helper_file_from_gem
|
99
|
+
regex_to_use = Helper.xcode_at_least?("9.0") ? /\n.*SnapshotHelperVersion \[.+\]/ : /\n.*SnapshotHelperXcode8Version \[.+\]/
|
100
|
+
|
101
|
+
bundled_helper = File.read(asset_path)
|
102
|
+
current_version = bundled_helper.match(regex_to_use)[0]
|
103
|
+
|
104
|
+
# Something like "// SnapshotHelperVersion [1.2]", but be relaxed about whitespace
|
105
|
+
return current_version.gsub(%r{^//\w*}, '').strip
|
291
106
|
end
|
292
107
|
|
293
108
|
# rubocop:disable Style/Next
|