diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 04c9e999d06ea597a33c33f8c2f34c360f286226..0f9db92437244990b3da4ad234ce6e92cb141a3e 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -2372,7 +2372,7 @@ Layout/LineLength: - 'lib/gitlab/ci/config/external/file/project.rb' - 'lib/gitlab/ci/config/external/file/remote.rb' - 'lib/gitlab/ci/parsers/coverage/cobertura.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/ci/parsers/security/common.rb' - 'lib/gitlab/ci/parsers/security/validators/schema_validator.rb' - 'lib/gitlab/ci/pipeline/chain/create_cross_database_associations.rb' @@ -3407,7 +3407,7 @@ Layout/LineLength: - 'spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb' - 'spec/lib/gitlab/ci/cron_parser_spec.rb' - 'spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb' - - 'spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb' + - 'spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document_spec.rb' - 'spec/lib/gitlab/ci/parsers/security/common_spec.rb' - 'spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb' - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb' diff --git a/.rubocop_todo/lint/assignment_in_condition.yml b/.rubocop_todo/lint/assignment_in_condition.yml index e86b19135659489fb8d051199a763b496a9fc8d3..49d42a5bfb4f92f80bc3afa65ecf3c1fd87ca006 100644 --- a/.rubocop_todo/lint/assignment_in_condition.yml +++ b/.rubocop_todo/lint/assignment_in_condition.yml @@ -154,7 +154,7 @@ Lint/AssignmentInCondition: - 'lib/gitlab/ci/build/rules/rule.rb' - 'lib/gitlab/ci/config/external/rules.rb' - 'lib/gitlab/ci/config/normalizer.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/ci/yaml_processor.rb' - 'lib/gitlab/counters/buffered_counter.rb' - 'lib/gitlab/data_builder/deployment.rb' diff --git a/.rubocop_todo/naming/heredoc_delimiter_naming.yml b/.rubocop_todo/naming/heredoc_delimiter_naming.yml index 09e03a992dd083ccc08088617211de6b51298c69..97355ed07f917c8cb06f29c5e2ec225c5f0731cd 100644 --- a/.rubocop_todo/naming/heredoc_delimiter_naming.yml +++ b/.rubocop_todo/naming/heredoc_delimiter_naming.yml @@ -56,7 +56,7 @@ Naming/HeredocDelimiterNaming: - 'spec/lib/banzai/pipeline/full_pipeline_spec.rb' - 'spec/lib/gitlab/auth/ldap/config_spec.rb' - 'spec/lib/gitlab/ci/config_spec.rb' - - 'spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb' + - 'spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document_spec.rb' - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb' - 'spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb' - 'spec/lib/gitlab/ci/templates/templates_spec.rb' diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml index 1156e89a01971f5eb473a682ba19786367d27e76..8dd12d76f4b3c6676014937fcf0c187f44df14c9 100644 --- a/.rubocop_todo/rspec/feature_category.yml +++ b/.rubocop_todo/rspec/feature_category.yml @@ -2355,7 +2355,6 @@ RSpec/FeatureCategory: - 'spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb' - 'spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb' - 'spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb' - - 'spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb' - 'spec/lib/gitlab/ci/parsers/instrumentation_spec.rb' - 'spec/lib/gitlab/ci/parsers/security/sast_spec.rb' - 'spec/lib/gitlab/ci/parsers/security/secret_detection_spec.rb' diff --git a/.rubocop_todo/style/guard_clause.yml b/.rubocop_todo/style/guard_clause.yml index 23e93ec6f178cedf2c0944fdfd3b626580d1cdcb..31970e804d2f9c73c450d363fc9b3b5058f53e27 100644 --- a/.rubocop_todo/style/guard_clause.yml +++ b/.rubocop_todo/style/guard_clause.yml @@ -401,7 +401,7 @@ Style/GuardClause: - 'lib/gitlab/ci/config/external/file/base.rb' - 'lib/gitlab/ci/config/external/file/remote.rb' - 'lib/gitlab/ci/config/external/file/template.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/ci/parsers/security/common.rb' - 'lib/gitlab/ci/pipeline/chain/command.rb' - 'lib/gitlab/ci/pipeline/chain/limit/rate_limit.rb' diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml index bc1ef52bad9187774d683d7c327428ce7f825aa4..a3b7bcbffdf0f42e8372ed437e74c41e1e25acdd 100644 --- a/.rubocop_todo/style/if_unless_modifier.yml +++ b/.rubocop_todo/style/if_unless_modifier.yml @@ -599,7 +599,7 @@ Style/IfUnlessModifier: - 'lib/gitlab/ci/config/external/file/base.rb' - 'lib/gitlab/ci/config/external/file/template.rb' - 'lib/gitlab/ci/config/normalizer.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/ci/parsers/security/common.rb' - 'lib/gitlab/ci/pipeline/chain/validate/abilities.rb' - 'lib/gitlab/ci/pipeline/chain/validate/repository.rb' diff --git a/.rubocop_todo/style/redundant_return.yml b/.rubocop_todo/style/redundant_return.yml index 8a1812a32a6c7934faff779a540c49030b0f29eb..5b4b2d0d4ceb22a6a86e27fe0c60160c08d92a69 100644 --- a/.rubocop_todo/style/redundant_return.yml +++ b/.rubocop_todo/style/redundant_return.yml @@ -50,7 +50,7 @@ Style/RedundantReturn: - 'lib/feature/gitaly.rb' - 'lib/gitlab/auth/database/authentication.rb' - 'lib/gitlab/checks/diff_check.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/database/health_status/indicators/prometheus_alert_indicator.rb' - 'lib/gitlab/graphql/queries.rb' - 'lib/gitlab/hotlinking_detector.rb' diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml index e4e3f52cbdc87647f5ff9f5df005db5589da9b06..a3f65de97e46f4a360366d8a8314632db12fb296 100644 --- a/.rubocop_todo/style/redundant_self.yml +++ b/.rubocop_todo/style/redundant_self.yml @@ -259,7 +259,7 @@ Style/RedundantSelf: - 'lib/gitlab/ci/config/entry/jobs.rb' - 'lib/gitlab/ci/config/entry/root.rb' - 'lib/gitlab/ci/jwt.rb' - - 'lib/gitlab/ci/parsers/coverage/sax_document.rb' + - 'lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - 'lib/gitlab/ci/pipeline/expression/lexeme/null.rb' - 'lib/gitlab/ci/pipeline/preloader.rb' - 'lib/gitlab/ci/queue/metrics.rb' diff --git a/app/models/concerns/enums/ci/job_artifact.rb b/app/models/concerns/enums/ci/job_artifact.rb index 18b0b9f3d3730d10b5532689e33597eacae9436d..938b371c4572fe297062135b371483c51bd97069 100644 --- a/app/models/concerns/enums/ci/job_artifact.rb +++ b/app/models/concerns/enums/ci/job_artifact.rb @@ -180,7 +180,7 @@ def self.file_type cyclonedx: 28, ## EE-specific requirements_v2: 29, ## EE-specific annotations: 30, - repository_xray: 31 ## EE-specifric + repository_xray: 31 ## EE-specific } end diff --git a/lib/gitlab/ci/parsers/coverage/cobertura.rb b/lib/gitlab/ci/parsers/coverage/cobertura.rb index 6041907ef78da11a433821ed09e255ed1d1ca475..9f56b81fda9a7cf74b71f70078c31a1dd5f042c3 100644 --- a/lib/gitlab/ci/parsers/coverage/cobertura.rb +++ b/lib/gitlab/ci/parsers/coverage/cobertura.rb @@ -9,7 +9,7 @@ class Cobertura InvalidLineInformationError = Class.new(Gitlab::Ci::Parsers::ParserError) def parse!(xml_data, coverage_report, project_path: nil, worktree_paths: nil) - Nokogiri::XML::SAX::Parser.new(SaxDocument.new(coverage_report, project_path, worktree_paths)).parse(xml_data) + Nokogiri::XML::SAX::Parser.new(Documents::CoberturaDocument.new(coverage_report, project_path, worktree_paths)).parse(xml_data) end end end diff --git a/lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb b/lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb new file mode 100644 index 0000000000000000000000000000000000000000..1bdfc9ab2b6adc943678ccb246eb2816bc99f202 --- /dev/null +++ b/lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Parsers + module Coverage + module Documents + class CoberturaDocument < Nokogiri::XML::SAX::Document + GO_SOURCE_PATTERN = '/usr/local/go/src' + MAX_SOURCES = 100 + + def initialize(coverage_report, project_path, worktree_paths) + @coverage_report = coverage_report + @project_path = project_path + @paths = worktree_paths&.to_set + + @matched_filenames = [] + @parsed_lines = [] + @sources = [] + end + + def error(error) + raise Cobertura::InvalidXMLError, "XML parsing failed with error: #{error}" + end + + def start_element(node_name, attrs = []) + return unless node_name + + self.node_name = node_name + node_attrs = Hash[attrs] + + if node_name == 'class' && node_attrs["filename"].present? + self.filename = determine_filename(node_attrs["filename"]) + self.matched_filenames << filename if filename + elsif node_name == 'line' + self.parsed_lines << parse_line(node_attrs) + end + end + + def characters(node_content) + if node_name == 'source' + parse_source(node_content) + end + end + + def end_element(node_name) + if node_name == "package" + remove_matched_filenames + elsif node_name == "class" && filename && parsed_lines.present? + coverage_report.add_file(filename, Hash[parsed_lines]) + self.filename = nil + self.parsed_lines = [] + end + end + + private + + attr_accessor :coverage_report, :project_path, :paths, :sources, :node_name, :filename, :parsed_lines, :matched_filenames + + def parse_line(line) + [Integer(line["number"]), Integer(line["hits"])] + rescue StandardError + raise Cobertura::InvalidLineInformationError, "Line information had invalid values" + end + + def parse_source(node) + return unless project_path && paths && node.exclude?(GO_SOURCE_PATTERN) + + source = build_source_path(node) + self.sources << source if source.present? + end + + def build_source_path(node) + # | raw source | extracted | + # |-----------------------------|------------| + # | /builds/foo/test/SampleLib/ | SampleLib/ | + # | /builds/foo/test/something | something | + # | /builds/foo/test/ | nil | + # | /builds/foo/test | nil | + # | D:\builds\foo\bar\app\ | app\ | + unixify(node).split("#{project_path}/", 2)[1] + end + + def unixify(path) + path.tr('\\', '/') + end + + def remove_matched_filenames + return unless paths + + matched_filenames.each { |f| paths.delete(f) } + end + + def determine_filename(filename) + return filename unless sources.any? + + full_filename = nil + + sources.each_with_index do |source, index| + break if index >= MAX_SOURCES + break if full_filename = check_source(source, filename) + end + + full_filename + end + + def check_source(source, filename) + full_path = File.join(source, filename) + + return full_path if paths.include?(full_path) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/parsers/coverage/sax_document.rb b/lib/gitlab/ci/parsers/coverage/sax_document.rb deleted file mode 100644 index eafeaf9d505adf3097734f220f24b646d2ab3d42..0000000000000000000000000000000000000000 --- a/lib/gitlab/ci/parsers/coverage/sax_document.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Parsers - module Coverage - class SaxDocument < Nokogiri::XML::SAX::Document - GO_SOURCE_PATTERN = '/usr/local/go/src' - MAX_SOURCES = 100 - - def initialize(coverage_report, project_path, worktree_paths) - @coverage_report = coverage_report - @project_path = project_path - @paths = worktree_paths&.to_set - - @matched_filenames = [] - @parsed_lines = [] - @sources = [] - end - - def error(error) - raise Cobertura::InvalidXMLError, "XML parsing failed with error: #{error}" - end - - def start_element(node_name, attrs = []) - return unless node_name - - self.node_name = node_name - node_attrs = Hash[attrs] - - if node_name == 'class' && node_attrs["filename"].present? - self.filename = determine_filename(node_attrs["filename"]) - self.matched_filenames << filename if filename - elsif node_name == 'line' - self.parsed_lines << parse_line(node_attrs) - end - end - - def characters(node_content) - if node_name == 'source' - parse_source(node_content) - end - end - - def end_element(node_name) - if node_name == "package" - remove_matched_filenames - elsif node_name == "class" && filename && parsed_lines.present? - coverage_report.add_file(filename, Hash[parsed_lines]) - self.filename = nil - self.parsed_lines = [] - end - end - - private - - attr_accessor :coverage_report, :project_path, :paths, :sources, :node_name, :filename, :parsed_lines, :matched_filenames - - def parse_line(line) - [Integer(line["number"]), Integer(line["hits"])] - rescue StandardError - raise Cobertura::InvalidLineInformationError, "Line information had invalid values" - end - - def parse_source(node) - return unless project_path && paths && node.exclude?(GO_SOURCE_PATTERN) - - source = build_source_path(node) - self.sources << source if source.present? - end - - def build_source_path(node) - # | raw source | extracted | - # |-----------------------------|------------| - # | /builds/foo/test/SampleLib/ | SampleLib/ | - # | /builds/foo/test/something | something | - # | /builds/foo/test/ | nil | - # | /builds/foo/test | nil | - # | D:\builds\foo\bar\app\ | app\ | - unixify(node).split("#{project_path}/", 2)[1] - end - - def unixify(path) - path.tr('\\', '/') - end - - def remove_matched_filenames - return unless paths - - matched_filenames.each { |f| paths.delete(f) } - end - - def determine_filename(filename) - return filename unless sources.any? - - full_filename = nil - - sources.each_with_index do |source, index| - break if index >= MAX_SOURCES - break if full_filename = check_source(source, filename) - end - - full_filename - end - - def check_source(source, filename) - full_path = File.join(source, filename) - - return full_path if paths.include?(full_path) - end - end - end - end - end -end diff --git a/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb b/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb index 65d85c7f1c0c52b753546f3c8ea1c06934b8ca92..8930129e35b65e7e2328da3720a6dfdb49d0479f 100644 --- a/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb +++ b/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb @@ -16,8 +16,8 @@ end end - it 'uses Sax parser' do - expect(Gitlab::Ci::Parsers::Coverage::SaxDocument).to receive(:new) + it 'uses Cobertura parser' do + expect(Gitlab::Ci::Parsers::Coverage::Documents::CoberturaDocument).to receive(:new) parse_report end diff --git a/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb b/spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document_spec.rb similarity index 99% rename from spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb rename to spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document_spec.rb index b168ad0816a4b811293925362af36b2ab45ca1c2..9dee1100de47b6978bf4ea021673c46ab87eb6ef 100644 --- a/spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb +++ b/spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document_spec.rb @@ -2,7 +2,8 @@ require 'fast_spec_helper' -RSpec.describe Gitlab::Ci::Parsers::Coverage::SaxDocument do +RSpec.describe Gitlab::Ci::Parsers::Coverage::Documents::CoberturaDocument, + feature_category: :code_testing do subject(:parse_report) { Nokogiri::XML::SAX::Parser.new(described_class.new(coverage_report, project_path, paths)).parse(cobertura) } describe '#parse!' do diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 5dae8a460d5240a14b820888ca8679c0883fd459..beef5f5175b7ec6c763ae83d003687a0299fecba 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -4854,7 +4854,7 @@ - './spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb' - './spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb' - './spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb' -- './spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb' +- './spec/lib/gitlab/ci/parsers/coverage/documents/cobertura_document.rb' - './spec/lib/gitlab/ci/parsers/instrumentation_spec.rb' - './spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb' - './spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb'