119 lines
4.0 KiB
Ruby
119 lines
4.0 KiB
Ruby
# Logs if constants created by module loading are left over after suite has completed.
|
|
module Metasploit::Framework::Spec::Constants::Suite
|
|
#
|
|
# CONSTANTS
|
|
#
|
|
|
|
LOGS_PATHNAME = Pathname.new('log/metasploit/framework/spec/constants/suite')
|
|
|
|
# Logs leaked constants to {LOG_PATHNAME} and prints `message` to stderr.
|
|
#
|
|
# @param hook (see log_pathname)
|
|
# @param message [String] additional message printed to stderr when there is at least one leaked constant.
|
|
# @return [void]
|
|
def self.log_leaked_constants(hook, message)
|
|
count = 0
|
|
hook_log_pathname = log_pathname(hook)
|
|
hook_log_pathname.parent.mkpath
|
|
|
|
hook_log_pathname.open('w') do |f|
|
|
count = Metasploit::Framework::Spec::Constants.each do |child_name|
|
|
f.puts child_name
|
|
end
|
|
end
|
|
|
|
if count > 0
|
|
$stderr.puts "#{count} #{'constant'.pluralize(count)} leaked under " \
|
|
"#{Metasploit::Framework::Spec::Constants::PARENT_CONSTANT}. #{message} See #{hook_log_pathname} " \
|
|
"for details."
|
|
else
|
|
hook_log_pathname.delete
|
|
end
|
|
end
|
|
|
|
# Configures after(:suite) callback for RSpec to check for leaked constants.
|
|
def self.configure!
|
|
unless @configured
|
|
RSpec.configure do |config|
|
|
config.before(:suite) do
|
|
Metasploit::Framework::Spec::Constants::Suite.log_leaked_constants(
|
|
:before,
|
|
'Modules are being loaded outside callbacks before suite starts.'
|
|
)
|
|
end
|
|
|
|
config.after(:suite) do
|
|
Metasploit::Framework::Spec::Constants::Suite.log_leaked_constants(
|
|
:after,
|
|
'Modules are being loaded inside callbacks or examples during suite run.'
|
|
)
|
|
end
|
|
end
|
|
|
|
@configured = true
|
|
end
|
|
end
|
|
|
|
# Adds action to `spec` task so that `rake spec` fails if `log/leaked-constants.log` exists after printing out the
|
|
# leaked constants.
|
|
#
|
|
# @return [void]
|
|
def self.define_task
|
|
Rake::Task.define_task(:spec) do
|
|
leaked_before = Metasploit::Framework::Spec::Constants::Suite.print_leaked_constants(:before)
|
|
leaked_after = Metasploit::Framework::Spec::Constants::Suite.print_leaked_constants(:after)
|
|
|
|
# leaks after suite can be be cleaned up by {Metasploit::Framework::Spec::Constants::Each.configure!}, but
|
|
# leaks before suite require user intervention to find the leaks since it's a programming error in how the specs
|
|
# are written where Modules are being loaded in the context scope.
|
|
if leaked_after
|
|
$stderr.puts
|
|
$stderr.puts "Add `Metasploit::Framework::Spec::Constants::Each.configure!` to `spec/spec_helper.rb` " \
|
|
"**NOTE: `Metasploit::Framework::Spec::Constants::Each` may report false leaks if `after(:all)` " \
|
|
"is used to clean up constants instead of `after(:each)`**"
|
|
end
|
|
|
|
if leaked_before || leaked_after
|
|
exit 1
|
|
end
|
|
end
|
|
end
|
|
|
|
# @param hook [:after, :before] Whether the log is recording leaked constants `:before` the suite runs or `:after` the
|
|
# suite runs.
|
|
def self.log_pathname(hook)
|
|
LOGS_PATHNAME.join("#{hook}.log")
|
|
end
|
|
|
|
# Prints logged leaked constants to stderr.
|
|
#
|
|
# @param hook [:after, :before] Whether the log is recording leaked constants `:before` the suite runs or `:after` the
|
|
# suite runs.
|
|
# @return [true] if leaks printed
|
|
# @return [false] otherwise
|
|
def self.print_leaked_constants(hook)
|
|
hook_log_pathname = log_pathname(hook)
|
|
|
|
leaks = false
|
|
|
|
if hook_log_pathname.exist?
|
|
leaks = true
|
|
$stderr.puts "Leaked constants detected under #{Metasploit::Framework::Spec::Constants::PARENT_CONSTANT} #{hook} suite:"
|
|
|
|
hook_log_pathname.open do |f|
|
|
f.each_line do |line|
|
|
constant_name = line.strip
|
|
full_name = Metasploit::Framework::Spec::Constants.full_name(constant_name)
|
|
|
|
if full_name
|
|
formatted_full_name = " # #{full_name}"
|
|
end
|
|
|
|
$stderr.puts " #{constant_name}#{formatted_full_name}"
|
|
end
|
|
end
|
|
end
|
|
|
|
leaks
|
|
end
|
|
end |