require 'rake' require 'rake/tasklib' module TripAdvisor class TestDevice attr_accessor :platform, :name, :valid_schemes def initialize @platform = 'iOS Simulator' @name = 'iPhone 5s' @valid_schemes = [] end def destination_arg(os_version) "platform='#{platform}',OS=#{os_version},name='#{name}'" end def valid_for?(scheme_name) valid_schemes.empty? || valid_schemes.include?(scheme_name) end def sdk platform.eql?('iOS Simulator') ? 'iphonesimulator' : 'iphoneos' end def short_name "#{name.downcase.gsub(/\s+/, '')}" end def to_s "#{name} (#{platform})" end def hash [platform, name].hash end def eql?(object) platform.eql?(object.platform) && name.eql?(object.name) end end class TestReport include Singleton def initialize @namespaces = {} @success = true end def add_result(namespace, ios_version, test_device, success) @namespaces[namespace] ||= {} @namespaces[namespace][ios_version] ||= {} @namespaces[namespace][ios_version][test_device] = success @success = false unless success end def [](ios_version) @namespaces[ios_version] end def success? @success end def failure? @success == false end def report @namespaces.each do |namespace, version_status| version_status.each do |ios_version, device_status| device_status.each do |test_device, success| puts "\033[0;31m!! #{namespace} tests failed on #{test_device} under iOS #{ios_version}" unless success end end end puts "\033[0;32m** All tests executed successfully" if success? end end class TestTasks < Rake::TaskLib include ::Rake::DSL if defined?(::Rake::DSL) attr_reader :namespace_name, :prepare_dependency attr_accessor :workspace, :schemes_dir, :schemes, :ios_versions attr_accessor :runner, :xctool_path, :xcodebuild_path, :environment_variables attr_accessor :ios_versions, :test_devices, :settings def initialize(namespace_name = :test) @namespace_name = namespace_name.is_a?(Hash) ? namespace_name.keys.first : namespace_name @prepare_dependency = namespace_name.is_a?(Hash) ? namespace_name.values.first : nil @schemes_dir = 'Tests/Schemes' @ios_versions = %w{7.0 8.0} @xctool_path = '/usr/local/bin/xctool' @xcodebuild_path = 'xcodebuild' @runner = :xctool @settings = {} @environment_variables = '' yield self if block_given? raise "A workspace must be configured" unless workspace raise "At least one scheme must be configured" unless schemes raise "At least one test device must be configured" unless test_devices define_tasks end def runner=(runner) raise ArgumentError, "Must be :xctool or :xcodebuild" unless %w{xctool xcodebuild}.include?(runner.to_s) @runner = runner.to_sym end def define_tasks namespace self.namespace_name do task (prepare_dependency ? { prepare: prepare_dependency} : :prepare ) do run(%Q{mkdir -p "#{workspace}/xcshareddata/xcschemes" && cp Tests/Schemes/*.xcscheme "#{workspace}/xcshareddata/xcschemes/"}) end # Iterate across the schemes and define a task for each version of iOS and an aggregate task schemes.each do |scheme_namespace, scheme_names| scheme_names_array = scheme_names.is_a?(Array) ? scheme_names : [scheme_names] namespace scheme_namespace do scheme_names_array.each do |scheme_name| ios_versions.each do |ios_version| namespace ios_version do test_devices.each do |test_device| next unless test_device.valid_for?(scheme_name) desc "Run #{scheme_namespace} tests against #{ios_version} SDK on #{test_device}" task test_device.short_name => :prepare do test_scheme(scheme_name, namespace: scheme_namespace, ios_version: ios_version, test_device: test_device) end end end desc "Run #{scheme_namespace} tests against #{ios_version} SDK on valid devices" task "#{ios_version}" => test_devices.select { |test_device| test_device.valid_for?(scheme_name) }.map { |test_device| "#{scheme_namespace}:#{ios_version}:#{test_device.short_name}" } end end end desc "Run #{scheme_namespace} tests against all SDKs and valid devices" task scheme_namespace => ios_versions.map { |ios_version| "#{scheme_namespace}:#{ios_version}" } end end desc "Run the #{schemes.keys.join(', ')} tests on all devices and SDKs" task namespace_name => schemes.keys.map { |scheme| "#{namespace_name}:#{scheme}" } do TripAdvisor::TestReport.instance.report end end private def run(command) puts "Executing `#{command}`" system(command) end def test_scheme(scheme, options) system(%q{killall "iPhone Simulator"}) namespace = options[:namespace] ios_version = options[:ios_version] test_device = options[:test_device] test_sdk = "#{ios_version}" settings_arg = settings.map { |k,v| "#{k}=#{v}"}.join(' ') destination_arg = test_device.destination_arg(ios_version) success = if xctool? run("#{xctool_path} -workspace #{workspace} -scheme #{scheme} -sdk #{test_device.sdk} test -freshSimulator -destination #{destination_arg} #{settings_arg} #{environment_variables} DEVICE_NAME=#{test_device.short_name}_#{ios_version} -reporter junit:#{scheme}-#{test_sdk}-#{test_device.short_name}.xml") elsif xcodebuild? run("#{xcodebuild_path} -workspace #{workspace} -scheme #{scheme} -sdk #{test_device.sdk} test -destination #{destination_arg} #{settings_arg} #{environment_variables} DEVICE_NAME=#{test_device.short_name}_#{ios_version} | xcpretty -cr html --output build/reports/#{scheme}-#{test_sdk}-#{test_device.short_name}.html ; exit ${PIPESTATUS[0]}") end TripAdvisor::TestReport.instance.add_result(namespace, ios_version, test_device, success) end def xctool? runner == :xctool end def xcodebuild? runner == :xcodebuild end end end