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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/deliver/lib/deliver/loader.rb +29 -3
  4. data/deliver/lib/deliver/upload_metadata.rb +18 -0
  5. data/fastlane/lib/fastlane/actions/opt_out_crash_reporting.rb +1 -1
  6. data/fastlane/lib/fastlane/version.rb +1 -1
  7. data/fastlane_core/lib/fastlane_core.rb +1 -0
  8. data/fastlane_core/lib/fastlane_core/crash_reporter/crash_reporter.rb +1 -1
  9. data/fastlane_core/lib/fastlane_core/device_manager.rb +12 -12
  10. data/fastlane_core/lib/fastlane_core/test_parser.rb +145 -0
  11. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
  12. data/pilot/lib/pilot/tester_manager.rb +8 -1
  13. data/sigh/lib/sigh/runner.rb +27 -18
  14. data/snapshot/README.md +3 -1
  15. data/snapshot/lib/assets/SnapshotHelper.swift +57 -35
  16. data/snapshot/lib/assets/SnapshotHelperXcode8.swift +173 -0
  17. data/snapshot/lib/snapshot.rb +6 -0
  18. data/snapshot/lib/snapshot/collector.rb +37 -11
  19. data/snapshot/lib/snapshot/detect_values.rb +4 -1
  20. data/snapshot/lib/snapshot/fixes/simulator_zoom_fix.rb +1 -1
  21. data/snapshot/lib/snapshot/options.rb +5 -0
  22. data/snapshot/lib/snapshot/reports_generator.rb +34 -3
  23. data/snapshot/lib/snapshot/runner.rb +30 -215
  24. data/snapshot/lib/snapshot/setup.rb +9 -3
  25. data/snapshot/lib/snapshot/simulator_launchers/launcher_configuration.rb +53 -0
  26. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +203 -0
  27. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +129 -0
  28. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_xcode_8.rb +110 -0
  29. data/snapshot/lib/snapshot/test_command_generator.rb +39 -107
  30. data/snapshot/lib/snapshot/test_command_generator_base.rb +94 -0
  31. data/snapshot/lib/snapshot/test_command_generator_xcode_8.rb +65 -0
  32. data/snapshot/lib/snapshot/update.rb +4 -2
  33. data/spaceship/lib/spaceship/portal/provisioning_profile.rb +5 -2
  34. data/spaceship/lib/spaceship/test_flight/client.rb +8 -0
  35. data/spaceship/lib/spaceship/test_flight/tester.rb +20 -4
  36. 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]
@@ -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
- to_store = attachments(containing)
11
- matches = output.scan(/snapshot: (.*)/)
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
- if FastlaneCore::Globals.verbose?
34
- UI.success "Copying file '#{from_path}' to '#{output_path}'..."
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] << "Mac"
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 simulator to 100%"
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 available_devices
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 inlucded in the iPhone 6 Plus
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
- self.number_of_retries_due_to_failing_simulator = 0
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
- results[device][language] = run_for_device_and_language(language, locale, device, launch_arguments)
54
-
55
- copy_simulator_logs(device, language, locale, launch_arguments)
56
- end
57
- end
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!(self.collected_errors.join('; ')) if self.collected_errors.count > 0
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
- FileUtils.rm_rf(TestCommandGenerator.derived_data_path)
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
- def version_of_bundled_helper
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
- ## Something like "// SnapshotHelperVersion [1.2]", but be relaxed about whitespace
290
- current_version.gsub(%r{^//\w*}, '').strip
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