test-prof 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ # Vernier wrapper.
5
+ #
6
+ # Has 2 modes: global and per-example.
7
+ #
8
+ # Example:
9
+ #
10
+ # # To activate global profiling you can use env variable
11
+ # TEST_VERNIER=1 rspec ...
12
+ #
13
+ # To profile a specific examples add :vernier tag to it:
14
+ #
15
+ # it "is doing heavy stuff", :vernier do
16
+ # ...
17
+ # end
18
+ #
19
+ module Vernier
20
+ # Vernier configuration
21
+ class Configuration
22
+ attr_accessor :mode, :target, :interval
23
+
24
+ def initialize
25
+ @mode = ENV.fetch("TEST_VERNIER_MODE", :wall).to_sym
26
+ @target = (ENV["TEST_VERNIER"] == "boot") ? :boot : :suite
27
+
28
+ sample_interval = ENV["TEST_VERNIER_INTERVAL"].to_i
29
+ @interval = (sample_interval > 0) ? sample_interval : nil
30
+ end
31
+
32
+ def boot?
33
+ target == :boot
34
+ end
35
+
36
+ def suite?
37
+ target == :suite
38
+ end
39
+ end
40
+
41
+ class << self
42
+ include Logging
43
+
44
+ def config
45
+ @config ||= Configuration.new
46
+ end
47
+
48
+ def configure
49
+ yield config
50
+ end
51
+
52
+ attr_reader :default_collector
53
+
54
+ # Run Vernier and automatically dump
55
+ # a report when the process exits or when the application is booted.
56
+ def run
57
+ collector = profile
58
+ return unless collector
59
+
60
+ @locked = true
61
+ @default_collector = collector
62
+
63
+ log :info, "Vernier enabled globally: " \
64
+ "mode – #{config.mode}, target – #{config.target}"
65
+
66
+ at_exit { dump(collector, "total") } if config.suite?
67
+ end
68
+
69
+ def profile(name = nil)
70
+ if locked?
71
+ log :warn, <<~MSG
72
+ Vernier has been already activated.
73
+
74
+ Make sure you do not have the TEST_VERNIER environmental variable set somewhere.
75
+ MSG
76
+
77
+ return false
78
+ end
79
+
80
+ return false unless init_vernier
81
+
82
+ options = {}
83
+
84
+ options[:interval] = config.interval if config.interval
85
+
86
+ if block_given?
87
+ options[:mode] = config.mode
88
+ options[:out] = build_path(name)
89
+ ::Vernier.trace(**options) { yield }
90
+ else
91
+ collector = ::Vernier::Collector.new(config.mode, **options)
92
+ collector.start
93
+
94
+ collector
95
+ end
96
+ end
97
+
98
+ def dump(collector, name)
99
+ result = collector.stop
100
+
101
+ path = build_path(name)
102
+
103
+ File.write(path, ::Vernier::Output::Firefox.new(result).output)
104
+
105
+ log :info, "Vernier report generated: #{path}"
106
+ end
107
+
108
+ private
109
+
110
+ def build_path(name)
111
+ TestProf.artifact_path(
112
+ "vernier-report-#{config.mode}-#{name}.json"
113
+ )
114
+ end
115
+
116
+ def locked?
117
+ @locked == true
118
+ end
119
+
120
+ def init_vernier
121
+ return @initialized if instance_variable_defined?(:@initialized)
122
+ @locked = false
123
+ @initialized = TestProf.require(
124
+ "vernier",
125
+ <<~MSG
126
+ Please, install 'vernier' first:
127
+ # Gemfile
128
+ gem 'vernier', '>= 0.3.0', require: false
129
+ MSG
130
+ ) { check_vernier_version }
131
+ end
132
+
133
+ def check_vernier_version
134
+ if Utils.verify_gem_version("vernier", at_least: "0.3.0")
135
+ true
136
+ else
137
+ log :error, <<~MSG
138
+ Please, upgrade 'vernier' to version >= 0.3.0.
139
+ MSG
140
+ false
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ require "test_prof/vernier/rspec" if TestProf.rspec?
148
+
149
+ # Hook to run Vernier globally
150
+ TestProf.activate("TEST_VERNIER") do
151
+ TestProf::Vernier.run
152
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "1.2.2"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/test_prof.rb CHANGED
@@ -1,177 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "logger"
5
-
6
3
  require "test_prof/version"
7
- require "test_prof/logging"
8
- require "test_prof/utils"
9
-
10
- # Ruby applications tests profiling tools.
11
- #
12
- # Contains tools to analyze factories usage, integrate with Ruby profilers,
13
- # profile your examples using ActiveSupport notifications (if any) and
14
- # statically analyze your code with custom RuboCop cops.
15
- #
16
- # Example usage:
17
- #
18
- # require 'test_prof'
19
- #
20
- # # Activate a tool by providing environment variable, e.g.
21
- # TEST_RUBY_PROF=1 rspec ...
22
- #
23
- # # or manually in your code
24
- # TestProf::RubyProf.run
25
- #
26
- # See other modules for more examples.
27
- module TestProf
28
- class << self
29
- include Logging
30
-
31
- def config
32
- @config ||= Configuration.new
33
- end
34
-
35
- def configure
36
- yield config
37
- end
38
-
39
- # Returns true if we're inside RSpec
40
- def rspec?
41
- defined?(RSpec::Core)
42
- end
43
-
44
- # Returns true if we're inside Minitest
45
- def minitest?
46
- defined?(Minitest)
47
- end
48
-
49
- # Returns true if Spring is used and not disabled
50
- def spring?
51
- # See https://github.com/rails/spring/blob/577cf01f232bb6dbd0ade7df2df2ac209697e741/lib/spring/binstub.rb
52
- disabled = ENV["DISABLE_SPRING"]
53
- defined?(::Spring::Application) && (disabled.nil? || disabled.empty? || disabled == "0")
54
- end
55
-
56
- # Returns the current process time
57
- def now
58
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
59
- end
60
-
61
- # Require gem and shows a custom
62
- # message if it fails to load
63
- def require(gem_name, msg = nil)
64
- Kernel.require gem_name
65
- block_given? ? yield : true
66
- rescue LoadError
67
- log(:error, msg) if msg
68
- false
69
- end
70
-
71
- # Run block only if provided env var is present and
72
- # equal to the provided value (if any).
73
- # Contains workaround for applications using Spring.
74
- def activate(env_var, val = nil)
75
- if spring?
76
- notify_spring_detected
77
- ::Spring.after_fork do
78
- activate!(env_var, val) do
79
- notify_spring_activate env_var
80
- yield
81
- end
82
- end
83
- else
84
- activate!(env_var, val) { yield }
85
- end
86
- end
87
-
88
- # Return absolute path to asset
89
- def asset_path(filename)
90
- ::File.expand_path(filename, ::File.join(::File.dirname(__FILE__), "..", "assets"))
91
- end
92
-
93
- # Return a path to store artifact
94
- def artifact_path(filename)
95
- create_artifact_dir
96
-
97
- with_timestamps(
98
- ::File.join(
99
- config.output_dir,
100
- with_report_suffix(
101
- filename
102
- )
103
- )
104
- )
105
- end
106
-
107
- def create_artifact_dir
108
- FileUtils.mkdir_p(config.output_dir)[0]
109
- end
110
-
111
- private
112
-
113
- def activate!(env_var, val)
114
- yield if ENV[env_var] && (val.nil? || val === ENV[env_var])
115
- end
116
-
117
- def with_timestamps(path)
118
- return path unless config.timestamps?
119
- timestamps = "-#{now.to_i}"
120
- "#{path.sub(/\.\w+$/, "")}#{timestamps}#{::File.extname(path)}"
121
- end
122
-
123
- def with_report_suffix(path)
124
- return path if config.report_suffix.nil?
125
-
126
- "#{path.sub(/\.\w+$/, "")}-#{config.report_suffix}#{::File.extname(path)}"
127
- end
128
-
129
- def notify_spring_detected
130
- return if instance_variable_defined?(:@spring_notified)
131
- log :info, "Spring detected"
132
- @spring_notified = true
133
- end
134
-
135
- def notify_spring_activate(env_var)
136
- log :info, "Activating #{env_var} with `Spring.after_fork`"
137
- end
138
- end
139
-
140
- # TestProf configuration
141
- class Configuration
142
- attr_accessor :output, # IO to write logs
143
- :color, # Whether to colorize output or not
144
- :output_dir, # Directory to store artifacts
145
- :timestamps, # Whether to use timestamped names for artifacts,
146
- :report_suffix # Custom suffix for reports/artifacts
147
-
148
- def initialize
149
- @output = $stdout
150
- @color = true
151
- @output_dir = "tmp/test_prof"
152
- @timestamps = false
153
- @report_suffix = ENV["TEST_PROF_REPORT"]
154
- end
155
-
156
- def color?
157
- color == true && output.is_a?(IO) && output.tty?
158
- end
159
-
160
- def timestamps?
161
- timestamps == true
162
- end
163
-
164
- def logger
165
- @logger ||= Logger.new(output, formatter: Logging::Formatter.new)
166
- end
167
- end
168
- end
4
+ require "test_prof/core"
169
5
 
170
6
  require "test_prof/ruby_prof"
171
7
  require "test_prof/stack_prof"
8
+ require "test_prof/vernier"
172
9
  require "test_prof/event_prof"
173
10
  require "test_prof/factory_doctor"
174
11
  require "test_prof/factory_prof"
12
+ require "test_prof/memory_prof"
175
13
  require "test_prof/rspec_stamp"
176
14
  require "test_prof/tag_prof"
177
15
  require "test_prof/rspec_dissect" if TestProf.rspec?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-prof
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-27 00:00:00.000000000 Z
11
+ date: 2023-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,6 +143,7 @@ files:
143
143
  - lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb
144
144
  - lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb
145
145
  - lib/test_prof/cops/rspec/language.rb
146
+ - lib/test_prof/core.rb
146
147
  - lib/test_prof/event_prof.rb
147
148
  - lib/test_prof/event_prof/custom_events.rb
148
149
  - lib/test_prof/event_prof/custom_events/factory_create.rb
@@ -179,6 +180,14 @@ files:
179
180
  - lib/test_prof/factory_prof/printers/nate_heckler.rb
180
181
  - lib/test_prof/factory_prof/printers/simple.rb
181
182
  - lib/test_prof/logging.rb
183
+ - lib/test_prof/memory_prof.rb
184
+ - lib/test_prof/memory_prof/minitest.rb
185
+ - lib/test_prof/memory_prof/printer.rb
186
+ - lib/test_prof/memory_prof/printer/number_to_human.rb
187
+ - lib/test_prof/memory_prof/rspec.rb
188
+ - lib/test_prof/memory_prof/tracker.rb
189
+ - lib/test_prof/memory_prof/tracker/linked_list.rb
190
+ - lib/test_prof/memory_prof/tracker/rss_tool.rb
182
191
  - lib/test_prof/recipes/logging.rb
183
192
  - lib/test_prof/recipes/minitest/before_all.rb
184
193
  - lib/test_prof/recipes/minitest/sample.rb
@@ -211,6 +220,8 @@ files:
211
220
  - lib/test_prof/utils/html_builder.rb
212
221
  - lib/test_prof/utils/rspec.rb
213
222
  - lib/test_prof/utils/sized_ordered_set.rb
223
+ - lib/test_prof/vernier.rb
224
+ - lib/test_prof/vernier/rspec.rb
214
225
  - lib/test_prof/version.rb
215
226
  homepage: http://github.com/test-prof/test-prof
216
227
  licenses:
@@ -237,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
248
  - !ruby/object:Gem::Version
238
249
  version: '0'
239
250
  requirements: []
240
- rubygems_version: 3.4.8
251
+ rubygems_version: 3.4.20
241
252
  signing_key:
242
253
  specification_version: 4
243
254
  summary: Ruby applications tests profiling tools