From b6f775a0f4c5d69ab96879510a348b673fd9d628 Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 27 May 2020 22:33:21 -0400 Subject: [PATCH] New option --display-only-failed for junit format Speeds up test report processing for large codebases and helps address the sorts of concerns raised at https://github.com/mikian/rubocop-junit-formatter/issues/18. Refactors file_finished method in junit_formatter.rb to reduce complexity. Fixes buggy error message when --disable-uncorrectable is run without --auto-correct, which I noticed because I added similar error messaging when you try to run --display-only-failed without --format junit Tests, CHANGELOG.md, doc updates, and help message updates to accompany new option. --- CHANGELOG.md | 2 ++ docs/modules/ROOT/pages/formatters.adoc | 13 +++++++++++++ legacy-docs/formatters.md | 14 +++++++++++++- lib/rubocop/formatter/junit_formatter.rb | 18 ++++++++++++++---- lib/rubocop/options.rb | 16 ++++++++++++++-- spec/rubocop/options_spec.rb | 14 ++++++++++++++ 6 files changed, 70 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e09889306..c04d0bd8169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * [#7908](https://github.com/rubocop-hq/rubocop/pull/7908): Add new `Style/RedundantRegexpEscape` cop. ([@owst][]) * [#7978](https://github.com/rubocop-hq/rubocop/pull/7978): Add new option `OnlyFor` to the `Bundler/GemComment` cop. ([@ric2b][]) * [#8063](https://github.com/rubocop-hq/rubocop/issues/8063): Add new `AllowedNames` option for `Naming/ClassAndModuleCamelCase`. ([@tejasbubane][]) +* New option `--display-only-failed` that can be used with `--format junit`. Speeds up test report processing for large codebases and helps address the sorts of concerns raised at [mikian/rubocop-junit-formatter #18](https://github.com/mikian/rubocop-junit-formatter/issues/18). ([@burnettk][]) ### Bug fixes @@ -4558,3 +4559,4 @@ [@CvX]: https://github.com/CvX [@jschneid]: https://github.com/jschneid [@ric2b]: https://github.com/ric2b +[@burnettk]: https://github.com/burnettk diff --git a/docs/modules/ROOT/pages/formatters.adoc b/docs/modules/ROOT/pages/formatters.adoc index d4b40be8cd6..e76d52bd56f 100644 --- a/docs/modules/ROOT/pages/formatters.adoc +++ b/docs/modules/ROOT/pages/formatters.adoc @@ -280,6 +280,19 @@ $ rubocop --format junit ---- +The `junit` style formatter is very useful for continuous integration systems +such as Jenkins, most of which support junit formatting when parsing test +results. A typical invocation in this type of scenario might look like: + +[source,sh] +---- +$ rubocop --format junit --out test-reports/junit.xml +---- + +Since there is one XML node for each cop for each file, the size of the resulting +XML can get quite large. If it is too large for you, you can restrict the output +to just failures by adding the `--display-only-failed` option. + == Offense Count Formatter Sometimes when first applying RuboCop to a codebase, it's nice to be able to diff --git a/legacy-docs/formatters.md b/legacy-docs/formatters.md index 454c9663c76..6e2e1fee665 100644 --- a/legacy-docs/formatters.md +++ b/legacy-docs/formatters.md @@ -242,7 +242,7 @@ The JSON structure is like the following example: **Machine-parsable** The `junit` style formatter provides the JUnit formatting. -This formatter is based on [rubocop-junit-formatter gem](https://github.com/mikian/rubocop-junit-formatter). +This formatter is based on the [rubocop-junit-formatter gem](https://github.com/mikian/rubocop-junit-formatter). ```sh $ rubocop --format junit @@ -268,6 +268,18 @@ $ rubocop --format junit ``` +The `junit` style formatter is very useful for continuous integration systems +such as Jenkins, most of which support junit formatting when parsing test +results. A typical invocation in this type of scenario might look like: + +```sh +$ rubocop --format junit --out test-reports/junit.xml +``` + +Since there is one XML node for each cop for each file, the size of the resulting +XML can get quite large. If it is too large for you, you can restrict the output +to just failures by adding the `--display-only-failed` option. + ## Offense Count Formatter Sometimes when first applying RuboCop to a codebase, it's nice to be able to diff --git a/lib/rubocop/formatter/junit_formatter.rb b/lib/rubocop/formatter/junit_formatter.rb index fe20f09d23a..d048ae636db 100644 --- a/lib/rubocop/formatter/junit_formatter.rb +++ b/lib/rubocop/formatter/junit_formatter.rb @@ -35,19 +35,29 @@ def file_finished(file, offenses) # # In the future, it would be preferable to return only enabled cops. Cop::Cop.all.each do |cop| + target_offenses = offenses_for_cop(offenses, cop) + + next unless relevant_for_output?(options, target_offenses) + REXML::Element.new('testcase', @testsuite).tap do |testcase| testcase.attributes['classname'] = classname_attribute_value(file) testcase.attributes['name'] = cop.cop_name - target_offenses = offenses.select do |offense| - offense.cop_name == cop.cop_name - end - add_failure_to(testcase, target_offenses, cop.cop_name) end end end + def relevant_for_output?(options, target_offenses) + !options[:display_only_failed] || target_offenses.any? + end + + def offenses_for_cop(all_offenses, cop) + all_offenses.select do |offense| + offense.cop_name == cop.cop_name + end + end + def classname_attribute_value(file) file.gsub(/\.rb\Z/, '').gsub("#{Dir.pwd}/", '').tr('/', '.') end diff --git a/lib/rubocop/options.rb b/lib/rubocop/options.rb index b49e50d405a..4edddbd1eb3 100644 --- a/lib/rubocop/options.rb +++ b/lib/rubocop/options.rb @@ -143,6 +143,8 @@ def add_formatting_options(opts) @options[:output_path] = path end end + + option(opts, '--display-only-failed') end def add_severity_option(opts) @@ -286,6 +288,7 @@ def validate_compatibility # rubocop:disable Metrics/MethodLength end validate_auto_gen_config validate_auto_correct + validate_display_only_failed validate_parallel return if incompatible_options.size <= 1 @@ -309,13 +312,20 @@ def validate_auto_gen_config end end + def validate_display_only_failed + return unless @options.key?(:display_only_failed) + return if @options[:format] == 'junit' + + raise OptionArgumentError, + format('--display-only-failed can only be used together with --format junit.') + end + def validate_auto_correct return if @options.key?(:auto_correct) return unless @options.key?(:disable_uncorrectable) raise OptionArgumentError, - format('--%s can only be used together with --auto-correct.', - flag: '--disable-uncorrectable') + format('--disable-uncorrectable can only be used together with --auto-correct.') end def validate_parallel @@ -431,6 +441,8 @@ module OptionsHelp 'if no format is specified.'], fail_level: ['Minimum severity (A/R/C/W/E/F) for exit', 'with error code.'], + display_only_failed: ['Only output offense messages. Omit passing', + 'cops. Only valid for --format junit.'], display_only_fail_level_offenses: ['Only output offense messages at', 'the specified --fail-level or above'], diff --git a/spec/rubocop/options_spec.rb b/spec/rubocop/options_spec.rb index b775b069f7f..c24bd365807 100644 --- a/spec/rubocop/options_spec.rb +++ b/spec/rubocop/options_spec.rb @@ -92,6 +92,8 @@ def abs(path) This option applies to the previously specified --format, or the default format if no format is specified. + --display-only-failed Only output offense messages. Omit passing + cops. Only valid for --format junit. -r, --require FILE Require Ruby file. --fail-level SEVERITY Minimum severity (A/R/C/W/E/F) for exit with error code. @@ -224,6 +226,18 @@ def abs(path) end end + describe '--display-only-failed' do + it 'fails if given without --format junit' do + expect { options.parse %w[--display-only-failed] } + .to raise_error(RuboCop::OptionArgumentError) + end + + it 'works if given with --format junit' do + expect { options.parse %w[--format junit --display-only-failed] } + .not_to raise_error(RuboCop::OptionArgumentError) + end + end + describe '--fail-level' do it 'accepts full severity names' do %w[refactor convention warning error fatal].each do |severity|