test-prof 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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