summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel/warn_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/kernel/warn_spec.rb')
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb244
1 files changed, 193 insertions, 51 deletions
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
index 2a404a2a35..189129dd31 100644
--- a/spec/ruby/core/kernel/warn_spec.rb
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -8,15 +8,16 @@ describe "Kernel#warn" do
end
after :each do
- $VERBOSE = @before_verbose
+ $VERBOSE = nil
$/ = @before_separator
+ $VERBOSE = @before_verbose
end
it "is a private method" do
- Kernel.should have_private_instance_method(:warn)
+ Kernel.private_instance_methods(false).should.include?(:warn)
end
- it "requires multiple arguments" do
+ it "accepts multiple arguments" do
Kernel.method(:warn).arity.should < 0
end
@@ -86,71 +87,212 @@ describe "Kernel#warn" do
}.should output(nil, "to_s called\n")
end
- ruby_version_is "2.5" do
- describe ":uplevel keyword argument" do
- before :each do
- $VERBOSE = true
- 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
+ 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
+ -> { 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
- it "converts first arg using to_s" do
- w = KernelSpecs::WarnInNestedCall.new
+ # 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
- -> { 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 "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
- it "does not prepend caller information if line number is too big" do
- w = KernelSpecs::WarnInNestedCall.new
- -> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
- end
+ it "doesn't show the caller when the uplevel is `nil`" do
+ w = KernelSpecs::WarnInNestedCall.new
- it "prepends even if a message is empty or nil" do
- w = KernelSpecs::WarnInNestedCall.new
+ -> { w.f4("foo", nil) }.should output(nil, "foo\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:')
- -> { 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$|)
+ 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
- it "converts value to Integer" do
- w = KernelSpecs::WarnInNestedCall.new
+ it "accepts :category keyword with a symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: :deprecated)
+ }.should output(nil, "message\n")
+ end
- -> { 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 "accepts :category keyword with nil" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: nil)
+ }.should output(nil, "message\n")
+ 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 "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 ArgumentError if passed -1" do
- -> { warn "", uplevel: -1 }.should raise_error(ArgumentError)
+ it "raises if :category keyword is not nil and not convertible to symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: Object.new)
+ }.should.raise(TypeError)
+ 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(ArgumentError)
+ -> { warn "", uplevel: -100 }.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError if passed -1" do
+ -> { warn "", uplevel: -1 }.should.raise(ArgumentError)
+ end
+
+ it "raises TypeError if passed not Integer" do
+ -> { warn "", uplevel: "" }.should.raise(TypeError)
+ -> { warn "", uplevel: [] }.should.raise(TypeError)
+ -> { warn "", uplevel: {} }.should.raise(TypeError)
+ -> { warn "", uplevel: Object.new }.should.raise(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
+
+ 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
- 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)
+ 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
- it "treats empty hash as no keyword argument" do
- h = {}
- -> { warn(**h) }.should_not complain(verbose: true)
- -> { warn('foo', **h) }.should complain("foo\n")
+ 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