lib/scan/detect_values.rb in scan-0.12.0 vs lib/scan/detect_values.rb in scan-0.12.1

- old
+ new

@@ -18,12 +18,12 @@ config.load_configuration_file(Scan.scanfile_name) end Scan.project.select_scheme - default_device_ios if Scan.project.ios? - default_device_tvos if Scan.project.tvos? + detect_simulator_ios if Scan.project.ios? + detect_simulator_tvos if Scan.project.tvos? detect_destination default_derived_data return config @@ -37,106 +37,126 @@ default_path = File.expand_path("../../..", default_path) UI.verbose("Detected derived data path '#{default_path}'") Scan.config[:derived_data_path] = default_path end - def self.filter_simulators(simulators, deployment_target) - # Filter out any simulators that are not the same major and minor version of our deployment target + def self.filter_simulators(simulators, operator = :greater_than_or_equal, deployment_target) deployment_target_version = Gem::Version.new(deployment_target) simulators.select do |s| - sim_version = Gem::Version.new(s.ios_version) - (sim_version >= deployment_target_version) + sim_version = Gem::Version.new(s.os_version) + if operator == :greater_than_or_equal + sim_version >= deployment_target_version + elsif operator == :equal + sim_version == deployment_target_version + else + false # this will show an error message in the detect_simulator method + end end end - def self.default_device_ios + def self.regular_expression_for_split_on_whitespace_followed_by_parenthesized_version + # %r{ + # \s # a whitespace character + # (?= # followed by -- using lookahead + # \( # open parenthesis + # [\d\.]+ # our version -- one or more digits or full stops + # \) # close parenthesis + # $ # end of line + # ) # end of lookahead + # } + /\s(?=\([\d\.]+\)$)/ + end + + def self.detect_simulator_ios + # An iPhone 5s is a reasonably small and useful default for tests + detect_simulator('iOS', 'IPHONEOS_DEPLOYMENT_TARGET', 'iPhone 5s', nil) + end + + def self.detect_simulator_tvos + detect_simulator('tvOS', 'TVOS_DEPLOYMENT_TARGET', 'Apple TV 1080p', 'TV') + end + + def self.detect_simulator(requested_os_type, deployment_target_key, default_device_name, simulator_type_descriptor) + require 'set' devices = Scan.config[:devices] || Array(Scan.config[:device]) # important to use Array(nil) for when the value is nil - found_devices = [] - xcode_target = Scan.project.build_settings(key: "IPHONEOS_DEPLOYMENT_TARGET") - if devices.any? - # Optionally, we only do this if the user specified a custom device or an array of devices - devices.each do |device| - lookup_device = device.to_s.strip - has_version = lookup_device.include?(xcode_target) || lookup_device.include?('(') - lookup_device = lookup_device.tr('()', '') # Remove parenthesis - # Default to Xcode target version if no device version is specified. - lookup_device = lookup_device + " " + xcode_target unless has_version + deployment_target_version = Scan.project.build_settings(key: deployment_target_key) - found = FastlaneCore::Simulator.all.detect do |d| - (d.name + " " + d.ios_version).include? lookup_device + simulators = filter_simulators( + FastlaneCore::DeviceManager.simulators(requested_os_type).tap do |array| + if array.empty? + UI.user_error!(['No', simulator_type_descriptor, 'simulators found on local machine'].reject(&:nil?).join(' ')) end - - if found - found_devices.push(found) - else - UI.error("Ignoring '#{device}', couldn't find matching simulator") - end + end, + :greater_than_or_equal, + deployment_target_version + ).tap do |sims| + if sims.empty? + UI.error("No simulators found that are greater than or equal to the version of deployment target (#{deployment_target_version})") end - - if found_devices.any? - Scan.devices = found_devices - return - else - UI.error("Couldn't find any matching simulators for '#{devices}' - falling back to default simulator") - end end - sims = FastlaneCore::Simulator.all - sims = filter_simulators(sims, xcode_target) + # At this point we have all simulators for the given deployment target (or higher) - # An iPhone 5s is reasonable small and useful for tests - found = sims.detect { |d| d.name == "iPhone 5s" } - found ||= sims.first # anything is better than nothing + # We create 2 lambdas, which we iterate over later on + # If the first lambda `matches` found a simulator to use + # we'll never call the second one - if found - Scan.devices = [found] - else - UI.user_error!("No simulators found on local machine") - end - end + matches = lambda do + set_of_simulators = devices.inject( + Set.new # of simulators + ) do |set, device_string| + pieces = device_string.split(regular_expression_for_split_on_whitespace_followed_by_parenthesized_version) - def self.default_device_tvos - devices = Scan.config[:devices] || Array(Scan.config[:device]) # important to use Array(nil) for when the value is nil - found_devices = [] + selector = ->(sim) { pieces.count > 0 && sim.name == pieces.first } - if devices.any? - # Optionally, we only do this if the user specified a custom device or an array of devices - devices.each do |device| - lookup_device = device.to_s.strip.tr('()', '') # Remove parenthesis - - found = FastlaneCore::SimulatorTV.all.detect do |d| - (d.name + " " + d.os_version).include? lookup_device + set + ( + if pieces.count == 0 + [] # empty array + elsif pieces.count == 1 + simulators + .select(&selector) + .reverse # more efficient, because `simctl` prints higher versions first + .sort_by! { |sim| Gem::Version.new(sim.os_version) } + .pop(1) + else # pieces.count == 2 -- mathematically, because of the 'end of line' part of our regular expression + version = pieces[1].tr('()', '') + potential_emptiness_error = lambda do |sims| + UI.error("No simulators found that are equal to the version " \ + "of specifier (#{version}) and greater than or equal to the version " \ + "of deployment target (#{deployment_target_version})") if sims.empty? + end + filter_simulators(simulators, :equal, version).tap(&potential_emptiness_error).select(&selector) + end + ).tap do |array| + UI.error("Ignoring '#{device_string}', couldn’t find matching simulator") if array.empty? end - - if found - found_devices.push(found) - else - UI.error("Ignoring '#{device}', couldn't find matching simulator") - end end - if found_devices.any? - Scan.devices = found_devices - return - else - UI.error("Couldn't find any matching simulators for '#{devices}' - falling back to default simulator") - end + set_of_simulators.to_a end - sims = FastlaneCore::SimulatorTV.all - xcode_target = Scan.project.build_settings(key: "TVOS_DEPLOYMENT_TARGET") - sims = filter_simulators(sims, xcode_target) + default = lambda do + UI.error("Couldn't find any matching simulators for '#{devices}' - falling back to default simulator") - # Apple TV 1080p is useful for tests - found = sims.detect { |d| d.name == "Apple TV 1080p" } - found ||= sims.first # anything is better than nothing + result = Array( + simulators + .select { |sim| sim.name == default_device_name } + .reverse # more efficient, because `simctl` prints higher versions first + .sort_by! { |sim| Gem::Version.new(sim.os_version) } + .last || simulators.first + ) - if found - Scan.devices = [found] - else - UI.user_error!("No TV simulators found on the local machine") + UI.error("Found simulator \"#{result.first.name} (#{result.first.os_version})\"") if result.first + + result end + + # grab the first unempty evaluated array + Scan.devices = [matches, default].lazy.map { |x| + arr = x.call + arr unless arr.empty? + }.reject(&:nil?).first end def self.min_xcode8? Helper.xcode_version.split(".").first.to_i >= 8 end