Skip to content

Commit

Permalink
chore: extract test_prof/core.rb
Browse files Browse the repository at this point in the history
Fixes #270
  • Loading branch information
palkan committed Jul 11, 2023
1 parent dbf4169 commit 17d674d
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 171 deletions.
166 changes: 1 addition & 165 deletions lib/test_prof.rb
Original file line number Diff line number Diff line change
@@ -1,171 +1,7 @@
# frozen_string_literal: true

require "fileutils"
require "logger"

require "test_prof/version"
require "test_prof/logging"
require "test_prof/utils"

# Ruby applications tests profiling tools.
#
# Contains tools to analyze factories usage, integrate with Ruby profilers,
# profile your examples using ActiveSupport notifications (if any) and
# statically analyze your code with custom RuboCop cops.
#
# Example usage:
#
# require 'test_prof'
#
# # Activate a tool by providing environment variable, e.g.
# TEST_RUBY_PROF=1 rspec ...
#
# # or manually in your code
# TestProf::RubyProf.run
#
# See other modules for more examples.
module TestProf
class << self
include Logging

def config
@config ||= Configuration.new
end

def configure
yield config
end

# Returns true if we're inside RSpec
def rspec?
defined?(RSpec::Core)
end

# Returns true if we're inside Minitest
def minitest?
defined?(Minitest)
end

# Returns true if Spring is used and not disabled
def spring?
# See https://github.com/rails/spring/blob/577cf01f232bb6dbd0ade7df2df2ac209697e741/lib/spring/binstub.rb
disabled = ENV["DISABLE_SPRING"]
defined?(::Spring::Application) && (disabled.nil? || disabled.empty? || disabled == "0")
end

# Returns the current process time
def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

# Require gem and shows a custom
# message if it fails to load
def require(gem_name, msg = nil)
Kernel.require gem_name
block_given? ? yield : true
rescue LoadError
log(:error, msg) if msg
false
end

# Run block only if provided env var is present and
# equal to the provided value (if any).
# Contains workaround for applications using Spring.
def activate(env_var, val = nil)
if spring?
notify_spring_detected
::Spring.after_fork do
activate!(env_var, val) do
notify_spring_activate env_var
yield
end
end
else
activate!(env_var, val) { yield }
end
end

# Return absolute path to asset
def asset_path(filename)
::File.expand_path(filename, ::File.join(::File.dirname(__FILE__), "..", "assets"))
end

# Return a path to store artifact
def artifact_path(filename)
create_artifact_dir

with_timestamps(
::File.join(
config.output_dir,
with_report_suffix(
filename
)
)
)
end

def create_artifact_dir
FileUtils.mkdir_p(config.output_dir)[0]
end

private

def activate!(env_var, val)
yield if ENV[env_var] && (val.nil? || val === ENV[env_var])
end

def with_timestamps(path)
return path unless config.timestamps?
timestamps = "-#{now.to_i}"
"#{path.sub(/\.\w+$/, "")}#{timestamps}#{::File.extname(path)}"
end

def with_report_suffix(path)
return path if config.report_suffix.nil?

"#{path.sub(/\.\w+$/, "")}-#{config.report_suffix}#{::File.extname(path)}"
end

def notify_spring_detected
return if instance_variable_defined?(:@spring_notified)
log :info, "Spring detected"
@spring_notified = true
end

def notify_spring_activate(env_var)
log :info, "Activating #{env_var} with `Spring.after_fork`"
end
end

# TestProf configuration
class Configuration
attr_accessor :output, # IO to write logs
:color, # Whether to colorize output or not
:output_dir, # Directory to store artifacts
:timestamps, # Whether to use timestamped names for artifacts,
:report_suffix # Custom suffix for reports/artifacts

def initialize
@output = $stdout
@color = true
@output_dir = "tmp/test_prof"
@timestamps = false
@report_suffix = ENV["TEST_PROF_REPORT"]
end

def color?
color == true && output.is_a?(IO) && output.tty?
end

def timestamps?
timestamps == true
end

def logger
@logger ||= Logger.new(output, formatter: Logging::Formatter.new)
end
end
end
require "test_prof/core"

require "test_prof/ruby_prof"
require "test_prof/stack_prof"
Expand Down
2 changes: 1 addition & 1 deletion lib/test_prof/any_fixture.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"
require "test_prof/ext/float_duration"
require "test_prof/any_fixture/dump"

Expand Down
2 changes: 1 addition & 1 deletion lib/test_prof/before_all.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"

module TestProf
# `before_all` helper configuration
Expand Down
167 changes: 167 additions & 0 deletions lib/test_prof/core.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# frozen_string_literal: true

require "fileutils"
require "logger"

require "test_prof/logging"
require "test_prof/utils"

# Ruby applications tests profiling tools.
#
# Contains tools to analyze factories usage, integrate with Ruby profilers,
# profile your examples using ActiveSupport notifications (if any) and
# statically analyze your code with custom RuboCop cops.
#
# Example usage:
#
# require 'test_prof'
#
# # Activate a tool by providing environment variable, e.g.
# TEST_RUBY_PROF=1 rspec ...
#
# # or manually in your code
# TestProf::RubyProf.run
#
# See other modules for more examples.
module TestProf
class << self
include Logging

def config
@config ||= Configuration.new
end

def configure
yield config
end

# Returns true if we're inside RSpec
def rspec?
defined?(RSpec::Core)
end

# Returns true if we're inside Minitest
def minitest?
defined?(Minitest)
end

# Returns true if Spring is used and not disabled
def spring?
# See https://github.com/rails/spring/blob/577cf01f232bb6dbd0ade7df2df2ac209697e741/lib/spring/binstub.rb
disabled = ENV["DISABLE_SPRING"]
defined?(::Spring::Application) && (disabled.nil? || disabled.empty? || disabled == "0")
end

# Returns the current process time
def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

# Require gem and shows a custom
# message if it fails to load
def require(gem_name, msg = nil)
Kernel.require gem_name
block_given? ? yield : true
rescue LoadError
log(:error, msg) if msg
false
end

# Run block only if provided env var is present and
# equal to the provided value (if any).
# Contains workaround for applications using Spring.
def activate(env_var, val = nil)
if spring?
notify_spring_detected
::Spring.after_fork do
activate!(env_var, val) do
notify_spring_activate env_var
yield
end
end
else
activate!(env_var, val) { yield }
end
end

# Return absolute path to asset
def asset_path(filename)
::File.expand_path(filename, ::File.join(::File.dirname(__FILE__), "..", "..", "assets"))
end

# Return a path to store artifact
def artifact_path(filename)
create_artifact_dir

with_timestamps(
::File.join(
config.output_dir,
with_report_suffix(
filename
)
)
)
end

def create_artifact_dir
FileUtils.mkdir_p(config.output_dir)[0]
end

private

def activate!(env_var, val)
yield if ENV[env_var] && (val.nil? || val === ENV[env_var])
end

def with_timestamps(path)
return path unless config.timestamps?
timestamps = "-#{now.to_i}"
"#{path.sub(/\.\w+$/, "")}#{timestamps}#{::File.extname(path)}"
end

def with_report_suffix(path)
return path if config.report_suffix.nil?

"#{path.sub(/\.\w+$/, "")}-#{config.report_suffix}#{::File.extname(path)}"
end

def notify_spring_detected
return if instance_variable_defined?(:@spring_notified)
log :info, "Spring detected"
@spring_notified = true
end

def notify_spring_activate(env_var)
log :info, "Activating #{env_var} with `Spring.after_fork`"
end
end

# TestProf configuration
class Configuration
attr_accessor :output, # IO to write logs
:color, # Whether to colorize output or not
:output_dir, # Directory to store artifacts
:timestamps, # Whether to use timestamped names for artifacts,
:report_suffix # Custom suffix for reports/artifacts

def initialize
@output = $stdout
@color = true
@output_dir = "tmp/test_prof"
@timestamps = false
@report_suffix = ENV["TEST_PROF_REPORT"]
end

def color?
color == true && output.is_a?(IO) && output.tty?
end

def timestamps?
timestamps == true
end

def logger
@logger ||= Logger.new(output, formatter: Logging::Formatter.new)
end
end
end
2 changes: 1 addition & 1 deletion lib/test_prof/factory_all_stub.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"
require "test_prof/factory_bot"
require "test_prof/factory_all_stub/factory_bot_patch"

Expand Down
2 changes: 1 addition & 1 deletion lib/test_prof/factory_default.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"
require "test_prof/factory_bot"
require "test_prof/factory_default/factory_bot_patch"
require "test_prof/ext/float_duration"
Expand Down
2 changes: 1 addition & 1 deletion lib/test_prof/recipes/logging.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"

module TestProf
module Rails
Expand Down
2 changes: 1 addition & 1 deletion lib/test_prof/recipes/rspec/let_it_be.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require "test_prof"
require "test_prof/core"
require "test_prof/recipes/rspec/before_all"

module TestProf
Expand Down

0 comments on commit 17d674d

Please sign in to comment.