require 'test/unit' class TestNumeric < Test::Unit::TestCase class NumericWithSomeMethods < Numeric def initialize(value = 0) @value = value end def expect(arg) @expected_arg = arg end def check(arg) raise "Expected #{@expected_arg.inspect}, but received #{arg.inspect}" unless @expected_arg == arg end def /(arg) check(arg); 543.21 end def %(arg) check(arg); 89 end def to_f() @value.to_f end def <=>(other) @value <=> other end def <(other) @value < other end def >(other) @value > other end def eql?(other) @value.eql?(other) end def to_i() @value.to_i end end def setup @a = Numeric.new @b = Numeric.new end def test_unary_plus assert_same @a, +@a end def test_unary_minus_should_raise_if_self_cannot_be_coerced_to_float assert_raises(TypeError) { -@a } end def test_unary_minus_should_coerce_to_float_and_negate_result assert_equal 123.45, NumericWithSomeMethods.new(123.45).to_f assert_equal -123.45, -NumericWithSomeMethods.new(123.45) end def test_spaceship_should_return_zero_when_comparting_with_self_and_nil_otherwise assert_equal 0, @a <=> @a assert_nil @a <=> @b assert_nil @a <=> 1 assert_nil @a <=> "" assert_nil @a <=> nil end def test_abs_should_return_self_if_bigger_than_zero_and_result_of_unitary_minus_on_self_otherwise assert_raises(ArgumentError) { @a.abs } positive = NumericWithSomeMethods.new(1).abs assert positive > 0 assert_same positive, positive.abs negative = NumericWithSomeMethods.new(-2) assert negative < 0 # unitary minus operator effectively returns -(self.to_f) assert_equal 2.0, negative.abs assert_equal Float, negative.abs.class end # ceil def test_ceil_should_delegate_to_self_to_f assert_equal 124, NumericWithSomeMethods.new(123.49).ceil assert_equal -123, NumericWithSomeMethods.new(-123.51).ceil end def test_ceil_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.ceil } end def test_coerce_should_copy_argument_and_self_if_both_have_the_same_type assert_equal [@b, @a], @a.coerce(@b) assert_equal [0.9, 0.1], 0.1.coerce(0.9) assert_equal [0, 1], 1.coerce(0) end def test_coerce_should_call_float_on_self_and_arg_if_they_are_of_different_type bignum = 100000000000000000000000 assert bignum.is_a?(Bignum) assert_equal [Float(bignum), 0.0], 0.0.coerce(bignum) assert 0.0.coerce(bignum).first.is_a?(Float) assert 0.0.coerce(bignum).last.is_a?(Float) n = NumericWithSomeMethods.new(123.45) assert_equal [1.0, 123.45], n.coerce(1.0) end def test_coerce_should_blow_up_if_self_or_arg_cannot_be_converted_to_float_by_kernel_Float assert_raises(TypeError) { @a.coerce(1.0) } assert_raises(ArgumentError) { 1.coerce("test") } assert_raises(TypeError) { 1.coerce(nil) } assert_raises(TypeError) { 1.coerce(false) } end def test_div_should_raise_if_self_doesnt_implement_divison_operator assert_raises(NoMethodError) { @a.div(@b) } end def test_div_should_call_division_method_and_floor_it n = NumericWithSomeMethods.new # n / anything returns 543.21 n.expect(:foo) assert_equal 543.21, n / :foo n.expect(@a) assert_equal 543, n.div(@a) end def test_divmod_should_return_result_of_div_and_mod_as_array n = NumericWithSomeMethods.new n.expect(:foo) # n.div(anything) returns 543, n % anything returns 89 assert_equal [543, 89], n.divmod(:foo) end def test_divmod_should_raise_when_self_doesnt_implement_div_or_mod assert_raises(NoMethodError) { @a.divmod(@b) } end def test_eql assert_equal true, @a.eql?(@a) assert_equal false, @a.eql?(@b) end def test_floor_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.51).floor assert_equal -124, NumericWithSomeMethods.new(-123.49).floor end def test_floor_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.floor } end def test_initialize_copy_should_raise assert_raises(TypeError) { @a.instance_eval("initialize_copy(:foo)") } end def test_initialize_copy_should_be_private assert @a.private_methods.include?("initialize_copy") assert_equal false, @a.methods.include?("initialize_copy") end def test_integer assert_equal false, @a.integer? end def test_modulo_should_raise_if_self_doesnt_have_percent_operator assert_raises(NoMethodError) { @a.modulo(@b) } end def test_modulo_should_call_percent_operator n = NumericWithSomeMethods.new n.expect(:foo) # n % anything returns 89 assert_equal 89, n.modulo(:foo) end def test_nonzero_should_check_equality_and_returns_self_or_nil assert_same @a, @a.nonzero? assert_nil NumericWithSomeMethods.new(0).nonzero? one = NumericWithSomeMethods.new(1) minus_one = NumericWithSomeMethods.new(1) assert_same one, one.nonzero? assert_same minus_one, minus_one.nonzero? end def test_quo assert_raises(NoMethodError) { @a.quo @b } end def test_remainder_should_raise_if_self_doesnt_implement_modulo assert_raises(NoMethodError) { @a.remainder(@b) } end def test_remainder_should_return_modulo_if_self_and_arg_have_the_same_sign_and_modulo_minus_arg_otherwise_except_when_modulo_is_zero # Float doesn't override Numeric#remainder, so that's what we are testng here assert_equal 2.0, 5.0.remainder(3) assert_equal 2.0, 5.0.remainder(-3) assert_equal -2.0, -5.0.remainder(-3) assert_equal -2.0, -5.0.remainder(3) # special case, was a bug assert_equal 0.0, 4.0.remainder(2) assert_equal 0.0, 4.0.remainder(-2) assert_equal 0.0, -4.0.remainder(2) assert_equal 0.0, -4.0.remainder(-2) end def test_round_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.49).round assert_equal 124, NumericWithSomeMethods.new(123.51).round assert_equal -123, NumericWithSomeMethods.new(-123.49).round assert_equal -124, NumericWithSomeMethods.new(-123.51).round end def test_round_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.round } end def test_step # Fixnum doesn't override Numeric#step() # ends exactly at :to a = [] 1.step(5, 2) { |x| a << x } assert_equal [1, 3, 5], a # ends before :to a = [] 1.step(4, 2) { |x| a << x } assert_equal [1, 3], a # step is too big a = [] 1.step(4, 10) { |x| a << x } assert_equal [1], a # step is zero assert_raises(ArgumentError) { 1.step(1, 0) } # same to and from a = [] 1.step(1, 1) { |x| a << x } assert_equal [1], a # from less than to, positive step value a = [] 1.step(0, 1) { |x| a << x } assert_equal [], a # from less than to, negative step value a = [] 1.step(0, 1) { |x| a << x } assert_equal [], a # default step value of 1 a = [] 1.step(3) { |x| a << x } assert_equal [1, 2, 3], a end def test_step_should_raise_if_step_is_zero end def test_to_int_should_call_to_i_or_raise_if_to_i_is_not_implemented assert_equal 123, NumericWithSomeMethods.new(123).to_int assert_raises(NoMethodError) { @a.to_int } end def test_truncate_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.49).truncate assert_equal 123, NumericWithSomeMethods.new(123.51).truncate assert_equal -123, NumericWithSomeMethods.new(-123.49).truncate assert_equal -123, NumericWithSomeMethods.new(-123.51).truncate end def test_truncate_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.truncate } end def test_zero_should_check_equality assert_equal false, @a.zero? assert_equal true, NumericWithSomeMethods.new(0).zero? assert_equal false, NumericWithSomeMethods.new(1).zero? assert_equal false, NumericWithSomeMethods.new(-1).zero? end end require 'test/unit' class TestNumeric < Test::Unit::TestCase class NumericWithSomeMethods < Numeric def initialize(value = 0) @value = value end def expect(arg) @expected_arg = arg end def check(arg) raise "Expected #{@expected_arg.inspect}, but received #{arg.inspect}" unless @expected_arg == arg end def /(arg) check(arg); 543.21 end def %(arg) check(arg); 89 end def to_f() @value.to_f end def <=>(other) @value <=> other end def <(other) @value < other end def >(other) @value > other end def eql?(other) @value.eql?(other) end def to_i() @value.to_i end end def setup @a = Numeric.new @b = Numeric.new end def test_unary_plus assert_same @a, +@a end def test_unary_minus_should_raise_if_self_cannot_be_coerced_to_float assert_raises(TypeError) { -@a } end def test_unary_minus_should_coerce_to_float_and_negate_result assert_equal 123.45, NumericWithSomeMethods.new(123.45).to_f assert_equal -123.45, -NumericWithSomeMethods.new(123.45) end def test_spaceship_should_return_zero_when_comparting_with_self_and_nil_otherwise assert_equal 0, @a <=> @a assert_nil @a <=> @b assert_nil @a <=> 1 assert_nil @a <=> "" assert_nil @a <=> nil end def test_abs_should_return_self_if_bigger_than_zero_and_result_of_unitary_minus_on_self_otherwise assert_raises(ArgumentError) { @a.abs } positive = NumericWithSomeMethods.new(1).abs assert positive > 0 assert_same positive, positive.abs negative = NumericWithSomeMethods.new(-2) assert negative < 0 # unitary minus operator effectively returns -(self.to_f) assert_equal 2.0, negative.abs assert_equal Float, negative.abs.class end # ceil def test_ceil_should_delegate_to_self_to_f assert_equal 124, NumericWithSomeMethods.new(123.49).ceil assert_equal -123, NumericWithSomeMethods.new(-123.51).ceil end def test_ceil_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.ceil } end def test_coerce_should_copy_argument_and_self_if_both_have_the_same_type assert_equal [@b, @a], @a.coerce(@b) assert_equal [0.9, 0.1], 0.1.coerce(0.9) assert_equal [0, 1], 1.coerce(0) end def test_coerce_should_call_float_on_self_and_arg_if_they_are_of_different_type bignum = 100000000000000000000000 assert bignum.is_a?(Bignum) assert_equal [Float(bignum), 0.0], 0.0.coerce(bignum) assert 0.0.coerce(bignum).first.is_a?(Float) assert 0.0.coerce(bignum).last.is_a?(Float) n = NumericWithSomeMethods.new(123.45) assert_equal [1.0, 123.45], n.coerce(1.0) end def test_coerce_should_blow_up_if_self_or_arg_cannot_be_converted_to_float_by_kernel_Float assert_raises(TypeError) { @a.coerce(1.0) } assert_raises(ArgumentError) { 1.coerce("test") } assert_raises(TypeError) { 1.coerce(nil) } assert_raises(TypeError) { 1.coerce(false) } end def test_div_should_raise_if_self_doesnt_implement_divison_operator assert_raises(NoMethodError) { @a.div(@b) } end def test_div_should_call_division_method_and_floor_it n = NumericWithSomeMethods.new # n / anything returns 543.21 n.expect(:foo) assert_equal 543.21, n / :foo n.expect(@a) assert_equal 543, n.div(@a) end def test_divmod_should_return_result_of_div_and_mod_as_array n = NumericWithSomeMethods.new n.expect(:foo) # n.div(anything) returns 543, n % anything returns 89 assert_equal [543, 89], n.divmod(:foo) end def test_divmod_should_raise_when_self_doesnt_implement_div_or_mod assert_raises(NoMethodError) { @a.divmod(@b) } end def test_eql assert_equal true, @a.eql?(@a) assert_equal false, @a.eql?(@b) end def test_floor_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.51).floor assert_equal -124, NumericWithSomeMethods.new(-123.49).floor end def test_floor_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.floor } end def test_initialize_copy_should_raise assert_raises(TypeError) { @a.instance_eval("initialize_copy(:foo)") } end def test_initialize_copy_should_be_private assert @a.private_methods.include?("initialize_copy") assert_equal false, @a.methods.include?("initialize_copy") end def test_integer assert_equal false, @a.integer? end def test_modulo_should_raise_if_self_doesnt_have_percent_operator assert_raises(NoMethodError) { @a.modulo(@b) } end def test_modulo_should_call_percent_operator n = NumericWithSomeMethods.new n.expect(:foo) # n % anything returns 89 assert_equal 89, n.modulo(:foo) end def test_nonzero_should_check_equality_and_returns_self_or_nil assert_same @a, @a.nonzero? assert_nil NumericWithSomeMethods.new(0).nonzero? one = NumericWithSomeMethods.new(1) minus_one = NumericWithSomeMethods.new(1) assert_same one, one.nonzero? assert_same minus_one, minus_one.nonzero? end def test_quo assert_raises(NoMethodError) { @a.quo @b } end def test_remainder_should_raise_if_self_doesnt_implement_modulo assert_raises(NoMethodError) { @a.remainder(@b) } end def test_remainder_should_return_modulo_if_self_and_arg_have_the_same_sign_and_modulo_minus_arg_otherwise_except_when_modulo_is_zero # Float doesn't override Numeric#remainder, so that's what we are testng here assert_equal 2.0, 5.0.remainder(3) assert_equal 2.0, 5.0.remainder(-3) assert_equal -2.0, -5.0.remainder(-3) assert_equal -2.0, -5.0.remainder(3) # special case, was a bug assert_equal 0.0, 4.0.remainder(2) assert_equal 0.0, 4.0.remainder(-2) assert_equal 0.0, -4.0.remainder(2) assert_equal 0.0, -4.0.remainder(-2) end def test_round_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.49).round assert_equal 124, NumericWithSomeMethods.new(123.51).round assert_equal -123, NumericWithSomeMethods.new(-123.49).round assert_equal -124, NumericWithSomeMethods.new(-123.51).round end def test_round_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.round } end def test_step # Fixnum doesn't override Numeric#step() # ends exactly at :to a = [] 1.step(5, 2) { |x| a << x } assert_equal [1, 3, 5], a # ends before :to a = [] 1.step(4, 2) { |x| a << x } assert_equal [1, 3], a # step is too big a = [] 1.step(4, 10) { |x| a << x } assert_equal [1], a # step is zero assert_raises(ArgumentError) { 1.step(1, 0) } # same to and from a = [] 1.step(1, 1) { |x| a << x } assert_equal [1], a # from less than to, positive step value a = [] 1.step(0, 1) { |x| a << x } assert_equal [], a # from less than to, negative step value a = [] 1.step(0, 1) { |x| a << x } assert_equal [], a # default step value of 1 a = [] 1.step(3) { |x| a << x } assert_equal [1, 2, 3], a end def test_step_should_raise_if_step_is_zero end def test_to_int_should_call_to_i_or_raise_if_to_i_is_not_implemented assert_equal 123, NumericWithSomeMethods.new(123).to_int assert_raises(NoMethodError) { @a.to_int } end def test_truncate_should_delegate_to_self_to_f assert_equal 123, NumericWithSomeMethods.new(123.49).truncate assert_equal 123, NumericWithSomeMethods.new(123.51).truncate assert_equal -123, NumericWithSomeMethods.new(-123.49).truncate assert_equal -123, NumericWithSomeMethods.new(-123.51).truncate end def test_truncate_should_raise_if_self_doesnt_implement_to_f assert_raises(TypeError) { @a.truncate } end def test_zero_should_check_equality assert_equal false, @a.zero? assert_equal true, NumericWithSomeMethods.new(0).zero? assert_equal false, NumericWithSomeMethods.new(1).zero? assert_equal false, NumericWithSomeMethods.new(-1).zero? end end