class Highway::Steps::Library::XcodeTestStep
Public Class Methods
name()
click to toggle source
# File lib/highway/steps/library/xcode_test.rb, line 14 def self.name "xcode_test" end
parameters()
click to toggle source
# File lib/highway/steps/library/xcode_test.rb, line 18 def self.parameters [ Parameters::Single.new( name: "clean", type: Types::Bool.new(), required: false, default: true, ), Parameters::Single.new( name: "configuration", type: Types::String.new(), required: false, ), Parameters::Single.new( name: "device", type: Types::String.new(), required: false, ), Parameters::Single.new( name: "flags", type: Types::Array.new(Types::String.new()), required: false, default: [], ), Parameters::Single.new( name: "project", type: Types::AnyOf.new( project: Types::String.regex(/.+\.xcodeproj/), workspace: Types::String.regex(/.+\.xcworkspace/), ), required: true, ), Parameters::Single.new( name: "scheme", type: Types::String.new(), required: true, ), Parameters::Single.new( name: "skip_build", type: Types::Bool.new(), required: false, default: false, ), Parameters::Single.new( name: "settings", type: Types::Hash.new(Types::String.new(), validate: lambda { |dict| dict.keys.all? { |key| /[A-Z_][A-Z0-9_]*/ =~ key } }), required: false, default: {}, ), Parameters::Single.new( name: "code_coverage", type: Types::Bool.new(), required: false, ) ] end
run(parameters:, context:, report:)
click to toggle source
# File lib/highway/steps/library/xcode_test.rb, line 75 def self.run(parameters:, context:, report:) # Interpret the parameters. At this point they are parsed and # transformed to be recognizable by Fastlane. clean = parameters["clean"] configuration = parameters["configuration"] device = parameters["device"] scheme = parameters["scheme"] skip_build = parameters["skip_build"] code_coverage = parameters["code_coverage"] flags = parameters["flags"].join(" ") settings = parameters["settings"].map { |setting, value| "#{setting}=\"#{value.shellescape}\"" }.join(" ") xcargs = flags + settings xcargs = nil if xcargs.empty? project_key = parameters["project"][:tag] project_value = parameters["project"][:value] # Prepare artifacts. Create temporary directories, get file names that # will be later passed to the build command. output_raw_temp_dir = Dir.mktmpdir() output_raw_path = report.prepare_artifact("raw.log") output_html_path = report.prepare_artifact("report.html") output_junit_path = report.prepare_artifact("report.junit") output_html_file = File.basename(output_html_path) output_junit_file = File.basename(output_junit_path) # Configure xcpretty. Set custom locations of report artifacts so that # we can track them accurately. output_dir = context.artifacts_dir output_types = ["html", "junit"].join(",") output_files = [output_html_file, output_junit_file].join(",") # Prepare temporary variables. report_test = {} report_artifacts = {} rescued_error = nil # Run the build and test. begin context.run_action("run_tests", options: { project_key => project_value, clean: clean, configuration: configuration, device: device, scheme: scheme, code_coverage: code_coverage, skip_build: skip_build, xcargs: xcargs, buildlog_path: output_raw_temp_dir, output_directory: output_dir, output_types: output_types, output_files: output_files, }) rescue FastlaneCore::Interface::FastlaneBuildFailure => error # A compile error occured. Save it to be re-raised later. report_test[:result] = :error rescued_error = error rescue FastlaneCore::Interface::FastlaneTestFailure => error # A test failure occured. Save it to be re-raised later. report_test[:result] = :failure rescued_error = error else # Build succeeded! report_test[:result] = :success end # Now the real fun begins. Move the raw xcodebuild log from temporary # directory to artifacts directory. output_raw_temp_path = Dir.glob(File.join(output_raw_temp_dir, "*.log")).first unless output_raw_temp_path.nil? FileUtils.mv(output_raw_temp_path, output_raw_path) end # Save the artifact paths in the subreport. report_artifacts[:raw] = output_raw_path report_artifacts[:html] = output_html_path report_artifacts[:junit] = output_junit_path # Load the raw log and pipe it through xcpretty with a JSON formatter. # That will output a machine-readable information about everything # that happened in the build. xcpretty_json_formatter_path = context.run_sh("xcpretty-json-formatter", bundle_exec: true, silent: true) temp_json_report_path = File.join(Dir.mktmpdir(), "report.json") context.with_modified_env({"XCPRETTY_JSON_FILE_OUTPUT" => temp_json_report_path}) do context.run_sh(["cat", output_raw_path, "| xcpretty --formatter", xcpretty_json_formatter_path], bundle_exec: true, silent: true) end # Export JSON file report to environment variable context.env["XCODE_TEST_JSON_REPORT_PATH"] = temp_json_report_path # Load the build report and a JUnit report into memory. junit_report = Scan::TestResultParser.new.parse_result(File.read(output_junit_path)) if File.exist?(temp_json_report_path) xcode_report = JSON.parse(File.read(temp_json_report_path)) else xcode_report = Hash.new() end # Extract test numbers from JUnit report. report_test_count = {} report_test_count[:all] = junit_report[:tests] report_test_count[:failed] = junit_report[:failures] report_test_count[:succeeded] = report_test_count[:all] - report_test_count[:failed] report_test[:count] = report_test_count # Extract compile errors from the build report. report_test_errors = [] report_test_errors += xcode_report.fetch("file_missing_errors", []).map { |entry| {location: File.basename(entry["file_path"]), reason: entry["reason"]} } report_test_errors += xcode_report.fetch("compile_errors", []).map { |entry| {location: File.basename(entry["file_path"]), reason: entry["reason"]} } report_test_errors += xcode_report.fetch("undefined_symbols_errors", []).map { |entry| {location: entry["symbol"], reason: entry["message"]} } report_test_errors += xcode_report.fetch("format_duplicate_symbols", []).map { {location: nil, reason: entry["message"]} } report_test_errors += xcode_report.fetch("errors", []).map { |entry| {location: nil, reason: entry} } report_test[:errors] = report_test_errors # Extract test failures from the build report. report_test_failures = xcode_report.fetch("tests_failures", {}).values.flatten.map { |entry| {location: entry["test_case"], reason: entry["reason"]} } report_test[:failures] = report_test_failures # Save the test and artifacts subreports in the report. report[:test] = report_test report[:artifacts] = report_artifacts # Re-raise the error after the report is finally prepared. raise rescued_error if rescued_error != nil end