mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
a21ac07f69
or an invalid value is returned in #coerce called by numeric comparison operators and the exception thrown by the caller has no information on the failure. In the next release such exception should not be rescued or should be the cause of the caller exception. nil is accepted as the "no possible coercion" return value. See #7688. * test/ruby/test_numeric.rb: Add corresponding test. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@46375 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
363 lines
10 KiB
Ruby
363 lines
10 KiB
Ruby
require 'test/unit'
|
|
require_relative 'envutil'
|
|
|
|
class TestNumeric < Test::Unit::TestCase
|
|
class DummyNumeric < Numeric
|
|
end
|
|
|
|
def test_coerce
|
|
a, b = 1.coerce(2)
|
|
assert_equal(Fixnum, a.class)
|
|
assert_equal(Fixnum, b.class)
|
|
|
|
a, b = 1.coerce(2.0)
|
|
assert_equal(Float, a.class)
|
|
assert_equal(Float, b.class)
|
|
|
|
assert_raise(TypeError) { -Numeric.new }
|
|
|
|
assert_raise_with_message(TypeError, /can't be coerced into /) {1+:foo}
|
|
assert_raise_with_message(TypeError, /can't be coerced into /) {1&:foo}
|
|
assert_raise_with_message(TypeError, /can't be coerced into /) {1|:foo}
|
|
assert_raise_with_message(TypeError, /can't be coerced into /) {1^:foo}
|
|
|
|
EnvUtil.with_default_external(Encoding::UTF_8) do
|
|
assert_raise_with_message(TypeError, /:\u{3042}/) {1+:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:\u{3042}/) {1&:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:\u{3042}/) {1|:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:\u{3042}/) {1^:"\u{3042}"}
|
|
end
|
|
EnvUtil.with_default_external(Encoding::US_ASCII) do
|
|
assert_raise_with_message(TypeError, /:"\\u3042"/) {1+:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:"\\u3042"/) {1&:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:"\\u3042"/) {1|:"\u{3042}"}
|
|
assert_raise_with_message(TypeError, /:"\\u3042"/) {1^:"\u{3042}"}
|
|
end
|
|
end
|
|
|
|
def test_dummynumeric
|
|
a = DummyNumeric.new
|
|
|
|
DummyNumeric.class_eval do
|
|
def coerce(x); nil; end
|
|
end
|
|
assert_raise(TypeError) { -a }
|
|
assert_nil(1 <=> a)
|
|
assert_raise(ArgumentError) { 1 <= a }
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :coerce
|
|
def coerce(x); 1.coerce(x); end
|
|
end
|
|
assert_equal(2, 1 + a)
|
|
assert_equal(0, 1 <=> a)
|
|
assert_operator(1, :<=, a)
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :coerce
|
|
def coerce(x); [x, 1]; end
|
|
end
|
|
assert_equal(-1, -a)
|
|
|
|
bug7688 = '[ruby-core:51389] [Bug #7688]'
|
|
DummyNumeric.class_eval do
|
|
remove_method :coerce
|
|
def coerce(x); raise StandardError; end
|
|
end
|
|
assert_raise_with_message(TypeError, /can't be coerced into /) { 1 + a }
|
|
warn = /will no more rescue exceptions of #coerce.+ in the next release/m
|
|
assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :coerce
|
|
def coerce(x); :bad_return_value; end
|
|
end
|
|
assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 + a }
|
|
warn = /Bad return value for #coerce.+next release will raise an error/m
|
|
assert_warn(warn, bug7688) { assert_raise(ArgumentError) { 1 < a } }
|
|
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :coerce
|
|
end
|
|
end
|
|
|
|
def test_singleton_method
|
|
a = Numeric.new
|
|
assert_raise_with_message(TypeError, /foo/) { def a.foo; end }
|
|
assert_raise_with_message(TypeError, /\u3042/) { eval("def a.\u3042; end") }
|
|
end
|
|
|
|
def test_dup
|
|
a = Numeric.new
|
|
assert_raise(TypeError) { a.dup }
|
|
|
|
c = Module.new do
|
|
break eval("class C\u{3042} < Numeric; self; end")
|
|
end
|
|
assert_raise_with_message(TypeError, /C\u3042/) {c.new.dup}
|
|
end
|
|
|
|
def test_quo
|
|
assert_raise(TypeError) {DummyNumeric.new.quo(1)}
|
|
end
|
|
|
|
def test_quo_ruby_core_41575
|
|
x = DummyNumeric.new
|
|
rat = 84.quo(1)
|
|
DummyNumeric.class_eval do
|
|
define_method(:to_r) { rat }
|
|
end
|
|
assert_equal(2.quo(1), x.quo(42), '[ruby-core:41575]')
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :to_r
|
|
end
|
|
end
|
|
|
|
def test_divmod
|
|
=begin
|
|
DummyNumeric.class_eval do
|
|
def /(x); 42.0; end
|
|
def %(x); :mod; end
|
|
end
|
|
|
|
assert_equal(42, DummyNumeric.new.div(1))
|
|
assert_equal(:mod, DummyNumeric.new.modulo(1))
|
|
assert_equal([42, :mod], DummyNumeric.new.divmod(1))
|
|
=end
|
|
|
|
assert_kind_of(Integer, 11.divmod(3.5).first, '[ruby-dev:34006]')
|
|
|
|
=begin
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :/, :%
|
|
end
|
|
=end
|
|
end
|
|
|
|
def test_real_p
|
|
assert_predicate(Numeric.new, :real?)
|
|
end
|
|
|
|
def test_integer_p
|
|
assert_not_predicate(Numeric.new, :integer?)
|
|
end
|
|
|
|
def test_abs
|
|
a = DummyNumeric.new
|
|
DummyNumeric.class_eval do
|
|
def -@; :ok; end
|
|
def <(x); true; end
|
|
end
|
|
|
|
assert_equal(:ok, a.abs)
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :<
|
|
def <(x); false; end
|
|
end
|
|
|
|
assert_equal(a, a.abs)
|
|
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :-@, :<
|
|
end
|
|
end
|
|
|
|
def test_zero_p
|
|
DummyNumeric.class_eval do
|
|
def ==(x); true; end
|
|
end
|
|
|
|
assert_predicate(DummyNumeric.new, :zero?)
|
|
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :==
|
|
end
|
|
end
|
|
|
|
def test_to_int
|
|
DummyNumeric.class_eval do
|
|
def to_i; :ok; end
|
|
end
|
|
|
|
assert_equal(:ok, DummyNumeric.new.to_int)
|
|
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :to_i
|
|
end
|
|
end
|
|
|
|
def test_cmp
|
|
a = Numeric.new
|
|
assert_equal(0, a <=> a)
|
|
assert_nil(a <=> :foo)
|
|
end
|
|
|
|
def test_floor_ceil_round_truncate
|
|
DummyNumeric.class_eval do
|
|
def to_f; 1.5; end
|
|
end
|
|
|
|
a = DummyNumeric.new
|
|
assert_equal(1, a.floor)
|
|
assert_equal(2, a.ceil)
|
|
assert_equal(2, a.round)
|
|
assert_equal(1, a.truncate)
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :to_f
|
|
def to_f; 1.4; end
|
|
end
|
|
|
|
a = DummyNumeric.new
|
|
assert_equal(1, a.floor)
|
|
assert_equal(2, a.ceil)
|
|
assert_equal(1, a.round)
|
|
assert_equal(1, a.truncate)
|
|
|
|
DummyNumeric.class_eval do
|
|
remove_method :to_f
|
|
def to_f; -1.5; end
|
|
end
|
|
|
|
a = DummyNumeric.new
|
|
assert_equal(-2, a.floor)
|
|
assert_equal(-1, a.ceil)
|
|
assert_equal(-2, a.round)
|
|
assert_equal(-1, a.truncate)
|
|
|
|
ensure
|
|
DummyNumeric.class_eval do
|
|
remove_method :to_f
|
|
end
|
|
end
|
|
|
|
def assert_step(expected, (from, *args), inf: false)
|
|
enum = from.step(*args)
|
|
size = enum.size
|
|
xsize = expected.size
|
|
|
|
if inf
|
|
assert_send [size, :infinite?], "step size: +infinity"
|
|
assert_send [size, :>, 0], "step size: +infinity"
|
|
|
|
a = []
|
|
from.step(*args) { |x| a << x; break if a.size == xsize }
|
|
assert_equal expected, a, "step"
|
|
|
|
a = []
|
|
enum.each { |x| a << x; break if a.size == xsize }
|
|
assert_equal expected, a, "step enumerator"
|
|
else
|
|
assert_equal expected.size, size, "step size"
|
|
|
|
a = []
|
|
from.step(*args) { |x| a << x }
|
|
assert_equal expected, a, "step"
|
|
|
|
a = []
|
|
enum.each { |x| a << x }
|
|
assert_equal expected, a, "step enumerator"
|
|
end
|
|
end
|
|
|
|
def test_step
|
|
i, bignum = 32, 1 << 30
|
|
bignum <<= (i <<= 1) - 32 until bignum.is_a?(Bignum)
|
|
assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
|
|
assert_raise(ArgumentError) { 1.step(10, 1, 0).size }
|
|
assert_raise(ArgumentError) { 1.step(10, 0) { } }
|
|
assert_raise(ArgumentError) { 1.step(10, 0).size }
|
|
assert_raise(ArgumentError) { 1.step(10, "1") { } }
|
|
assert_raise(ArgumentError) { 1.step(10, "1").size }
|
|
assert_raise(TypeError) { 1.step(10, nil) { } }
|
|
assert_raise(TypeError) { 1.step(10, nil).size }
|
|
assert_nothing_raised { 1.step(by: 0, to: nil) }
|
|
assert_nothing_raised { 1.step(by: 0, to: nil).size }
|
|
assert_nothing_raised { 1.step(by: 0) }
|
|
assert_nothing_raised { 1.step(by: 0).size }
|
|
assert_nothing_raised { 1.step(by: nil) }
|
|
assert_nothing_raised { 1.step(by: nil).size }
|
|
|
|
bug9811 = '[ruby-dev:48177] [Bug #9811]'
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil) {} }
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil).size }
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11) {} }
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11).size }
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11) {} }
|
|
assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11).size }
|
|
|
|
assert_equal(bignum*2+1, (-bignum).step(bignum, 1).size)
|
|
assert_equal(bignum*2, (-bignum).step(bignum-1, 1).size)
|
|
|
|
assert_equal(10+1, (0.0).step(10.0, 1.0).size)
|
|
|
|
i, bigflo = 1, bignum.to_f
|
|
i <<= 1 until (bigflo - i).to_i < bignum
|
|
bigflo -= i >> 1
|
|
assert_equal(bigflo.to_i, (0.0).step(bigflo-1.0, 1.0).size)
|
|
assert_operator((0.0).step(bignum.to_f, 1.0).size, :>=, bignum) # may loose precision
|
|
|
|
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 10]
|
|
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10]
|
|
assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10, by: nil]
|
|
assert_step [1, 3, 5, 7, 9], [1, 10, 2]
|
|
assert_step [1, 3, 5, 7, 9], [1, to: 10, by: 2]
|
|
|
|
assert_step [10, 8, 6, 4, 2], [10, 1, -2]
|
|
assert_step [10, 8, 6, 4, 2], [10, to: 1, by: -2]
|
|
assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, 10.0, 2.0]
|
|
assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, to: 10.0, by: 2.0]
|
|
assert_step [1], [1, 10, bignum]
|
|
assert_step [1], [1, to: 10, by: bignum]
|
|
|
|
assert_step [], [2, 1, 3]
|
|
assert_step [], [-2, -1, -3]
|
|
assert_step [3, 3, 3, 3], [3, by: 0], inf: true
|
|
assert_step [3, 3, 3, 3], [3, by: 0, to: 42], inf: true
|
|
assert_step [10], [10, 1, -bignum]
|
|
|
|
assert_step [], [1, 0, Float::INFINITY]
|
|
assert_step [], [0, 1, -Float::INFINITY]
|
|
assert_step [10], [10, to: 1, by: -bignum]
|
|
|
|
assert_step [10, 11, 12, 13], [10], inf: true
|
|
assert_step [10, 9, 8, 7], [10, by: -1], inf: true
|
|
assert_step [10, 9, 8, 7], [10, by: -1, to: nil], inf: true
|
|
|
|
assert_step [42, 42, 42, 42], [42, by: 0, to: -Float::INFINITY], inf: true
|
|
assert_step [42, 42, 42, 42], [42, by: 0, to: 42.5], inf: true
|
|
assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: 0.0], inf: true
|
|
assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: -0.0], inf: true
|
|
assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 44], inf: true
|
|
assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 0], inf: true
|
|
assert_step [42.0, 42.0, 42.0, 42.0], [42, by: -0.0, to: 44], inf: true
|
|
|
|
assert_step [bignum]*4, [bignum, by: 0], inf: true
|
|
assert_step [bignum]*4, [bignum, by: 0.0], inf: true
|
|
assert_step [bignum]*4, [bignum, by: 0, to: bignum+1], inf: true
|
|
assert_step [bignum]*4, [bignum, by: 0, to: 0], inf: true
|
|
end
|
|
|
|
def test_num2long
|
|
assert_raise(TypeError) { 1 & nil }
|
|
assert_raise(TypeError) { 1 & 1.0 }
|
|
assert_raise(TypeError) { 1 & 2147483648.0 }
|
|
assert_raise(TypeError) { 1 & 9223372036854777856.0 }
|
|
o = Object.new
|
|
def o.to_int; 1; end
|
|
assert_raise(TypeError) { assert_equal(1, 1 & o) }
|
|
end
|
|
|
|
def test_eql
|
|
assert_equal(1, 1.0)
|
|
assert_not_operator(1, :eql?, 1.0)
|
|
assert_not_operator(1, :eql?, 2)
|
|
end
|
|
end
|