Class: RailVerdict::Comparison

Inherits:
Object
  • Object
show all
Defined in:
lib/rail_verdict/comparison.rb

Defined Under Namespace

Classes: Result

Class Method Summary collapse

Class Method Details

.classify(findings:, baseline:, waivers: [], clock: Time.now.utc, rename_map: nil) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rail_verdict/comparison.rb', line 9

def self.classify(findings:, baseline:, waivers: [], clock: Time.now.utc, rename_map: nil)
  findings = findings.sort_by(&:sort_key)
  if baseline.nil?
    introduced = findings.map { |finding| restate(finding, "introduced") }
    resolved = []
    existing = []
    changed = []
    moved = []
    if waivers.any?
      waived, orphaned = apply_waivers(nil, findings, baseline, waivers, clock)
      if waived.any?
        current_by_fp = findings.to_h { |finding| [finding.fingerprint, finding] }
        introduced = introduced.reject { |finding| waived.include?(finding.fingerprint) }
        waived_findings = waived.map { |fingerprint| current_by_fp[fingerprint] }.compact.map { |finding| restate(finding, "waived") }.sort_by(&:sort_key)
        classified = (introduced + waived_findings).sort_by(&:sort_key)
        return build_result(existing: existing, introduced: introduced, resolved: resolved, changed: changed, moved: moved, classified_findings: classified, waived: waived_findings, orphaned_waivers: orphaned, baseline: baseline)
      end
      return build_result(existing: existing, introduced: introduced, resolved: resolved, changed: changed, moved: moved, classified_findings: introduced, waived: [], orphaned_waivers: orphaned, baseline: baseline)
    end
    return build_result(existing: existing, introduced: introduced, resolved: resolved, changed: changed, moved: moved, findings: findings, baseline: baseline, waivers: waivers, clock: clock)
  end

  baseline_fps = Set.new(baseline.entries.map { |entry| entry.fetch("fingerprint") })
  baseline_by_fp = baseline.entries.to_h { |entry| [entry.fetch("fingerprint"), entry] }
  current_by_fp = findings.to_h { |finding| [finding.fingerprint, finding] }
  current_fps = Set.new(current_by_fp.keys)

  existing_fps = baseline_fps & current_fps
  resolved_fps = baseline_fps - current_fps
  introduced_fps = current_fps - baseline_fps

  existing = existing_fps.map { |fingerprint| restate(current_by_fp.fetch(fingerprint), "existing") }.sort_by(&:sort_key)
  resolved_entries = resolved_fps.map { |fingerprint| baseline_by_fp.fetch(fingerprint) }.sort_by { |entry| entry.fetch("fingerprint") }
  introduced_candidates = introduced_fps.map { |fingerprint| current_by_fp.fetch(fingerprint) }.sort_by(&:sort_key)

  resolved_by_key = build_resolved_index(resolved_entries)
  changed, moved, introduced = partition_introduced(introduced_candidates, resolved_by_key, resolved_entries, rename_map: rename_map)
  resolved = resolved_entries.map { |entry| { "fingerprint" => entry.fetch("fingerprint"), "analyzer" => entry.fetch("analyzer"), "rule_id" => entry.fetch("rule_id"), "path" => entry.fetch("path"), "message" => entry.fetch("message"), "state" => "resolved" } }
  if changed.any? || moved.any?
    consumed_fps = Set.new((changed + moved).flat_map { |pair| [pair[:resolved].fetch("fingerprint")] })
    resolved = resolved.reject { |entry| consumed_fps.include?(entry.fetch("fingerprint")) }
  end

  waived = Set.new
  orphaned = []
  if waivers.any?
    waived, orphaned = apply_waivers(nil, findings, baseline, waivers, clock)
  end
  waived_findings = []
  if waived.any?
    existing = existing.reject { |finding| waived.include?(finding.fingerprint) }
    introduced = introduced.reject { |finding| waived.include?(finding.fingerprint) }
    changed_pairs = changed
    moved_pairs = moved
    changed_findings_tmp = changed_pairs.map { |pair| pair[:finding] }.reject { |finding| waived.include?(finding.fingerprint) }
    moved_findings_tmp = moved_pairs.map { |pair| pair[:finding] }.reject { |finding| waived.include?(finding.fingerprint) }
    changed = changed_pairs.select { |pair| changed_findings_tmp.include?(pair[:finding]) }
    moved = moved_pairs.select { |pair| moved_findings_tmp.include?(pair[:finding]) }
    waived_findings = waived.map { |fingerprint| current_by_fp[fingerprint] }.compact.map { |finding| restate(finding, "waived") }.sort_by(&:sort_key)
    classified_extra = waived_findings
    changed_findings = changed.map { |pair| pair[:finding] }
    moved_findings = moved.map { |pair| pair[:finding] }
    classified = (existing + introduced + changed_findings + moved_findings + classified_extra).sort_by(&:sort_key)
    return build_result(existing: existing, introduced: introduced, resolved: resolved, changed: changed_findings, moved: moved_findings, classified_findings: classified, waived: classified_extra, orphaned_waivers: orphaned, baseline: baseline)
  end
  changed_findings = changed.map { |pair| pair[:finding] }
  moved_findings = moved.map { |pair| pair[:finding] }
  classified = (existing + introduced + changed_findings + moved_findings).sort_by(&:sort_key)
  build_result(existing: existing, introduced: introduced, resolved: resolved, changed: changed_findings, moved: moved_findings, classified_findings: classified, waived: [], orphaned_waivers: orphaned, baseline: baseline)
end