File: rspec.rb

package info (click to toggle)
ruby-test-prof 0.12.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 508 kB
  • sloc: ruby: 4,075; makefile: 4
file content (156 lines) | stat: -rw-r--r-- 4,302 bytes parent folder | download
1
2
3
4
5
6
7
8
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# frozen_string_literal: true

require "test_prof/ext/float_duration"
require "test_prof/ext/string_truncate"

module TestProf
  module EventProf
    class RSpecListener # :nodoc:
      include Logging
      using FloatDuration
      using StringTruncate

      NOTIFICATIONS = %i[
        example_group_started
        example_group_finished
        example_started
        example_finished
      ].freeze

      def initialize
        @profiler = EventProf.build

        log :info, "EventProf enabled (#{@profiler.events.join(", ")})"
      end

      def example_group_started(notification)
        return unless notification.group.top_level?
        @profiler.group_started notification.group
      end

      def example_group_finished(notification)
        return unless notification.group.top_level?
        @profiler.group_finished notification.group
      end

      def example_started(notification)
        @profiler.example_started notification.example
      end

      def example_finished(notification)
        @profiler.example_finished notification.example
      end

      def print
        @profiler.each(&method(:report))
      end

      def report(profiler)
        result = profiler.results
        time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)

        msgs = []

        msgs <<
          <<~MSG
            EventProf results for #{profiler.event}

            Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
            Total events: #{profiler.total_count}

            Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):

          MSG

        result[:groups].each do |group|
          description = group[:id].top_level_description
          location = group[:id].metadata[:location]
          time = group[:time]
          run_time = group[:run_time]
          time_percentage = time_percentage(time, run_time)

          msgs <<
            <<~GROUP
              #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
            GROUP
        end

        if result[:examples]
          msgs << "\nTop #{profiler.top_count} slowest tests (by #{profiler.rank_by}):\n\n"

          result[:examples].each do |example|
            description = example[:id].description
            location = example[:id].metadata[:location]
            time = example[:time]
            run_time = example[:run_time]
            time_percentage = time_percentage(time, run_time)

            msgs <<
              <<~GROUP
                #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
              GROUP
          end
        end

        log :info, msgs.join

        stamp!(profiler) if EventProf.config.stamp?
      end

      def stamp!(profiler)
        result = profiler.results

        stamper = RSpecStamp::Stamper.new

        examples = Hash.new { |h, k| h[k] = [] }

        (result[:groups].to_a + result.fetch(:examples, []).to_a)
          .map { |obj| obj[:id].metadata[:location] }.each do |location|
          file, line = location.split(":")
          examples[file] << line.to_i
        end

        examples.each do |file, lines|
          stamper.stamp_file(file, lines.uniq)
        end

        msgs = []

        msgs <<
          <<~MSG
            RSpec Stamp results

            Total patches: #{stamper.total}
            Total files: #{examples.keys.size}

            Failed patches: #{stamper.failed}
            Ignored files: #{stamper.ignored}
          MSG

        log :info, msgs.join
      end

      def time_percentage(time, total_time)
        (time / total_time * 100).round(2)
      end
    end
  end
end

# Register EventProf listener
TestProf.activate("EVENT_PROF") do
  TestProf::EventProf::CustomEvents.activate_all(ENV["EVENT_PROF"])

  RSpec.configure do |config|
    listener = nil

    config.before(:suite) do
      listener = TestProf::EventProf::RSpecListener.new
      config.reporter.register_listener(
        listener, *TestProf::EventProf::RSpecListener::NOTIFICATIONS
      )
    end

    config.after(:suite) { listener&.print }
  end
end