diff --git a/README.md b/README.md
index 90c26f4..6317dce 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Fueled specific.
- [install_wwdr_certificate](#user-content-install_wwdr_certificate)
- [set_app_versions_plist_ios](#user-content-set_app_versions_plist_ios)
- [set_app_versions_xcodeproj_ios](#user-content-set_app_versions_xcodeproj_ios)
+ - [check_code_coverage_ios](#check_code_coverage_ios)
- [upload_to_app_store](#user-content-upload_to_app_store)
* Android
- [define_versions_android](#user-content-define_versions_android)
@@ -370,6 +371,16 @@ contain the build number.
| `build_number`
`BUILD_NUMBER` | The build number (eg: 625) | `SharedValues::FUELED_BUILD_NUMBER` |
| `export_method`
`EXPORT_METHOD` | The build export method (eg: app-store) | |
+### `check_code_coverage_ios`
+
+Check how much of your code is covered by unit tests.
+
+| Key & Env Var | Description | Default Value
+|-----------------|--------------------|---|
+| `code_coverage_config_file_path` | The path of the code coverage config file, the structure of this file is created by Fueled | |
+| `result_bundle_file_path` | The result bundle file path (xcresult) | |
+| `minimum_code_coverage_percentage` | The minimum code coverage percentage accepted (eg: 64.5) | 80 |
+
#### `tag`
Tag the version using the following pattern : `v[short_version]#[build_number]-[build_config]`
diff --git a/lib/fastlane/plugin/fueled/actions/check_code_coverage_ios.rb b/lib/fastlane/plugin/fueled/actions/check_code_coverage_ios.rb
new file mode 100644
index 0000000..2e3fa9a
--- /dev/null
+++ b/lib/fastlane/plugin/fueled/actions/check_code_coverage_ios.rb
@@ -0,0 +1,116 @@
+require 'json'
+
+module Fastlane
+ module Actions
+
+ class CheckCodeCoverageIosAction < Action
+
+ def self.run(params)
+ code_coverage_config_file_path = params[:code_coverage_config_file_path]
+ result_bundle_file_path = params[:result_bundle_file_path]
+ minimum_code_coverage_percentage = params[:minimum_code_coverage_percentage]
+
+ if !minimum_code_coverage_percentage.between?(0, 100)
+ UI.user_error!("Minimum code coverage percentage should be between 0 and 100.")
+ end
+
+ if !File.exist? code_coverage_config_file_path
+ UI.user_error!("Cannot find Fueled code coverage config file at #{code_coverage_config_file_path}")
+ end
+
+ if !File.exist? result_bundle_file_path
+ UI.user_error!("Cannot find result bundle file at #{result_bundle_file_path}")
+ end
+
+ code_coverage_config_file = File.read(code_coverage_config_file_path)
+
+ if !code_coverage_config = JSON.parse(code_coverage_config_file) rescue nil
+ UI.user_error!('The provided Fueled code coverage config file isn\'t in a valid format')
+ end
+
+ UI.message("Calculating Code Coverage Percentage... 🤓")
+ test_results_json_string = `xcrun xccov view --report --json \"#{result_bundle_file_path}\"`
+ test_results = JSON.parse(test_results_json_string)
+
+ included_targets = code_coverage_config["targets"]
+ UI.message("\n\nLooking inside these targets: #{included_targets.join(', ')} 🕵🏻♂️\n")
+
+ number_of_files = 0
+ code_coverage_percentage = 0
+
+ test_results['targets'].each do |target|
+ target_name = target['name'].split(".")[0]
+ if included_targets.include?(target_name)
+ target['files'].each do |file|
+ if should_check_code_coverage(file, code_coverage_config)
+ UI.message("#{file['name']}: #{file['lineCoverage'] * 100}%")
+ number_of_files += 1
+ code_coverage_percentage += file['lineCoverage'] * 100
+ end
+ end
+ end
+ end
+
+ total_code_coverage_percentage = code_coverage_percentage / number_of_files
+ file_message = number_of_files > 1 ? "files" : "file"
+ UI.message("Checked code coverage on #{number_of_files} #{file_message} with total percentage of #{total_code_coverage_percentage}%")
+
+ if total_code_coverage_percentage < minimum_code_coverage_percentage
+ UI.build_failure!("Code coverage percentage is below the minimum! 🚫🚫🚫")
+ else
+ UI.success("Code coverage percentage is accepted ✅.")
+ end
+ end
+
+ def self.should_check_code_coverage(file, code_coverage_config)
+ file_name = file['name'].downcase
+ include_condition = code_coverage_config['file_name_include'].reduce(false) { |result, name| result || file_name.include?(name.downcase) }
+ exclude_condition = code_coverage_config['file_name_exclude'].reduce(false) { |result, name| result || file_name.include?(name.downcase) }
+ include_condition && !exclude_condition && !file_name.end_with?(".generated.swift")
+ end
+
+ #####################################################
+ # @!group Documentation
+ #####################################################
+
+ def self.description
+ "Check the code coverage percentage, if it's lower than the provided one the action will fail. result_bundle should be set to true in your scan action"
+ end
+
+ def self.available_options
+ [
+ FastlaneCore::ConfigItem.new(
+ key: :code_coverage_config_file_path,
+ env_name: "CODE_COVERAGE_CONFIG_FILE_PATH",
+ description: "The Fueled code coverage config file path",
+ is_string: true,
+ optional: false
+ ),
+ FastlaneCore::ConfigItem.new(
+ key: :result_bundle_file_path,
+ env_name: "RESULT_BUNDLE_FILE_PATH",
+ description: "The result bundle file path (xcresult)",
+ is_string: true,
+ optional: false
+ ),
+ FastlaneCore::ConfigItem.new(
+ key: :minimum_code_coverage_percentage,
+ env_name: "MINIMUM_CODE_COVERAGE_PERCENTAGE",
+ description: "The minimum percentage required for code coverage, a calculated percentage below the given one will lead to a failure",
+ is_string: false,
+ optional: true,
+ default_value: 80
+ )
+ ]
+ end
+
+ def self.authors
+ ["fueled"]
+ end
+
+ def self.is_supported?(platform)
+ [:ios, :mac].include?(platform)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/fastlane/plugin/fueled/version.rb b/lib/fastlane/plugin/fueled/version.rb
index bc4c855..ff3c6a9 100644
--- a/lib/fastlane/plugin/fueled/version.rb
+++ b/lib/fastlane/plugin/fueled/version.rb
@@ -1,5 +1,5 @@
module Fastlane
module Fueled
- VERSION = "0.2.4"
+ VERSION = "0.2.5"
end
end