move ceedling tests to test/unit-test

This commit is contained in:
hathach
2022-12-08 09:59:02 +07:00
parent 4b50ca2a61
commit be4f4e4f79
213 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,433 @@
ceedling-gcov
=============
# Plugin Overview
Plugin for integrating GNU GCov code coverage tool into Ceedling projects.
Currently only designed for the gcov command (like LCOV for example). In the
future we could configure this to work with other code coverage tools.
This plugin currently uses [gcovr](https://www.gcovr.com/) and / or
[ReportGenerator](https://danielpalme.github.io/ReportGenerator/)
as utilities to generate HTML, XML, JSON, or Text reports. The normal gcov
plugin _must_ be run first for these reports to generate.
## Installation
gcovr can be installed via pip like so:
```sh
pip install gcovr
```
ReportGenerator can be installed via .NET Core like so:
```sh
dotnet tool install -g dotnet-reportgenerator-globaltool
```
It is not required to install both `gcovr` and `ReportGenerator`. Either utility
may be installed to create reports.
## Configuration
The gcov plugin supports configuration options via your `project.yml` provided
by Ceedling.
### Utilities
Gcovr and / or ReportGenerator may be enabled to create coverage reports.
```yaml
:gcov:
:utilities:
- gcovr # Use gcovr to create the specified reports (default).
- ReportGenerator # Use ReportGenerator to create the specified reports.
```
### Reports
Various reports are available and may be enabled with the following
configuration item. See the specific report sections in this README
for additional options and information. All generated reports will be found in `build/artifacts/gcov`.
```yaml
:gcov:
# Specify one or more reports to generate.
# Defaults to HtmlBasic.
:reports:
# Make an HTML summary report.
# Supported utilities: gcovr, ReportGenerator
- HtmlBasic
# Make an HTML report with line by line coverage of each source file.
# Supported utilities: gcovr, ReportGenerator
- HtmlDetailed
# Make a Text report, which may be output to the console with gcovr or a file in both gcovr and ReportGenerator.
# Supported utilities: gcovr, ReportGenerator
- Text
# Make a Cobertura XML report.
# Supported utilities: gcovr, ReportGenerator
- Cobertura
# Make a SonarQube XML report.
# Supported utilities: gcovr, ReportGenerator
- SonarQube
# Make a JSON report.
# Supported utilities: gcovr
- JSON
# Make a detailed HTML report with CSS and JavaScript included in every HTML page. Useful for build servers.
# Supported utilities: ReportGenerator
- HtmlInline
# Make a detailed HTML report with a light theme and CSS and JavaScript included in every HTML page for Azure DevOps.
# Supported utilities: ReportGenerator
- HtmlInlineAzure
# Make a detailed HTML report with a dark theme and CSS and JavaScript included in every HTML page for Azure DevOps.
# Supported utilities: ReportGenerator
- HtmlInlineAzureDark
# Make a single HTML file containing a chart with historic coverage information.
# Supported utilities: ReportGenerator
- HtmlChart
# Make a detailed HTML report in a single file.
# Supported utilities: ReportGenerator
- MHtml
# Make SVG and PNG files that show line and / or branch coverage information.
# Supported utilities: ReportGenerator
- Badges
# Make a single CSV file containing coverage information per file.
# Supported utilities: ReportGenerator
- CsvSummary
# Make a single TEX file containing a summary for all files and detailed reports for each files.
# Supported utilities: ReportGenerator
- Latex
# Make a single TEX file containing a summary for all files.
# Supported utilities: ReportGenerator
- LatexSummary
# Make a single PNG file containing a chart with historic coverage information.
# Supported utilities: ReportGenerator
- PngChart
# Command line output interpreted by TeamCity.
# Supported utilities: ReportGenerator
- TeamCitySummary
# Make a text file in lcov format.
# Supported utilities: ReportGenerator
- lcov
# Make a XML file containing a summary for all classes and detailed reports for each class.
# Supported utilities: ReportGenerator
- Xml
# Make a single XML file containing a summary for all files.
# Supported utilities: ReportGenerator
- XmlSummary
```
### Gcovr HTML Reports
Generation of Gcovr HTML reports may be modified with the following configuration items.
```yaml
:gcov:
# Set to 'true' to enable HTML reports or set to 'false' to disable.
# Defaults to enabled. (gcovr --html)
# Deprecated - See the :reports: configuration option.
:html_report: [true|false]
# Gcovr supports generating two types of HTML reports. Use 'basic' to create
# an HTML report with only the overall file information. Use 'detailed' to create
# an HTML report with line by line coverage of each source file.
# Defaults to 'basic'. Set to 'detailed' for (gcovr --html-details).
# Deprecated - See the :reports: configuration option.
:html_report_type: [basic|detailed]
:gcovr:
# HTML report filename.
:html_artifact_filename: <output>
# Use 'title' as title for the HTML report.
# Default is 'Head'. (gcovr --html-title)
:html_title: <title>
# If the coverage is below MEDIUM, the value is marked as low coverage in the HTML report.
# MEDIUM has to be lower than or equal to value of html_high_threshold.
# If MEDIUM is equal to value of html_high_threshold the report has only high and low coverage.
# Default is 75.0. (gcovr --html-medium-threshold)
:html_medium_threshold: 75
# If the coverage is below HIGH, the value is marked as medium coverage in the HTML report.
# HIGH has to be greater than or equal to value of html_medium_threshold.
# If HIGH is equal to value of html_medium_threshold the report has only high and low coverage.
# Default is 90.0. (gcovr -html-high-threshold)
:html_high_threshold: 90
# Set to 'true' to use absolute paths to link the 'detailed' reports.
# Defaults to relative links. (gcovr --html-absolute-paths)
:html_absolute_paths: [true|false]
# Override the declared HTML report encoding. Defaults to UTF-8. (gcovr --html-encoding)
:html_encoding: <html_encoding>
```
### Cobertura XML Reports
Generation of Cobertura XML reports may be modified with the following configuration items.
```yaml
:gcov:
# Set to 'true' to enable Cobertura XML reports or set to 'false' to disable.
# Defaults to disabled. (gcovr --xml)
# Deprecated - See the :reports: configuration option.
:xml_report: [true|false]
:gcovr:
# Set to 'true' to pretty-print the Cobertura XML report, otherwise set to 'false'.
# Defaults to disabled. (gcovr --xml-pretty)
:xml_pretty: [true|false]
:cobertura_pretty: [true|false]
# Cobertura XML report filename.
:xml_artifact_filename: <output>
:cobertura_artifact_filename: <output>
```
### SonarQube XML Reports
Generation of SonarQube XML reports may be modified with the following configuration items.
```yaml
:gcov:
:gcovr:
# SonarQube XML report filename.
:sonarqube_artifact_filename: <output>
```
### JSON Reports
Generation of JSON reports may be modified with the following configuration items.
```yaml
:gcov:
:gcovr:
# Set to 'true' to pretty-print the JSON report, otherwise set 'false'.
# Defaults to disabled. (gcovr --json-pretty)
:json_pretty: [true|false]
# JSON report filename.
:json_artifact_filename: <output>
```
### Text Reports
Generation of text reports may be modified with the following configuration items.
Text reports may be printed to the console or output to a file.
```yaml
:gcov:
:gcovr:
# Text report filename.
# The text report is printed to the console when no filename is provided.
:text_artifact_filename: <output>
```
### Common Report Options
There are a number of options to control which files are considered part of
the coverage report. Most often, we only care about coverage on our source code, and not
on tests or automatically generated mocks, runners, etc. However, there are times
where this isn't true... or there are times where we've moved ceedling's directory
structure so that the project file isn't at the root of the project anymore. In these
cases, you may need to tweak `report_include`, `report_exclude`, and `exclude_directories`.
One important note about `report_root`: gcovr will take only a single root folder, unlike
Ceedling's ability to take as many as you like. So you will need to choose a folder which is
a superset of ALL the folders you want, and then use the include or exclude options to set up
patterns of files to pay attention to or ignore. It's not ideal, but it works.
Finally, there are a number of settings which can be specified to adjust the
default behaviors of gcovr:
```yaml
:gcov:
:gcovr:
# The root directory of your source files. Defaults to ".", the current directory.
# File names are reported relative to this root. The report_root is the default report_include.
:report_root: "."
# Load the specified configuration file.
# Defaults to gcovr.cfg in the report_root directory. (gcovr --config)
:config_file: <config_file>
# Exit with a status of 2 if the total line coverage is less than MIN.
# Can be ORed with exit status of 'fail_under_branch' option. (gcovr --fail-under-line)
:fail_under_line: 30
# Exit with a status of 4 if the total branch coverage is less than MIN.
# Can be ORed with exit status of 'fail_under_line' option. (gcovr --fail-under-branch)
:fail_under_branch: 30
# Select the source file encoding.
# Defaults to the system default encoding (UTF-8). (gcovr --source-encoding)
:source_encoding: <source_encoding>
# Report the branch coverage instead of the line coverage. For text report only. (gcovr --branches).
:branches: [true|false]
# Sort entries by increasing number of uncovered lines.
# For text and HTML report. (gcovr --sort-uncovered)
:sort_uncovered: [true|false]
# Sort entries by increasing percentage of uncovered lines.
# For text and HTML report. (gcovr --sort-percentage)
:sort_percentage: [true|false]
# Print a small report to stdout with line & branch percentage coverage.
# This is in addition to other reports. (gcovr --print-summary).
:print_summary: [true|false]
# Keep only source files that match this filter. (gcovr --filter).
:report_include: "^src"
# Exclude source files that match this filter. (gcovr --exclude).
:report_exclude: "^vendor.*|^build.*|^test.*|^lib.*"
# Keep only gcov data files that match this filter. (gcovr --gcov-filter).
:gcov_filter: <gcov_filter>
# Exclude gcov data files that match this filter. (gcovr --gcov-exclude).
:gcov_exclude: <gcov_exclude>
# Exclude directories that match this regex while searching
# raw coverage files. (gcovr --exclude-directories).
:exclude_directories: <exclude_dirs>
# Use a particular gcov executable. (gcovr --gcov-executable).
:gcov_executable: <gcov_cmd>
# Exclude branch coverage from lines without useful
# source code. (gcovr --exclude-unreachable-branches).
:exclude_unreachable_branches: [true|false]
# For branch coverage, exclude branches that the compiler
# generates for exception handling. (gcovr --exclude-throw-branches).
:exclude_throw_branches: [true|false]
# Use existing gcov files for analysis. Default: False. (gcovr --use-gcov-files)
:use_gcov_files: [true|false]
# Skip lines with parse errors in GCOV files instead of
# exiting with an error. (gcovr --gcov-ignore-parse-errors).
:gcov_ignore_parse_errors: [true|false]
# Override normal working directory detection. (gcovr --object-directory)
:object_directory: <objdir>
# Keep gcov files after processing. (gcovr --keep).
:keep: [true|false]
# Delete gcda files after processing. (gcovr --delete).
:delete: [true|false]
# Set the number of threads to use in parallel. (gcovr -j).
:num_parallel_threads: <num_threads>
# When scanning the code coverage, if any files are found that do not have
# associated coverage data, the command will abort with an error message.
:abort_on_uncovered: true
# When using the ``abort_on_uncovered`` option, the files in this list will not
# trigger a failure.
# Ceedling globs described in the Ceedling packet ``Path`` section can be used
# when directories are placed on the list. Globs are limited to matching directories
# and not files.
:uncovered_ignore_list: []
```
### ReportGenerator Configuration
The ReportGenerator utility may be configured with the following configuration items.
All generated reports may be found in `build/artifacts/gcov/ReportGenerator`.
```yaml
:gcov:
:report_generator:
# Optional directory for storing persistent coverage information.
# Can be used in future reports to show coverage evolution.
:history_directory: <history_directory>
# Optional plugin files for custom reports or custom history storage (separated by semicolon).
:plugins: CustomReports.dll
# Optional list of assemblies that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:assembly_filters: "+Included;-Excluded"
# Optional list of classes that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:class_filters: "+Included;-Excluded"
# Optional list of files that should be included or excluded in the report (separated by semicolon)..
# Exclusion filters take precedence over inclusion filters.
# Wildcards are allowed, but not regular expressions.
:file_filters: "-./vendor/*;-./build/*;-./test/*;-./lib/*;+./src/*"
# The verbosity level of the log messages.
# Values: Verbose, Info, Warning, Error, Off
:verbosity: Warning
# Optional tag or build version.
:tag: <tag>
# Optional list of one or more regular expressions to exclude gcov notes files that match these filters.
:gcov_exclude:
- <exclude_regex1>
- <exclude_regex2>
# Optionally use a particular gcov executable. Defaults to gcov.
:gcov_executable: <gcov_cmd>
# Optionally set the number of threads to use in parallel. Defaults to 1.
:num_parallel_threads: <num_threads>
# Optional list of one or more command line arguments to pass to Report Generator.
# Useful for configuring Risk Hotspots and Other Settings.
# https://github.com/danielpalme/ReportGenerator/wiki/Settings
:custom_args:
- <custom_arg1>
- <custom_arg2>
```
## Example Usage
```sh
ceedling gcov:all utils:gcov
```
## To-Do list
- Generate overall report (combined statistics from all files with coverage)
## Citations
Most of the comment text which describes the options was taken from the
[Gcovr User Guide](https://www.gcovr.com/en/stable/guide.html) and the
[ReportGenerator Wiki](https://github.com/danielpalme/ReportGenerator/wiki).
The text is repeated here to provide the most accurate option functionality.

View File

@@ -0,0 +1,15 @@
% function_string = hash[:coverage][:functions].to_s
% branch_string = hash[:coverage][:branches].to_s
% format_string = "%#{[function_string.length, branch_string.length].max}i"
<%=@ceedling[:plugin_reportinator].generate_banner("#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY")%>
% if (!hash[:coverage][:functions].nil?)
FUNCTIONS: <%=sprintf(format_string, hash[:coverage][:functions])%>%
% else
FUNCTIONS: none
% end
% if (!hash[:coverage][:branches].nil?)
BRANCHES: <%=sprintf(format_string, hash[:coverage][:branches])%>%
% else
BRANCHES: none
% end

View File

@@ -0,0 +1,118 @@
DEFAULT_GCOV_COMPILER_TOOL = {
:executable => ENV['CC'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CC'].split[0],
:name => 'default_gcov_compiler'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze,
:arguments => [
"-g".freeze,
"-fprofile-arcs".freeze,
"-ftest-coverage".freeze,
ENV['CC'].nil? ? "" : ENV['CC'].split[1..-1],
ENV['CPPFLAGS'].nil? ? "" : ENV['CPPFLAGS'].split,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
{"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
{"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
"-DGCOV_COMPILER".freeze,
"-DCODE_COVERAGE".freeze,
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
"-c \"${1}\"".freeze,
"-o \"${2}\"".freeze
].freeze
}
DEFAULT_GCOV_LINKER_TOOL = {
:executable => ENV['CCLD'].nil? ? FilePathUtils.os_executable_ext('gcc').freeze : ENV['CCLD'].split[0],
:name => 'default_gcov_linker'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze,
:arguments => [
"-g".freeze,
"-fprofile-arcs".freeze,
"-ftest-coverage".freeze,
ENV['CCLD'].nil? ? "" : ENV['CCLD'].split[1..-1],
ENV['CFLAGS'].nil? ? "" : ENV['CFLAGS'].split,
ENV['LDFLAGS'].nil? ? "" : ENV['LDFLAGS'].split,
"\"${1}\"".freeze,
"-o \"${2}\"".freeze,
"${4}".freeze,
"${5}".freeze,
ENV['LDLIBS'].nil? ? "" : ENV['LDLIBS'].split
].freeze
}
DEFAULT_GCOV_FIXTURE_TOOL = {
:executable => '${1}'.freeze,
:name => 'default_gcov_fixture'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze,
:arguments => [].freeze
}
DEFAULT_GCOV_REPORT_TOOL = {
:executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0],
:name => 'default_gcov_report'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => false.freeze,
:arguments => [
"-n".freeze,
"-p".freeze,
"-b".freeze,
{"-o \"$\"" => 'GCOV_BUILD_OUTPUT_PATH'}.freeze,
"\"${1}\"".freeze
].freeze
}
DEFAULT_GCOV_GCOV_POST_REPORT_TOOL = {
:executable => ENV['GCOV'].nil? ? FilePathUtils.os_executable_ext('gcov').freeze : ENV['GCOV'].split[0],
:name => 'default_gcov_gcov_post_report'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => true.freeze,
:arguments => [
"-b".freeze,
"-c".freeze,
"-r".freeze,
"-x".freeze,
"${1}".freeze
].freeze
}
DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL = {
:executable => 'gcovr'.freeze,
:name => 'default_gcov_gcovr_post_report'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => true.freeze,
:arguments => [
"${1}".freeze
].freeze
}
DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT = {
:executable => 'reportgenerator'.freeze,
:name => 'default_gcov_reportgenerator_post_report'.freeze,
:stderr_redirect => StdErrRedirect::NONE.freeze,
:background_exec => BackgroundExec::NONE.freeze,
:optional => true.freeze,
:arguments => [
"${1}".freeze
].freeze
}
def get_default_config
return :tools => {
:gcov_compiler => DEFAULT_GCOV_COMPILER_TOOL,
:gcov_linker => DEFAULT_GCOV_LINKER_TOOL,
:gcov_fixture => DEFAULT_GCOV_FIXTURE_TOOL,
:gcov_report => DEFAULT_GCOV_REPORT_TOOL,
:gcov_gcov_post_report => DEFAULT_GCOV_GCOV_POST_REPORT_TOOL,
:gcov_gcovr_post_report => DEFAULT_GCOV_GCOVR_POST_REPORT_TOOL,
:gcov_reportgenerator_post_report => DEFAULT_GCOV_REPORTGENERATOR_POST_REPORT
}
end

View File

@@ -0,0 +1,209 @@
require 'reportgenerator_reportinator'
require 'gcovr_reportinator'
directory(GCOV_BUILD_OUTPUT_PATH)
directory(GCOV_RESULTS_PATH)
directory(GCOV_ARTIFACTS_PATH)
directory(GCOV_DEPENDENCIES_PATH)
CLEAN.include(File.join(GCOV_BUILD_OUTPUT_PATH, '*'))
CLEAN.include(File.join(GCOV_RESULTS_PATH, '*'))
CLEAN.include(File.join(GCOV_ARTIFACTS_PATH, '*'))
CLEAN.include(File.join(GCOV_DEPENDENCIES_PATH, '*'))
CLOBBER.include(File.join(GCOV_BUILD_PATH, '**/*'))
rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_OBJECT}$/ => [
proc do |task_name|
@ceedling[:file_finder].find_compilation_input_file(task_name)
end
]) do |object|
if File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX})|(#{VENDORS_FILES.map{|source| '\b' + source + '\b'}.join('|')})/
@ceedling[:generator].generate_object_file(
TOOLS_GCOV_COMPILER,
OPERATION_COMPILE_SYM,
GCOV_SYM,
object.source,
object.name,
@ceedling[:file_path_utils].form_test_build_list_filepath(object.name)
)
else
@ceedling[GCOV_SYM].generate_coverage_object_file(object.source, object.name)
end
end
rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\' + EXTENSION_EXECUTABLE}$/) do |bin_file|
lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments()
lib_paths = @ceedling[:test_invoker].get_library_paths_to_arguments()
@ceedling[:generator].generate_executable_file(
TOOLS_GCOV_LINKER,
GCOV_SYM,
bin_file.prerequisites,
bin_file.name,
@ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name),
lib_args,
lib_paths
)
end
rule(/#{GCOV_RESULTS_PATH}\/#{'.+\\' + EXTENSION_TESTPASS}$/ => [
proc do |task_name|
@ceedling[:file_path_utils].form_test_executable_filepath(task_name)
end
]) do |test_result|
@ceedling[:generator].generate_test_results(TOOLS_GCOV_FIXTURE, GCOV_SYM, test_result.source, test_result.name)
end
rule(/#{GCOV_DEPENDENCIES_PATH}\/#{'.+\\' + EXTENSION_DEPENDENCIES}$/ => [
proc do |task_name|
@ceedling[:file_finder].find_compilation_input_file(task_name)
end
]) do |dep|
@ceedling[:generator].generate_dependencies_file(
TOOLS_TEST_DEPENDENCIES_GENERATOR,
GCOV_SYM,
dep.source,
File.join(GCOV_BUILD_OUTPUT_PATH, File.basename(dep.source).ext(EXTENSION_OBJECT)),
dep.name
)
end
task directories: [GCOV_BUILD_OUTPUT_PATH, GCOV_RESULTS_PATH, GCOV_DEPENDENCIES_PATH, GCOV_ARTIFACTS_PATH]
namespace GCOV_SYM do
task source_coverage: COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}")
desc 'Run code coverage for all tests'
task all: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM)
@ceedling[:configurator].restore_config
end
desc 'Run single test w/ coverage ([*] real test or source file name, no path).'
task :* do
message = "\nOops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " \
"Use a real test or source file name (no path) in place of the wildcard.\n" \
"Example: rake #{GCOV_ROOT_NAME}:foo.c\n\n"
@ceedling[:streaminator].stdout_puts(message)
end
desc 'Run tests by matching regular expression pattern.'
task :pattern, [:regex] => [:test_deps] do |_t, args|
matches = []
COLLECTION_ALL_TESTS.each do |test|
matches << test if test =~ /#{args.regex}/
end
if !matches.empty?
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, force_run: false)
@ceedling[:configurator].restore_config
else
@ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.")
end
end
desc 'Run tests whose test path contains [dir] or [dir] substring.'
task :path, [:dir] => [:test_deps] do |_t, args|
matches = []
COLLECTION_ALL_TESTS.each do |test|
matches << test if File.dirname(test).include?(args.dir.tr('\\', '/'))
end
if !matches.empty?
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, force_run: false)
@ceedling[:configurator].restore_config
else
@ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.")
end
end
desc 'Run code coverage for changed files'
task delta: [:test_deps] do
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, force_run: false)
@ceedling[:configurator].restore_config
end
# use a rule to increase efficiency for large projects
# gcov test tasks by regex
rule(/^#{GCOV_TASK_ROOT}\S+$/ => [
proc do |task_name|
test = task_name.sub(/#{GCOV_TASK_ROOT}/, '')
test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" unless test.start_with?(PROJECT_TEST_FILE_PREFIX)
@ceedling[:file_finder].find_test_from_file_path(test)
end
]) do |test|
@ceedling[:rake_wrapper][:test_deps].invoke
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM)
@ceedling[:configurator].restore_config
end
end
if PROJECT_USE_DEEP_DEPENDENCIES
namespace REFRESH_SYM do
task GCOV_SYM do
@ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
@ceedling[:test_invoker].refresh_deep_dependencies
@ceedling[:configurator].restore_config
end
end
end
namespace UTILS_SYM do
# Report Creation Utilities
UTILITY_NAME_GCOVR = "gcovr"
UTILITY_NAME_REPORT_GENERATOR = "ReportGenerator"
UTILITY_NAMES = [UTILITY_NAME_GCOVR, UTILITY_NAME_REPORT_GENERATOR]
# Returns true is the given utility is enabled, otherwise returns false.
def is_utility_enabled(opts, utility_name)
return !(opts.nil?) && !(opts[:gcov_utilities].nil?) && (opts[:gcov_utilities].map(&:upcase).include? utility_name.upcase)
end
desc "Create gcov code coverage html/xml/json/text report(s). (Note: Must run 'ceedling gcov' first)."
task GCOV_SYM do
# Get the gcov options from project.yml.
opts = @ceedling[:configurator].project_config_hash
# Create the artifacts output directory.
if !File.directory? GCOV_ARTIFACTS_PATH
FileUtils.mkdir_p GCOV_ARTIFACTS_PATH
end
# Remove unsupported reporting utilities.
if !(opts[:gcov_utilities].nil?)
opts[:gcov_utilities].reject! { |item| !(UTILITY_NAMES.map(&:upcase).include? item.upcase) }
end
# Default to gcovr when no reporting utilities are specified.
if opts[:gcov_utilities].nil? || opts[:gcov_utilities].empty?
opts[:gcov_utilities] = [UTILITY_NAME_GCOVR]
end
if opts[:gcov_reports].nil?
opts[:gcov_reports] = []
end
gcovr_reportinator = GcovrReportinator.new(@ceedling)
gcovr_reportinator.support_deprecated_options(opts)
if is_utility_enabled(opts, UTILITY_NAME_GCOVR)
gcovr_reportinator.make_reports(opts)
end
if is_utility_enabled(opts, UTILITY_NAME_REPORT_GENERATOR)
reportgenerator_reportinator = ReportGeneratorReportinator.new(@ceedling)
reportgenerator_reportinator.make_reports(opts)
end
end
end

View File

@@ -0,0 +1,136 @@
require 'ceedling/plugin'
require 'ceedling/constants'
require 'gcov_constants'
class Gcov < Plugin
attr_reader :config
def setup
@result_list = []
@config = {
project_test_build_output_path: GCOV_BUILD_OUTPUT_PATH,
project_test_build_output_c_path: GCOV_BUILD_OUTPUT_PATH,
project_test_results_path: GCOV_RESULTS_PATH,
project_test_dependencies_path: GCOV_DEPENDENCIES_PATH,
defines_test: DEFINES_TEST + ['CODE_COVERAGE'],
gcov_html_report_filter: GCOV_FILTER_EXCLUDE
}
@plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
@coverage_template_all = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb'))
end
def generate_coverage_object_file(source, object)
lib_args = @ceedling[:test_invoker].convert_libraries_to_arguments()
compile_command =
@ceedling[:tool_executor].build_command_line(
TOOLS_GCOV_COMPILER,
@ceedling[:flaginator].flag_down(OPERATION_COMPILE_SYM, GCOV_SYM, source),
source,
object,
@ceedling[:file_path_utils].form_test_build_list_filepath(object),
lib_args
)
@ceedling[:streaminator].stdout_puts("Compiling #{File.basename(source)} with coverage...")
@ceedling[:tool_executor].exec(compile_command[:line], compile_command[:options])
end
def post_test_fixture_execute(arg_hash)
result_file = arg_hash[:result_file]
if (result_file =~ /#{GCOV_RESULTS_PATH}/) && !@result_list.include?(result_file)
@result_list << arg_hash[:result_file]
end
end
def post_build
return unless @ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/)
# test results
results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list)
hash = {
header: GCOV_ROOT_NAME.upcase,
results: results
}
@ceedling[:plugin_reportinator].run_test_results_report(hash) do
message = ''
message = 'Unit test failures.' if results[:counts][:failed] > 0
message
end
report_per_file_coverage_results(@ceedling[:test_invoker].sources)
end
def summary
result_list = @ceedling[:file_path_utils].form_pass_results_filelist(GCOV_RESULTS_PATH, COLLECTION_ALL_TESTS)
# test results
# get test results for only those tests in our configuration and of those only tests with results on disk
hash = {
header: GCOV_ROOT_NAME.upcase,
results: @ceedling[:plugin_reportinator].assemble_test_results(result_list, boom: false)
}
@ceedling[:plugin_reportinator].run_test_results_report(hash)
end
private ###################################
def report_per_file_coverage_results(sources)
banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY"
@ceedling[:streaminator].stdout_puts "\n" + banner
coverage_sources = @ceedling[:project_config_manager].filter_internal_sources(sources)
coverage_sources.each do |source|
basename = File.basename(source)
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, [], [basename])
shell_results = @ceedling[:tool_executor].exec(command[:line], command[:options])
coverage_results = shell_results[:output]
if coverage_results.strip =~ /(File\s+'#{Regexp.escape(source)}'.+$)/m
report = Regexp.last_match(1).lines.to_a[1..-1].map { |line| basename + ' ' + line }.join('')
@ceedling[:streaminator].stdout_puts(report + "\n\n")
end
end
ignore_path_list = @ceedling[:file_system_utils].collect_paths(@ceedling[:configurator].project_config_hash[:gcov_uncovered_ignore_list] || [])
ignore_uncovered_list = @ceedling[:file_wrapper].instantiate_file_list
ignore_path_list.each do |path|
if File.exists?(path) and not File.directory?(path)
ignore_uncovered_list.include(path)
else
ignore_uncovered_list.include(File.join(path, "*#{EXTENSION_SOURCE}"))
end
end
found_uncovered = false
COLLECTION_ALL_SOURCE.each do |source|
unless coverage_sources.include?(source)
v = Verbosity::DEBUG
msg = "Could not find coverage results for " + source
if ignore_uncovered_list.include?(source)
msg += " [IGNORED]"
else
found_uncovered = true
v = Verbosity::NORMAL
end
msg += "\n"
@ceedling[:streaminator].stdout_puts(msg, v)
end
end
if found_uncovered
if @ceedling[:configurator].project_config_hash[:gcov_abort_on_uncovered]
@ceedling[:streaminator].stderr_puts("There were files with no coverage results: aborting.\n")
exit(-1)
end
end
end
end
# end blocks always executed following rake run
END {
# cache our input configurations to use in comparison upon next execution
@ceedling[:cacheinator].cache_test_config(@ceedling[:setupinator].config_hash) if @ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/)
}

View File

@@ -0,0 +1,48 @@
GCOV_ROOT_NAME = 'gcov'.freeze
GCOV_TASK_ROOT = GCOV_ROOT_NAME + ':'
GCOV_SYM = GCOV_ROOT_NAME.to_sym
GCOV_BUILD_PATH = File.join(PROJECT_BUILD_ROOT, GCOV_ROOT_NAME)
GCOV_BUILD_OUTPUT_PATH = File.join(GCOV_BUILD_PATH, "out")
GCOV_RESULTS_PATH = File.join(GCOV_BUILD_PATH, "results")
GCOV_DEPENDENCIES_PATH = File.join(GCOV_BUILD_PATH, "dependencies")
GCOV_ARTIFACTS_PATH = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, GCOV_ROOT_NAME)
GCOV_REPORT_GENERATOR_PATH = File.join(GCOV_ARTIFACTS_PATH, "ReportGenerator")
GCOV_ARTIFACTS_FILE_HTML = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageResults.html")
GCOV_ARTIFACTS_FILE_COBERTURA = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageCobertura.xml")
GCOV_ARTIFACTS_FILE_SONARQUBE = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverageSonarQube.xml")
GCOV_ARTIFACTS_FILE_JSON = File.join(GCOV_ARTIFACTS_PATH, "GcovCoverage.json")
GCOV_FILTER_EXCLUDE_PATHS = ['vendor', 'build', 'test', 'lib']
# gcovr supports regular expressions.
GCOV_FILTER_EXCLUDE = GCOV_FILTER_EXCLUDE_PATHS.map{|path| '^'.concat(*path).concat('.*')}.join('|')
# ReportGenerator supports text with wildcard characters.
GCOV_REPORT_GENERATOR_FILE_FILTERS = GCOV_FILTER_EXCLUDE_PATHS.map{|path| File.join('-.', *path, '*')}.join(';')
# Report Types
class ReportTypes
HTML_BASIC = "HtmlBasic"
HTML_DETAILED = "HtmlDetailed"
HTML_CHART = "HtmlChart"
HTML_INLINE = "HtmlInline"
HTML_INLINE_AZURE = "HtmlInlineAzure"
HTML_INLINE_AZURE_DARK = "HtmlInlineAzureDark"
MHTML = "MHtml"
TEXT = "Text"
COBERTURA = "Cobertura"
SONARQUBE = "SonarQube"
JSON = "JSON"
BADGES = "Badges"
CSV_SUMMARY = "CsvSummary"
LATEX = "Latex"
LATEX_SUMMARY = "LatexSummary"
PNG_CHART = "PngChart"
TEAM_CITY_SUMMARY = "TeamCitySummary"
LCOV = "lcov"
XML = "Xml"
XML_SUMMARY = "XmlSummary"
end

View File

@@ -0,0 +1,331 @@
require 'reportinator_helper'
class GcovrReportinator
def initialize(system_objects)
@ceedling = system_objects
@reportinator_helper = ReportinatorHelper.new
end
# Generate the gcovr report(s) specified in the options.
def make_reports(opts)
# Get the gcovr version number.
gcovr_version_info = get_gcovr_version()
# Build the common gcovr arguments.
args_common = args_builder_common(opts)
if ((gcovr_version_info[0] == 4) && (gcovr_version_info[1] >= 2)) || (gcovr_version_info[0] > 4)
# gcovr version 4.2 and later supports generating multiple reports with a single call.
args = args_common
args += args_builder_cobertura(opts, false)
args += args_builder_sonarqube(opts, false)
args += args_builder_json(opts, true)
# As of gcovr version 4.2, the --html argument must appear last.
args += args_builder_html(opts, false)
print "Creating gcov results report(s) in '#{GCOV_ARTIFACTS_PATH}'... "
STDOUT.flush
# Generate the report(s).
run(args)
else
# gcovr version 4.1 and earlier supports HTML and Cobertura XML reports.
# It does not support SonarQube and JSON reports.
# Reports must also be generated separately.
args_cobertura = args_builder_cobertura(opts, true)
args_html = args_builder_html(opts, true)
if args_html.length > 0
print "Creating a gcov HTML report in '#{GCOV_ARTIFACTS_PATH}'... "
STDOUT.flush
# Generate the HTML report.
run(args_common + args_html)
end
if args_cobertura.length > 0
print "Creating a gcov XML report in '#{GCOV_ARTIFACTS_PATH}'... "
STDOUT.flush
# Generate the Cobertura XML report.
run(args_common + args_cobertura)
end
end
# Determine if the gcovr text report is enabled. Defaults to disabled.
if is_report_enabled(opts, ReportTypes::TEXT)
make_text_report(opts, args_common)
end
end
def support_deprecated_options(opts)
# Support deprecated :html_report: and ":html_report_type: basic" options.
if !is_report_enabled(opts, ReportTypes::HTML_BASIC) && (opts[:gcov_html_report] || (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("basic") == 0))
opts[:gcov_reports].push(ReportTypes::HTML_BASIC)
end
# Support deprecated ":html_report_type: detailed" option.
if !is_report_enabled(opts, ReportTypes::HTML_DETAILED) && (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0)
opts[:gcov_reports].push(ReportTypes::HTML_DETAILED)
end
# Support deprecated :xml_report: option.
if opts[:gcov_xml_report]
opts[:gcov_reports].push(ReportTypes::COBERTURA)
end
# Default to HTML basic report when no report types are defined.
if opts[:gcov_reports].empty? && opts[:gcov_html_report_type].nil? && opts[:gcov_xml_report].nil?
opts[:gcov_reports] = [ReportTypes::HTML_BASIC]
puts "In your project.yml, define one or more of the"
puts "following to specify which reports to generate."
puts "For now, creating only an #{ReportTypes::HTML_BASIC} report."
puts ""
puts ":gcov:"
puts " :reports:"
puts " - #{ReportTypes::HTML_BASIC}"
puts " - #{ReportTypes::HTML_DETAILED}"
puts " - #{ReportTypes::TEXT}"
puts " - #{ReportTypes::COBERTURA}"
puts " - #{ReportTypes::SONARQUBE}"
puts " - #{ReportTypes::JSON}"
puts ""
end
end
private
GCOVR_SETTING_PREFIX = "gcov_gcovr"
# Build the gcovr report generation common arguments.
def args_builder_common(opts)
gcovr_opts = get_opts(opts)
args = ""
args += "--root \"#{gcovr_opts[:report_root] || '.'}\" "
args += "--config \"#{gcovr_opts[:config_file]}\" " unless gcovr_opts[:config_file].nil?
args += "--filter \"#{gcovr_opts[:report_include]}\" " unless gcovr_opts[:report_include].nil?
args += "--exclude \"#{gcovr_opts[:report_exclude] || GCOV_FILTER_EXCLUDE}\" "
args += "--gcov-filter \"#{gcovr_opts[:gcov_filter]}\" " unless gcovr_opts[:gcov_filter].nil?
args += "--gcov-exclude \"#{gcovr_opts[:gcov_exclude]}\" " unless gcovr_opts[:gcov_exclude].nil?
args += "--exclude-directories \"#{gcovr_opts[:exclude_directories]}\" " unless gcovr_opts[:exclude_directories].nil?
args += "--branches " if gcovr_opts[:branches].nil? || gcovr_opts[:branches] # Defaults to enabled.
args += "--sort-uncovered " if gcovr_opts[:sort_uncovered]
args += "--sort-percentage " if gcovr_opts[:sort_percentage].nil? || gcovr_opts[:sort_percentage] # Defaults to enabled.
args += "--print-summary " if gcovr_opts[:print_summary]
args += "--gcov-executable \"#{gcovr_opts[:gcov_executable]}\" " unless gcovr_opts[:gcov_executable].nil?
args += "--exclude-unreachable-branches " if gcovr_opts[:exclude_unreachable_branches]
args += "--exclude-throw-branches " if gcovr_opts[:exclude_throw_branches]
args += "--use-gcov-files " if gcovr_opts[:use_gcov_files]
args += "--gcov-ignore-parse-errors " if gcovr_opts[:gcov_ignore_parse_errors]
args += "--keep " if gcovr_opts[:keep]
args += "--delete " if gcovr_opts[:delete]
args += "-j #{gcovr_opts[:num_parallel_threads]} " if !(gcovr_opts[:num_parallel_threads].nil?) && (gcovr_opts[:num_parallel_threads].is_a? Integer)
[:fail_under_line, :fail_under_branch, :source_encoding, :object_directory].each do |opt|
unless gcovr_opts[opt].nil?
value = gcovr_opts[opt]
if (opt == :fail_under_line) || (opt == :fail_under_branch)
if not value.is_a? Integer
puts "Option value #{opt} has to be an integer"
value = nil
elsif (value < 0) || (value > 100)
puts "Option value #{opt} has to be a percentage from 0 to 100"
value = nil
end
end
args += "--#{opt.to_s.gsub('_','-')} #{value} " unless value.nil?
end
end
return args
end
# Build the gcovr Cobertura XML report generation arguments.
def args_builder_cobertura(opts, use_output_option=false)
gcovr_opts = get_opts(opts)
args = ""
# Determine if the Cobertura XML report is enabled. Defaults to disabled.
if is_report_enabled(opts, ReportTypes::COBERTURA)
# Determine the Cobertura XML report file name.
artifacts_file_cobertura = GCOV_ARTIFACTS_FILE_COBERTURA
if !(gcovr_opts[:cobertura_artifact_filename].nil?)
artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:cobertura_artifact_filename])
elsif !(gcovr_opts[:xml_artifact_filename].nil?)
artifacts_file_cobertura = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:xml_artifact_filename])
end
args += "--xml-pretty " if gcovr_opts[:xml_pretty] || gcovr_opts[:cobertura_pretty]
args += "--xml #{use_output_option ? "--output " : ""} \"#{artifacts_file_cobertura}\" "
end
return args
end
# Build the gcovr SonarQube report generation arguments.
def args_builder_sonarqube(opts, use_output_option=false)
gcovr_opts = get_opts(opts)
args = ""
# Determine if the gcovr SonarQube XML report is enabled. Defaults to disabled.
if is_report_enabled(opts, ReportTypes::SONARQUBE)
# Determine the SonarQube XML report file name.
artifacts_file_sonarqube = GCOV_ARTIFACTS_FILE_SONARQUBE
if !(gcovr_opts[:sonarqube_artifact_filename].nil?)
artifacts_file_sonarqube = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:sonarqube_artifact_filename])
end
args += "--sonarqube #{use_output_option ? "--output " : ""} \"#{artifacts_file_sonarqube}\" "
end
return args
end
# Build the gcovr JSON report generation arguments.
def args_builder_json(opts, use_output_option=false)
gcovr_opts = get_opts(opts)
args = ""
# Determine if the gcovr JSON report is enabled. Defaults to disabled.
if is_report_enabled(opts, ReportTypes::JSON)
# Determine the JSON report file name.
artifacts_file_json = GCOV_ARTIFACTS_FILE_JSON
if !(gcovr_opts[:json_artifact_filename].nil?)
artifacts_file_json = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:json_artifact_filename])
end
args += "--json-pretty " if gcovr_opts[:json_pretty]
# Note: In gcovr 4.2, the JSON report is output only when the --output option is specified.
# Hopefully we can remove --output after a future gcovr release.
args += "--json #{use_output_option ? "--output " : ""} \"#{artifacts_file_json}\" "
end
return args
end
# Build the gcovr HTML report generation arguments.
def args_builder_html(opts, use_output_option=false)
gcovr_opts = get_opts(opts)
args = ""
# Determine if the gcovr HTML report is enabled. Defaults to enabled.
html_enabled = (opts[:gcov_html_report].nil? && opts[:gcov_reports].empty?) ||
is_report_enabled(opts, ReportTypes::HTML_BASIC) ||
is_report_enabled(opts, ReportTypes::HTML_DETAILED)
if html_enabled
# Determine the HTML report file name.
artifacts_file_html = GCOV_ARTIFACTS_FILE_HTML
if !(gcovr_opts[:html_artifact_filename].nil?)
artifacts_file_html = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:html_artifact_filename])
end
is_html_report_type_detailed = (opts[:gcov_html_report_type].is_a? String) && (opts[:gcov_html_report_type].casecmp("detailed") == 0)
args += "--html-details " if is_html_report_type_detailed || is_report_enabled(opts, ReportTypes::HTML_DETAILED)
args += "--html-title \"#{gcovr_opts[:html_title]}\" " unless gcovr_opts[:html_title].nil?
args += "--html-absolute-paths " if !(gcovr_opts[:html_absolute_paths].nil?) && gcovr_opts[:html_absolute_paths]
args += "--html-encoding \"#{gcovr_opts[:html_encoding]}\" " unless gcovr_opts[:html_encoding].nil?
[:html_medium_threshold, :html_high_threshold].each do |opt|
args += "--#{opt.to_s.gsub('_','-')} #{gcovr_opts[opt]} " unless gcovr_opts[opt].nil?
end
# The following option must be appended last for gcovr version <= 4.2 to properly work.
args += "--html #{use_output_option ? "--output " : ""} \"#{artifacts_file_html}\" "
end
return args
end
# Generate a gcovr text report.
def make_text_report(opts, args_common)
gcovr_opts = get_opts(opts)
args_text = ""
message_text = "Creating a gcov text report"
if !(gcovr_opts[:text_artifact_filename].nil?)
artifacts_file_txt = File.join(GCOV_ARTIFACTS_PATH, gcovr_opts[:text_artifact_filename])
args_text += "--output \"#{artifacts_file_txt}\" "
message_text += " in '#{GCOV_ARTIFACTS_PATH}'... "
else
message_text += "... "
end
print message_text
STDOUT.flush
# Generate the text report.
run(args_common + args_text)
end
# Get the gcovr options from the project options.
def get_opts(opts)
return opts[GCOVR_SETTING_PREFIX.to_sym] || {}
end
# Run gcovr with the given arguments.
def run(args)
begin
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], args)
shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options])
@reportinator_helper.print_shell_result(shell_result)
rescue
# handle any unforeseen issues with called tool
exitcode = $?.exitstatus
show_gcovr_message(exitcode)
exit(exitcode)
end
end
# Get the gcovr version number as components.
# Returns [major, minor].
def get_gcovr_version()
version_number_major = 0
version_number_minor = 0
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_GCOVR_POST_REPORT, [], "--version")
shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options])
version_number_match_data = shell_result[:output].match(/gcovr ([0-9]+)\.([0-9]+)/)
if !(version_number_match_data.nil?) && !(version_number_match_data[1].nil?) && !(version_number_match_data[2].nil?)
version_number_major = version_number_match_data[1].to_i
version_number_minor = version_number_match_data[2].to_i
end
return version_number_major, version_number_minor
end
# Show a more human-friendly message on gcovr return code
def show_gcovr_message(exitcode)
if ((exitcode & 2) == 2)
puts "The line coverage is less than the minimum"
end
if ((exitcode & 4) == 4)
puts "The branch coverage is less than the minimum"
end
end
# Returns true if the given report type is enabled, otherwise returns false.
def is_report_enabled(opts, report_type)
return !(opts.nil?) && !(opts[:gcov_reports].nil?) && (opts[:gcov_reports].map(&:upcase).include? report_type.upcase)
end
end

View File

@@ -0,0 +1,195 @@
require 'benchmark'
require 'reportinator_helper'
class ReportGeneratorReportinator
def initialize(system_objects)
@ceedling = system_objects
@reportinator_helper = ReportinatorHelper.new
end
# Generate the ReportGenerator report(s) specified in the options.
def make_reports(opts)
shell_result = nil
total_time = Benchmark.realtime do
rg_opts = get_opts(opts)
print "Creating gcov results report(s) with ReportGenerator in '#{GCOV_REPORT_GENERATOR_PATH}'... "
STDOUT.flush
# Cleanup any existing .gcov files to avoid reporting old coverage results.
for gcov_file in Dir.glob("*.gcov")
File.delete(gcov_file)
end
# Use a custom gcov executable, if specified.
GCOV_TOOL_CONFIG[:executable] = rg_opts[:gcov_executable] unless rg_opts[:gcov_executable].nil?
# Avoid running gcov on the mock, test, unity, and cexception gcov notes files to save time.
gcno_exclude_str = "#{opts[:cmock_mock_prefix]}.*"
gcno_exclude_str += "|#{opts[:project_test_file_prefix]}.*"
gcno_exclude_str += "|#{VENDORS_FILES.join('|')}"
# Avoid running gcov on custom specified .gcno files.
if !(rg_opts.nil?) && !(rg_opts[:gcov_exclude].nil?) && !(rg_opts[:gcov_exclude].empty?)
for gcno_exclude_expression in rg_opts[:gcov_exclude]
if !(gcno_exclude_expression.nil?) && !(gcno_exclude_expression.empty?)
# We want to filter .gcno files, not .gcov files.
# We will generate .gcov files from .gcno files.
gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcov")
gcno_exclude_expression = gcno_exclude_expression.chomp(".gcov")
# The .gcno extension will be added later as we create the regex.
gcno_exclude_expression = gcno_exclude_expression.chomp("\\.gcno")
gcno_exclude_expression = gcno_exclude_expression.chomp(".gcno")
# Append the custom expression.
gcno_exclude_str += "|#{gcno_exclude_expression}"
end
end
end
gcno_exclude_regex = /(\/|\\)(#{gcno_exclude_str})\.gcno/
# Generate .gcov files by running gcov on gcov notes files (*.gcno).
for gcno_filepath in Dir.glob(File.join(GCOV_BUILD_PATH, "**", "*.gcno"))
match_data = gcno_filepath.match(gcno_exclude_regex)
if match_data.nil? || (match_data[1].nil? && match_data[1].nil?)
# Ensure there is a matching gcov data file.
if File.file?(gcno_filepath.gsub(".gcno", ".gcda"))
run_gcov("\"#{gcno_filepath}\"")
end
end
end
if Dir.glob("*.gcov").length > 0
# Build the command line arguments.
args = args_builder(opts)
# Generate the report(s).
shell_result = run(args)
else
puts "\nWarning: No matching .gcno coverage files found."
end
# Cleanup .gcov files.
for gcov_file in Dir.glob("*.gcov")
File.delete(gcov_file)
end
end
if shell_result
shell_result[:time] = total_time
@reportinator_helper.print_shell_result(shell_result)
end
end
private
# A dictionary of report types defined in this plugin to ReportGenerator report types.
REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME = {
ReportTypes::HTML_BASIC.upcase => "HtmlSummary",
ReportTypes::HTML_DETAILED.upcase => "Html",
ReportTypes::HTML_CHART.upcase => "HtmlChart",
ReportTypes::HTML_INLINE.upcase => "HtmlInline",
ReportTypes::HTML_INLINE_AZURE.upcase => "HtmlInline_AzurePipelines",
ReportTypes::HTML_INLINE_AZURE_DARK.upcase => "HtmlInline_AzurePipelines_Dark",
ReportTypes::MHTML.upcase => "MHtml",
ReportTypes::TEXT.upcase => "TextSummary",
ReportTypes::COBERTURA.upcase => "Cobertura",
ReportTypes::SONARQUBE.upcase => "SonarQube",
ReportTypes::BADGES.upcase => "Badges",
ReportTypes::CSV_SUMMARY.upcase => "CsvSummary",
ReportTypes::LATEX.upcase => "Latex",
ReportTypes::LATEX_SUMMARY.upcase => "LatexSummary",
ReportTypes::PNG_CHART.upcase => "PngChart",
ReportTypes::TEAM_CITY_SUMMARY.upcase => "TeamCitySummary",
ReportTypes::LCOV.upcase => "lcov",
ReportTypes::XML.upcase => "Xml",
ReportTypes::XML_SUMMARY.upcase => "XmlSummary",
}
REPORT_GENERATOR_SETTING_PREFIX = "gcov_report_generator"
# Deep clone the gcov tool config, so we can modify it locally if specified via options.
GCOV_TOOL_CONFIG = Marshal.load(Marshal.dump(TOOLS_GCOV_GCOV_POST_REPORT))
# Build the ReportGenerator arguments.
def args_builder(opts)
rg_opts = get_opts(opts)
report_type_count = 0
args = ""
args += "\"-reports:*.gcov\" "
args += "\"-targetdir:\"#{GCOV_REPORT_GENERATOR_PATH}\"\" "
# Build the report types argument.
if !(opts.nil?) && !(opts[:gcov_reports].nil?) && !(opts[:gcov_reports].empty?)
args += "\"-reporttypes:"
for report_type in opts[:gcov_reports]
rg_report_type = REPORT_TYPE_TO_REPORT_GENERATOR_REPORT_NAME[report_type.upcase]
if !(rg_report_type.nil?)
args += rg_report_type + ";"
report_type_count = report_type_count + 1
end
end
# Removing trailing ';' after the last report type.
args = args.chomp(";")
# Append a space seperator after the report type.
args += "\" "
end
# Build the source directories argument.
args += "\"-sourcedirs:.;"
if !(opts[:collection_paths_source].nil?)
args += opts[:collection_paths_source].join(';')
end
args = args.chomp(";")
args += "\" "
args += "\"-historydir:#{rg_opts[:history_directory]}\" " unless rg_opts[:history_directory].nil?
args += "\"-plugins:#{rg_opts[:plugins]}\" " unless rg_opts[:plugins].nil?
args += "\"-assemblyfilters:#{rg_opts[:assembly_filters]}\" " unless rg_opts[:assembly_filters].nil?
args += "\"-classfilters:#{rg_opts[:class_filters]}\" " unless rg_opts[:class_filters].nil?
file_filters = rg_opts[:file_filters] || @ceedling[:tool_executor_helper].osify_path_separators(GCOV_REPORT_GENERATOR_FILE_FILTERS)
args += "\"-filefilters:#{file_filters}\" "
args += "\"-verbosity:#{rg_opts[:verbosity] || "Warning"}\" "
args += "\"-tag:#{rg_opts[:tag]}\" " unless rg_opts[:tag].nil?
args += "\"settings:createSubdirectoryForAllReportTypes=true\" " unless report_type_count <= 1
args += "\"settings:numberOfReportsParsedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil?
args += "\"settings:numberOfReportsMergedInParallel=#{rg_opts[:num_parallel_threads]}\" " unless rg_opts[:num_parallel_threads].nil?
# Append custom arguments.
if !(rg_opts[:custom_args].nil?) && !(rg_opts[:custom_args].empty?)
for custom_arg in rg_opts[:custom_args]
args += "\"#{custom_arg}\" " unless custom_arg.nil? || custom_arg.empty?
end
end
return args
end
# Get the ReportGenerator options from the project options.
def get_opts(opts)
return opts[REPORT_GENERATOR_SETTING_PREFIX.to_sym] || {}
end
# Run ReportGenerator with the given arguments.
def run(args)
command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORTGENERATOR_POST_REPORT, [], args)
return @ceedling[:tool_executor].exec(command[:line], command[:options])
end
# Run gcov with the given arguments.
def run_gcov(args)
command = @ceedling[:tool_executor].build_command_line(GCOV_TOOL_CONFIG, [], args)
return @ceedling[:tool_executor].exec(command[:line], command[:options])
end
end

View File

@@ -0,0 +1,15 @@
class ReportinatorHelper
# Output the shell result to the console.
def print_shell_result(shell_result)
if !(shell_result.nil?)
puts "Done in %.3f seconds." % shell_result[:time]
if !(shell_result[:output].nil?) && (shell_result[:output].length > 0)
puts shell_result[:output]
end
end
end
end