1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/core/kernel/warn_spec.rb
2021-11-29 15:50:28 +01:00

309 lines
9.9 KiB
Ruby

require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#warn" do
before :each do
@before_verbose = $VERBOSE
@before_separator = $/
end
after :each do
$VERBOSE = nil
$/ = @before_separator
$VERBOSE = @before_verbose
end
it "is a private method" do
Kernel.should have_private_instance_method(:warn)
end
it "accepts multiple arguments" do
Kernel.method(:warn).arity.should < 0
end
it "does not append line-end if last character is line-end" do
-> {
$VERBOSE = true
warn("this is some simple text with line-end\n")
}.should output(nil, "this is some simple text with line-end\n")
end
it "calls #write on $stderr if $VERBOSE is true" do
-> {
$VERBOSE = true
warn("this is some simple text")
}.should output(nil, "this is some simple text\n")
end
it "calls #write on $stderr if $VERBOSE is false" do
-> {
$VERBOSE = false
warn("this is some simple text")
}.should output(nil, "this is some simple text\n")
end
it "does not call #write on $stderr if $VERBOSE is nil" do
-> {
$VERBOSE = nil
warn("this is some simple text")
}.should output(nil, "")
end
it "writes each argument on a line when passed multiple arguments" do
-> {
$VERBOSE = true
warn("line 1", "line 2")
}.should output(nil, "line 1\nline 2\n")
end
it "writes each array element on a line when passes an array" do
-> {
$VERBOSE = true
warn(["line 1", "line 2"])
}.should output(nil, "line 1\nline 2\n")
end
it "does not write strings when passed no arguments" do
-> {
$VERBOSE = true
warn
}.should output("", "")
end
it "writes the default record separator and NOT $/ to $stderr after the warning message" do
-> {
$VERBOSE = true
$/ = 'rs'
warn("")
}.should output(nil, /\n/)
end
it "writes to_s representation if passed a non-string" do
obj = mock("obj")
obj.should_receive(:to_s).and_return("to_s called")
-> {
$VERBOSE = true
warn(obj)
}.should output(nil, "to_s called\n")
end
describe ":uplevel keyword argument" do
before :each do
$VERBOSE = true
end
it "prepends a message with specified line from the backtrace" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4("foo", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: foo|)
-> { w.f4("foo", 1) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f1_call_lineno}: warning: foo|)
-> { w.f4("foo", 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: foo|)
-> { w.f4("foo", 3) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f3_call_lineno}: warning: foo|)
end
# Test both explicitly without and with RubyGems as RubyGems overrides Kernel#warn
it "shows the caller of #require and not #require itself without RubyGems" do
file = fixture(__FILE__ , "warn_require_caller.rb")
ruby_exe(file, options: "--disable-gems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
it "shows the caller of #require and not #require itself with RubyGems loaded" do
file = fixture(__FILE__ , "warn_require_caller.rb")
ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
guard -> { Kernel.instance_method(:tap).source_location } do
it "skips <internal: core library methods defined in Ruby" do
file, line = Kernel.instance_method(:tap).source_location
file.should.start_with?('<internal:')
file = fixture(__FILE__ , "warn_core_method.rb")
n = 9
ruby_exe(file, options: "--disable-gems", args: "2>&1").lines.should == [
"#{file}:#{n+0}: warning: use X instead\n",
"#{file}:#{n+1}: warning: use X instead\n",
"#{file}:#{n+2}: warning: use X instead\n",
"#{file}:#{n+4}: warning: use X instead\n",
]
end
end
ruby_version_is "3.0" do
it "accepts :category keyword with a symbol" do
-> {
$VERBOSE = true
warn("message", category: :deprecated)
}.should output(nil, "message\n")
end
it "accepts :category keyword with nil" do
-> {
$VERBOSE = true
warn("message", category: nil)
}.should output(nil, "message\n")
end
it "accepts :category keyword with object convertible to symbol" do
o = Object.new
def o.to_sym; :deprecated; end
-> {
$VERBOSE = true
warn("message", category: o)
}.should output(nil, "message\n")
end
it "raises if :category keyword is not nil and not convertible to symbol" do
-> {
$VERBOSE = true
warn("message", category: Object.new)
}.should raise_error(TypeError)
end
end
it "converts first arg using to_s" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4(false, 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: false|)
-> { w.f4(nil, 1) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f1_call_lineno}: warning: |)
obj = mock("obj")
obj.should_receive(:to_s).and_return("to_s called")
-> { w.f4(obj, 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: to_s called|)
end
it "does not prepend caller information if the uplevel argument is too large" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
end
it "prepends even if a message is empty or nil" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4("", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
-> { w.f4(nil, 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
end
it "converts value to Integer" do
w = KernelSpecs::WarnInNestedCall.new
-> { w.f4(0.1) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
-> { w.f4(Rational(1, 2)) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
end
it "raises ArgumentError if passed negative value" do
-> { warn "", uplevel: -2 }.should raise_error(ArgumentError)
-> { warn "", uplevel: -100 }.should raise_error(ArgumentError)
end
it "raises ArgumentError if passed -1" do
-> { warn "", uplevel: -1 }.should raise_error(ArgumentError)
end
it "raises TypeError if passed not Integer" do
-> { warn "", uplevel: "" }.should raise_error(TypeError)
-> { warn "", uplevel: [] }.should raise_error(TypeError)
-> { warn "", uplevel: {} }.should raise_error(TypeError)
-> { warn "", uplevel: Object.new }.should raise_error(TypeError)
end
end
it "treats empty hash as no keyword argument" do
h = {}
-> { warn(**h) }.should_not complain(verbose: true)
-> { warn('foo', **h) }.should complain("foo\n")
end
ruby_version_is '3.0' do
it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
verbose = $VERBOSE
$VERBOSE = false
class << Warning
alias_method :_warn, :warn
def warn(message)
ScratchPad.record(message)
end
end
begin
ScratchPad.clear
Kernel.warn("Chunky bacon!")
ScratchPad.recorded.should == "Chunky bacon!\n"
Kernel.warn("Deprecated bacon!", category: :deprecated)
ScratchPad.recorded.should == "Deprecated bacon!\n"
ensure
class << Warning
remove_method :warn
alias_method :warn, :_warn
remove_method :_warn
end
$VERBOSE = verbose
end
end
it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!")
ensure
$VERBOSE = verbose
end
end
it "calls Warning.warn with given category keyword converted to a symbol" do
Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!", category: 'deprecated')
ensure
$VERBOSE = verbose
end
end
end
ruby_version_is ''...'3.0' do
it "calls Warning.warn with no keyword arguments" do
Warning.should_receive(:warn).with("Chunky bacon!\n")
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!")
ensure
$VERBOSE = verbose
end
end
end
it "does not call Warning.warn if self is the Warning module" do
# RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
code = <<-RUBY
def Warning.warn(*args, **kwargs)
raise 'should not be called'
end
Kernel.instance_method(:warn).bind(Warning).call('Kernel#warn spec edge case')
RUBY
out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
out.should == "Kernel#warn spec edge case\n"
$?.should.success?
end
it "avoids recursion if Warning#warn is redefined and calls super" do
# This works because of the spec above, which is the workaround for it.
# Note that redefining Warning#warn is a mistake which would naturally end in infinite recursion,
# Warning.extend Module.new { def warn } should be used instead.
# RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
code = <<-RUBY
module Warning
def warn(*args, **kwargs)
super
end
end
warn "avoid infinite recursion"
RUBY
out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
out.should == "avoid infinite recursion\n"
$?.should.success?
end
end