diff options
Diffstat (limited to 'spec/ruby/core')
80 files changed, 1097 insertions, 329 deletions
diff --git a/spec/ruby/core/array/drop_spec.rb b/spec/ruby/core/array/drop_spec.rb index 89b8534af4..84ea86b04c 100644 --- a/spec/ruby/core/array/drop_spec.rb +++ b/spec/ruby/core/array/drop_spec.rb @@ -30,4 +30,22 @@ describe "Array#drop" do ary.shift ary.drop(1).should == [2] end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(2) + + [1, 2, 3].drop(obj).should == [3] + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + -> { [1, 2].drop("cat") }.should raise_error(TypeError) + end + + it "raises a TypeError when the passed argument isn't an integer and #to_int returns non-Integer" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return("cat") + + -> { [1, 2].drop(obj) }.should raise_error(TypeError) + end end diff --git a/spec/ruby/core/class/to_s_spec.rb b/spec/ruby/core/class/to_s_spec.rb deleted file mode 100644 index 2055593a03..0000000000 --- a/spec/ruby/core/class/to_s_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require_relative '../../spec_helper' -require_relative 'fixtures/classes' - -describe "Class#to_s" do - it 'regular class returns same name as Module#to_s' do - String.to_s.should == 'String' - end - - describe 'singleton class' do - it 'for modules includes module name' do - CoreClassSpecs.singleton_class.to_s.should == '#<Class:CoreClassSpecs>' - end - - it 'for classes includes class name' do - CoreClassSpecs::Record.singleton_class.to_s.should == '#<Class:CoreClassSpecs::Record>' - end - - it 'for objects includes class name and object ID' do - obj = CoreClassSpecs::Record.new - obj.singleton_class.to_s.should =~ /#<Class:#<CoreClassSpecs::Record:0x[0-9a-f]+>>/ - end - end -end diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb new file mode 100644 index 0000000000..1b4c0d2df3 --- /dev/null +++ b/spec/ruby/core/data/constants_spec.rb @@ -0,0 +1,15 @@ +require_relative '../../spec_helper' + +describe "Data" do + it "is a subclass of Object" do + suppress_warning do + Data.superclass.should == Object + end + end + + ruby_version_is "2.5" do + it "is deprecated" do + -> { Data }.should complain(/constant ::Data is deprecated/) + end + end +end diff --git a/spec/ruby/core/env/assoc_spec.rb b/spec/ruby/core/env/assoc_spec.rb index 9946e328a9..c7a388db75 100644 --- a/spec/ruby/core/env/assoc_spec.rb +++ b/spec/ruby/core/env/assoc_spec.rb @@ -26,6 +26,6 @@ describe "ENV.assoc" do end it "raises TypeError if the argument is not a String and does not respond to #to_str" do - -> { ENV.assoc(Object.new) }.should raise_error(TypeError) + -> { ENV.assoc(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") end end diff --git a/spec/ruby/core/env/delete_spec.rb b/spec/ruby/core/env/delete_spec.rb index e875df4aeb..b7fe1ee675 100644 --- a/spec/ruby/core/env/delete_spec.rb +++ b/spec/ruby/core/env/delete_spec.rb @@ -30,6 +30,12 @@ describe "ENV.delete" do ScratchPad.recorded.should == "foo" end + it "does not evaluate the block if the envirionment variable exists" do + ENV["foo"] = "bar" + ENV.delete("foo") { |name| fail "Should not happen" } + ENV["foo"].should == nil + end + it "raises TypeError if the argument is not a String and does not respond to #to_str" do -> { ENV.delete(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") end diff --git a/spec/ruby/core/env/each_key_spec.rb b/spec/ruby/core/env/each_key_spec.rb index 5c5cf4f80e..0efcb09900 100644 --- a/spec/ruby/core/env/each_key_spec.rb +++ b/spec/ruby/core/env/each_key_spec.rb @@ -10,7 +10,7 @@ describe "ENV.each_key" do ENV.clear ENV["1"] = "3" ENV["2"] = "4" - ENV.each_key { |k| e << k } + ENV.each_key { |k| e << k }.should equal(ENV) e.should include("1") e.should include("2") ensure diff --git a/spec/ruby/core/env/each_value_spec.rb b/spec/ruby/core/env/each_value_spec.rb index ea29b3a0d7..60d9f605d8 100644 --- a/spec/ruby/core/env/each_value_spec.rb +++ b/spec/ruby/core/env/each_value_spec.rb @@ -10,7 +10,7 @@ describe "ENV.each_value" do ENV.clear ENV["1"] = "3" ENV["2"] = "4" - ENV.each_value { |v| e << v } + ENV.each_value { |v| e << v }.should equal(ENV) e.should include("3") e.should include("4") ensure diff --git a/spec/ruby/core/env/index_spec.rb b/spec/ruby/core/env/index_spec.rb index 04986a0421..43875f5a50 100644 --- a/spec/ruby/core/env/index_spec.rb +++ b/spec/ruby/core/env/index_spec.rb @@ -3,4 +3,10 @@ require_relative 'shared/key' describe "ENV.index" do it_behaves_like :env_key, :index + + it "warns about deprecation" do + -> do + ENV.index("foo") + end.should complain(/warning: ENV.index is deprecated; use ENV.key/) + end end diff --git a/spec/ruby/core/env/keys_spec.rb b/spec/ruby/core/env/keys_spec.rb index 3699b2c225..b074a8f7c7 100644 --- a/spec/ruby/core/env/keys_spec.rb +++ b/spec/ruby/core/env/keys_spec.rb @@ -2,8 +2,8 @@ require_relative '../../spec_helper' describe "ENV.keys" do - it "returns all the keys" do - ENV.keys.sort.should == ENV.to_hash.keys.sort + it "returns an array of the keys" do + ENV.keys.should == ENV.to_hash.keys end it "returns the keys in the locale encoding" do diff --git a/spec/ruby/core/env/rehash_spec.rb b/spec/ruby/core/env/rehash_spec.rb index e724feaa39..3782e4b727 100644 --- a/spec/ruby/core/env/rehash_spec.rb +++ b/spec/ruby/core/env/rehash_spec.rb @@ -1 +1,7 @@ require_relative '../../spec_helper' + +describe "ENV.rehash" do + it "returns nil" do + ENV.rehash.should == nil + end +end diff --git a/spec/ruby/core/env/replace_spec.rb b/spec/ruby/core/env/replace_spec.rb index 8837deea4a..9fc67643d1 100644 --- a/spec/ruby/core/env/replace_spec.rb +++ b/spec/ruby/core/env/replace_spec.rb @@ -1,15 +1,51 @@ require_relative '../../spec_helper' describe "ENV.replace" do + before :each do + @orig = ENV.to_hash + ENV.delete("foo") + end + + after :each do + ENV.replace(@orig) + end it "replaces ENV with a Hash" do - ENV["foo"] = "bar" - e = ENV.reject { |k, v| k == "foo" } - e["baz"] = "bam" - ENV.replace e - ENV["foo"].should == nil - ENV["baz"].should == "bam" - ENV.delete "baz" + ENV.replace("foo" => "0", "bar" => "1").should equal(ENV) + ENV.size.should == 2 + ENV["foo"].should == "0" + ENV["bar"].should == "1" + end + + it "raises TypeError if the argument is not a Hash" do + -> { ENV.replace(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into Hash") + ENV.to_hash.should == @orig + end + + it "raises TypeError if a key is not a String" do + -> { ENV.replace(Object.new => "0") }.should raise_error(TypeError, "no implicit conversion of Object into String") + ENV.to_hash.should == @orig + end + + it "raises TypeError if a value is not a String" do + -> { ENV.replace("foo" => Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") + ENV.to_hash.should == @orig + end + + it "raises Errno::EINVAL when the key contains the '=' character" do + -> { ENV.replace("foo=" =>"bar") }.should raise_error(Errno::EINVAL) + end + + it "raises Errno::EINVAL when the key is an empty string" do + -> { ENV.replace("" => "bar") }.should raise_error(Errno::EINVAL) + end + + it "does not accept good data preceding an error" do + -> { ENV.replace("foo" => "1", Object.new => Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") end + it "does not accept good data following an error" do + -> { ENV.replace(Object.new => Object.new, "foo" => "0") }.should raise_error(TypeError, "no implicit conversion of Object into String") + ENV.to_hash.should == @orig + end end diff --git a/spec/ruby/core/env/shared/to_hash.rb b/spec/ruby/core/env/shared/to_hash.rb index 254054c14d..bfa5699e8d 100644 --- a/spec/ruby/core/env/shared/to_hash.rb +++ b/spec/ruby/core/env/shared/to_hash.rb @@ -1,10 +1,17 @@ describe :env_to_hash, shared: true do + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"]= @saved_foo + end + it "returns the ENV as a hash" do ENV["foo"] = "bar" h = ENV.send(@method) h.should be_an_instance_of(Hash) h["foo"].should == "bar" - ENV.delete "foo" end it "uses the locale encoding for keys" do @@ -18,5 +25,9 @@ describe :env_to_hash, shared: true do it "duplicates the ENV when converting to a Hash" do h = ENV.send(@method) h.should_not equal ENV + h.size.should == ENV.size + h.each_pair do |k, v| + ENV[k].should == v + end end end diff --git a/spec/ruby/core/env/shared/update.rb b/spec/ruby/core/env/shared/update.rb index 430cda5185..129a56544c 100644 --- a/spec/ruby/core/env/shared/update.rb +++ b/spec/ruby/core/env/shared/update.rb @@ -9,8 +9,8 @@ describe :env_update, shared: true do ENV["bar"] = @saved_bar end - it "adds the parameter hash to ENV" do - ENV.send @method, {"foo" => "0", "bar" => "1"} + it "adds the parameter hash to ENV, returning ENV" do + ENV.send(@method, "foo" => "0", "bar" => "1").should equal(ENV) ENV["foo"].should == "0" ENV["bar"].should == "1" end @@ -19,17 +19,43 @@ describe :env_update, shared: true do ENV.send(@method, {"foo" => "0", "bar" => "1"}).should equal(ENV) end - it "yields key, the old value and the new value when replacing entries" do + it "yields key, the old value and the new value when replacing an entry" do ENV.send @method, {"foo" => "0", "bar" => "3"} a = [] ENV.send @method, {"foo" => "1", "bar" => "4"} do |key, old, new| a << [key, old, new] + new + end + a[0].should == ["foo", "0", "1"] + a[1].should == ["bar", "3", "4"] + end + + it "yields key, the old value and the new value when replacing an entry" do + ENV.send @method, {"foo" => "0", "bar" => "3"} + ENV.send @method, {"foo" => "1", "bar" => "4"} do |key, old, new| (new.to_i + 1).to_s end ENV["foo"].should == "2" ENV["bar"].should == "5" - a[0].should == ["foo", "0", "1"] - a[1].should == ["bar", "3", "4"] + end + + ruby_version_is "2.7" do + # BUG: https://bugs.ruby-lang.org/issues/16192 + it "does not evaluate the block when the name is new" do + ENV.delete("bar") + ENV.send @method, {"foo" => "0"} + ENV.send(@method, "bar" => "1") { |key, old, new| fail "Should not get here" } + ENV["bar"].should == "1" + end + + # BUG: https://bugs.ruby-lang.org/issues/16192 + it "does not use the block's return value as the value when the name is new" do + ENV.delete("bar") + ENV.send @method, {"foo" => "0"} + ENV.send(@method, "bar" => "1") { |key, old, new| "Should not use this value" } + ENV["foo"].should == "0" + ENV["bar"].should == "1" + end end it "returns ENV when block given" do @@ -44,6 +70,14 @@ describe :env_update, shared: true do -> { ENV.send @method, "foo" => Object.new }.should raise_error(TypeError, "no implicit conversion of Object into String") end + it "raises Errno::EINVAL when a name contains the '=' character" do + -> { ENV.send(@method, "foo=" => "bar") }.should raise_error(Errno::EINVAL) + end + + it "raises Errno::EINVAL when a name is an empty string" do + -> { ENV.send(@method, "" => "bar") }.should raise_error(Errno::EINVAL) + end + it "updates good data preceding an error" do ENV["foo"] = "0" begin diff --git a/spec/ruby/core/env/to_a_spec.rb b/spec/ruby/core/env/to_a_spec.rb index ed290a48a5..25441eb029 100644 --- a/spec/ruby/core/env/to_a_spec.rb +++ b/spec/ruby/core/env/to_a_spec.rb @@ -3,11 +3,10 @@ require_relative '../../spec_helper' describe "ENV.to_a" do it "returns the ENV as an array" do - ENV["foo"] = "bar" a = ENV.to_a a.is_a?(Array).should == true - a.find { |e| e.first == "foo" }.should == ["foo", "bar"] - ENV.delete "foo" + a.size.should == ENV.size + ENV.each_pair { |k, v| a.should include([k, v])} end it "returns the entries in the locale encoding" do diff --git a/spec/ruby/core/env/to_h_spec.rb b/spec/ruby/core/env/to_h_spec.rb index 81a17700e9..822167aa2b 100644 --- a/spec/ruby/core/env/to_h_spec.rb +++ b/spec/ruby/core/env/to_h_spec.rb @@ -19,6 +19,11 @@ describe "ENV.to_h" do ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" } end + it "does not require the array elements to be strings" do + ENV.replace("a" => "b", "c" => "d") + ENV.to_h { |k, v| [k.to_sym, v.to_sym] }.should == { :a => :b, :c => :d } + end + it "raises ArgumentError if block returns longer or shorter array" do -> do ENV.to_h { |k, v| [k, v.upcase, 1] } diff --git a/spec/ruby/core/env/values_at_spec.rb b/spec/ruby/core/env/values_at_spec.rb index 906d8b01f2..ee970e5f65 100644 --- a/spec/ruby/core/env/values_at_spec.rb +++ b/spec/ruby/core/env/values_at_spec.rb @@ -1,17 +1,37 @@ require_relative '../../spec_helper' describe "ENV.values_at" do + before :each do + @saved_foo = ENV["foo"] + @saved_bar = ENV["bar"] + end + + after :each do + ENV["foo"] = @saved_foo + ENV["bar"] = @saved_bar + end - it "returns an array of the values referenced by the parameters as keys" do + it "returns an array of the values corresponding to the given keys" do ENV["foo"] = "oof" ENV["bar"] = "rab" - ENV.values_at.should == [] ENV.values_at("bar", "foo").should == ["rab", "oof"] - ENV.delete "foo" - ENV.delete "bar" + end + + it "returns an empty array if no keys specified" do + ENV.values_at.should == [] + end + + it "returns nil for each key that is not a name" do + ENV["foo"] = "oof" + ENV["bar"] = "rab" + ENV.values_at("x", "bar", "y", "foo", "z").should == [nil, "rab", nil, "oof", nil] end it "uses the locale encoding" do ENV.values_at(ENV.keys.first).first.encoding.should == Encoding.find('locale') end + + it "raises TypeError when a key is not coercible to String" do + -> { ENV.values_at("foo", Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") + end end diff --git a/spec/ruby/core/env/values_spec.rb b/spec/ruby/core/env/values_spec.rb index 2e4e69c23b..919d7ffb7c 100644 --- a/spec/ruby/core/env/values_spec.rb +++ b/spec/ruby/core/env/values_spec.rb @@ -3,14 +3,7 @@ require_relative '../../spec_helper' describe "ENV.values" do it "returns an array of the values" do - orig = ENV.to_hash - begin - ENV.replace "a" => "b", "c" => "d" - a = ENV.values - a.sort.should == ["b", "d"] - ensure - ENV.replace orig - end + ENV.values.should == ENV.to_hash.values end it "uses the locale encoding" do diff --git a/spec/ruby/core/exception/args_spec.rb b/spec/ruby/core/exception/args_spec.rb deleted file mode 100644 index 005c2dd198..0000000000 --- a/spec/ruby/core/exception/args_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../spec_helper' - -describe "NoMethodError#args" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/exception/arguments_spec.rb b/spec/ruby/core/exception/arguments_spec.rb deleted file mode 100644 index 0b283e9a54..0000000000 --- a/spec/ruby/core/exception/arguments_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require_relative '../../spec_helper' - -describe "ArgumentError" do - it "is a subclass of StandardError" do - StandardError.should be_ancestor_of(ArgumentError) - end - - it "gives its own class name as message if it has no message" do - ArgumentError.new.message.should == "ArgumentError" - end -end diff --git a/spec/ruby/core/exception/backtrace_spec.rb b/spec/ruby/core/exception/backtrace_spec.rb index 5e140f8d9b..2d6825180a 100644 --- a/spec/ruby/core/exception/backtrace_spec.rb +++ b/spec/ruby/core/exception/backtrace_spec.rb @@ -47,7 +47,7 @@ describe "Exception#backtrace" do end end - it "produces a backtrace for an exception captured using $!" do + it "captures the backtrace for an exception into $!" do exception = begin raise rescue RuntimeError @@ -57,6 +57,16 @@ describe "Exception#backtrace" do exception.backtrace.first.should =~ /backtrace_spec/ end + it "captures the backtrace for an exception into $@" do + backtrace = begin + raise + rescue RuntimeError + $@ + end + + backtrace.first.should =~ /backtrace_spec/ + end + it "returns an Array that can be updated" do begin raise diff --git a/spec/ruby/core/exception/case_compare_spec.rb b/spec/ruby/core/exception/case_compare_spec.rb index a4c9eaa6bf..87b9dee3ca 100644 --- a/spec/ruby/core/exception/case_compare_spec.rb +++ b/spec/ruby/core/exception/case_compare_spec.rb @@ -1,5 +1,39 @@ require_relative '../../spec_helper' describe "SystemCallError.===" do - it "needs to be reviewed for spec completeness" + before :all do + @example_errno_class = Errno::EINVAL + @example_errno = @example_errno_class::Errno + end + + it "returns true for an instance of the same class" do + Errno::EINVAL.should === Errno::EINVAL.new + end + + it "returns true if errnos same" do + e = SystemCallError.new('foo', @example_errno) + @example_errno_class.===(e).should == true + end + + it "returns false if errnos different" do + e = SystemCallError.new('foo', @example_errno + 1) + @example_errno_class.===(e).should == false + end + + it "returns false if arg is not kind of SystemCallError" do + e = Object.new + @example_errno_class.===(e).should == false + end + + it "returns true if receiver is generic and arg is kind of SystemCallError" do + unknown_error_number = Errno.constants.size + e = SystemCallError.new('foo', @example_errno) + SystemCallError.===(e).should == true + end + + it "returns false if receiver is generic and arg is not kind of SystemCallError" do + unknown_error_number = Errno.constants.size + e = Object.new + SystemCallError.===(e).should == false + end end diff --git a/spec/ruby/core/exception/destination_encoding_name_spec.rb b/spec/ruby/core/exception/destination_encoding_name_spec.rb index b6ffff8c9c..a9e6474974 100644 --- a/spec/ruby/core/exception/destination_encoding_name_spec.rb +++ b/spec/ruby/core/exception/destination_encoding_name_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' describe "Encoding::UndefinedConversionError#destination_encoding_name" do - it "needs to be reviewed for spec completeness" + it "returns the destination encoding name" do + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xa0") + rescue Encoding::UndefinedConversionError => e + e.destination_encoding_name.should == "EUC-JP" + end + end end describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do - it "needs to be reviewed for spec completeness" + it "returns the destination encoding name" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("\xa0") + rescue Encoding::InvalidByteSequenceError => e + e.destination_encoding_name.should == "UTF-8" + end + end end diff --git a/spec/ruby/core/exception/destination_encoding_spec.rb b/spec/ruby/core/exception/destination_encoding_spec.rb index c3ad0342a1..5709c31e55 100644 --- a/spec/ruby/core/exception/destination_encoding_spec.rb +++ b/spec/ruby/core/exception/destination_encoding_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' describe "Encoding::UndefinedConversionError#destination_encoding" do - it "needs to be reviewed for spec completeness" + it "returns the destination encoding" do + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xa0") + rescue Encoding::UndefinedConversionError => e + e.destination_encoding.should == Encoding::EUC_JP + end + end end describe "Encoding::InvalidByteSequenceError#destination_encoding" do - it "needs to be reviewed for spec completeness" + it "returns the destination encoding" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("\xa0") + rescue Encoding::InvalidByteSequenceError => e + e.destination_encoding.should == Encoding::UTF_8 + end + end end diff --git a/spec/ruby/core/exception/error_bytes_spec.rb b/spec/ruby/core/exception/error_bytes_spec.rb index 2a95bcfdf4..66dd4b62c1 100644 --- a/spec/ruby/core/exception/error_bytes_spec.rb +++ b/spec/ruby/core/exception/error_bytes_spec.rb @@ -1,5 +1,12 @@ require_relative '../../spec_helper' describe "Encoding::InvalidByteSequenceError#error_bytes" do - it "needs to be reviewed for spec completeness" + it "returns the error bytes" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("\xa0") + rescue Encoding::InvalidByteSequenceError => e + e.error_bytes.should == "\xA0".force_encoding("ASCII-8BIT") + end + end end diff --git a/spec/ruby/core/exception/error_char_spec.rb b/spec/ruby/core/exception/error_char_spec.rb index c0256af03a..f95ae2a6ce 100644 --- a/spec/ruby/core/exception/error_char_spec.rb +++ b/spec/ruby/core/exception/error_char_spec.rb @@ -1,5 +1,12 @@ require_relative '../../spec_helper' describe "Encoding::UndefinedConversionError#error_char" do - it "needs to be reviewed for spec completeness" + it "returns the error char" do + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xa0") + rescue Encoding::UndefinedConversionError => e + e.error_char.should == "\u00A0" + end + end end diff --git a/spec/ruby/core/exception/exception_spec.rb b/spec/ruby/core/exception/exception_spec.rb index 3a01366920..d6f5283bd9 100644 --- a/spec/ruby/core/exception/exception_spec.rb +++ b/spec/ruby/core/exception/exception_spec.rb @@ -6,48 +6,6 @@ describe "Exception.exception" do it_behaves_like :exception_new, :exception end -describe "Exception" do - it "is a Class" do - Exception.should be_kind_of(Class) - end - - it "is a superclass of NoMemoryError" do - Exception.should be_ancestor_of(NoMemoryError) - end - - it "is a superclass of ScriptError" do - Exception.should be_ancestor_of(ScriptError) - end - - it "is a superclass of SignalException" do - Exception.should be_ancestor_of(SignalException) - end - - it "is a superclass of Interrupt" do - SignalException.should be_ancestor_of(Interrupt) - end - - it "is a superclass of StandardError" do - Exception.should be_ancestor_of(StandardError) - end - - it "is a superclass of SystemExit" do - Exception.should be_ancestor_of(SystemExit) - end - - it "is a superclass of SystemStackError" do - Exception.should be_ancestor_of(SystemStackError) - end - - it "is a superclass of SecurityError" do - Exception.should be_ancestor_of(SecurityError) - end - - it "is a superclass of EncodingError" do - Exception.should be_ancestor_of(EncodingError) - end -end - describe "Exception#exception" do it "returns self when passed no argument" do e = RuntimeError.new @@ -82,6 +40,18 @@ describe "Exception#exception" do raised_second.should == caught_second end + it "captures an exception into $!" do + exception = begin + raise + rescue RuntimeError + $! + end + + exception.class.should == RuntimeError + exception.message.should == "" + exception.backtrace.first.should =~ /exception_spec/ + end + class CustomArgumentError < StandardError attr_reader :val def initialize(val) diff --git a/spec/ruby/core/exception/hierarchy_spec.rb b/spec/ruby/core/exception/hierarchy_spec.rb new file mode 100644 index 0000000000..e52811c761 --- /dev/null +++ b/spec/ruby/core/exception/hierarchy_spec.rb @@ -0,0 +1,62 @@ +require_relative '../../spec_helper' + +describe "Exception" do + it "has the right class hierarchy" do + hierarchy = { + Exception => { + NoMemoryError => nil, + ScriptError => { + LoadError => nil, + NotImplementedError => nil, + SyntaxError => nil, + }, + SecurityError => nil, + SignalException => { + Interrupt => nil, + }, + StandardError => { + ArgumentError => { + UncaughtThrowError => nil, + }, + EncodingError => nil, + FiberError => nil, + IOError => { + EOFError => nil, + }, + IndexError => { + KeyError => nil, + StopIteration => { + ClosedQueueError => nil, + }, + }, + LocalJumpError => nil, + NameError => { + NoMethodError => nil, + }, + RangeError => { + FloatDomainError => nil, + }, + RegexpError => nil, + RuntimeError => nil, + SystemCallError => nil, + ThreadError => nil, + TypeError => nil, + ZeroDivisionError => nil, + }, + SystemExit => nil, + SystemStackError => nil, + }, + } + ruby_version_is "2.5" do + hierarchy[Exception][StandardError][RuntimeError] = {FrozenError => nil} + end + traverse = -> parent_class, parent_subclass_hash { + parent_subclass_hash.each do |child_class, child_subclass_hash| + child_class.class.should == Class + child_class.superclass.should == parent_class + traverse.call(child_class, child_subclass_hash) if child_subclass_hash + end + } + traverse.call(Object, hierarchy) + end +end diff --git a/spec/ruby/core/exception/interrupt_spec.rb b/spec/ruby/core/exception/interrupt_spec.rb index bc01b7b703..14f294bec6 100644 --- a/spec/ruby/core/exception/interrupt_spec.rb +++ b/spec/ruby/core/exception/interrupt_spec.rb @@ -1,11 +1,5 @@ require_relative '../../spec_helper' -describe "Interrupt" do - it "is a subclass of SignalException" do - Interrupt.superclass.should == SignalException - end -end - describe "Interrupt.new" do it "returns an instance of interrupt with no message given" do e = Interrupt.new diff --git a/spec/ruby/core/exception/readagain_bytes_spec.rb b/spec/ruby/core/exception/readagain_bytes_spec.rb index f7e8d9d1d3..0f1e24f1cf 100644 --- a/spec/ruby/core/exception/readagain_bytes_spec.rb +++ b/spec/ruby/core/exception/readagain_bytes_spec.rb @@ -1,5 +1,12 @@ require_relative '../../spec_helper' describe "Encoding::InvalidByteSequenceError#readagain_bytes" do - it "needs to be reviewed for spec completeness" + it "returns the next byte" do + begin + "abc\xa4def".encode("ISO-8859-1", "EUC-JP") + rescue Encoding::InvalidByteSequenceError => e + e.error_bytes.should == "\xA4".force_encoding("ASCII-8BIT") + e.readagain_bytes.should == 'd' + end + end end diff --git a/spec/ruby/core/exception/script_error_spec.rb b/spec/ruby/core/exception/script_error_spec.rb deleted file mode 100644 index e33a5d3a58..0000000000 --- a/spec/ruby/core/exception/script_error_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require_relative '../../spec_helper' - -describe "ScriptError" do - it "is a superclass of LoadError" do - ScriptError.should be_ancestor_of(LoadError) - end - - it "is a superclass of NotImplementedError" do - ScriptError.should be_ancestor_of(NotImplementedError) - end - - it "is a superclass of SyntaxError" do - ScriptError.should be_ancestor_of(SyntaxError) - end -end diff --git a/spec/ruby/core/exception/signal_exception_spec.rb b/spec/ruby/core/exception/signal_exception_spec.rb index e0b30236f7..e494e18cde 100644 --- a/spec/ruby/core/exception/signal_exception_spec.rb +++ b/spec/ruby/core/exception/signal_exception_spec.rb @@ -30,6 +30,12 @@ describe "SignalException.new" do -> { SignalException.new("NONEXISTENT") }.should raise_error(ArgumentError) end + ruby_version_is "2.6" do + it "raises an exception with an invalid first argument type" do + -> { SignalException.new(Object.new) }.should raise_error(ArgumentError) + end + end + it "takes a signal symbol without SIG prefix as the first argument" do exc = SignalException.new(:INT) exc.signo.should == Signal.list["INT"] @@ -72,3 +78,48 @@ describe "rescuing SignalException" do end end end + +describe "SignalException" do + it "can be rescued" do + ruby_exe(<<-RUBY) + begin + raise SignalException, 'SIGKILL' + rescue SignalException + exit(0) + end + exit(1) + RUBY + + $?.exitstatus.should == 0 + end + + platform_is_not :windows do + it "runs after at_exit" do + output = ruby_exe(<<-RUBY) + at_exit do + puts "hello" + $stdout.flush + end + + raise SignalException, 'SIGKILL' + RUBY + + $?.termsig.should == Signal.list.fetch("KILL") + output.should == "hello\n" + end + + it "cannot be trapped with Signal.trap" do + ruby_exe(<<-RUBY) + Signal.trap("PROF") {} + raise(SignalException, "PROF") + RUBY + + $?.termsig.should == Signal.list.fetch("PROF") + end + + it "self-signals for USR1" do + ruby_exe("raise(SignalException, 'USR1')") + $?.termsig.should == Signal.list.fetch('USR1') + end + end +end diff --git a/spec/ruby/core/exception/source_encoding_name_spec.rb b/spec/ruby/core/exception/source_encoding_name_spec.rb index bd8bc359b6..6f5dbd01aa 100644 --- a/spec/ruby/core/exception/source_encoding_name_spec.rb +++ b/spec/ruby/core/exception/source_encoding_name_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' describe "Encoding::UndefinedConversionError#source_encoding_name" do - it "needs to be reviewed for spec completeness" + it "returns the source encoding name" do + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xa0") + rescue Encoding::UndefinedConversionError => e + e.source_encoding_name.should == "UTF-8" + end + end end describe "Encoding::InvalidByteSequenceError#source_encoding_name" do - it "needs to be reviewed for spec completeness" + it "returns the source encoding name" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("\xa0") + rescue Encoding::InvalidByteSequenceError => e + e.source_encoding_name.should == "EUC-JP" + end + end end diff --git a/spec/ruby/core/exception/source_encoding_spec.rb b/spec/ruby/core/exception/source_encoding_spec.rb index 65ac98d791..fac38e75f4 100644 --- a/spec/ruby/core/exception/source_encoding_spec.rb +++ b/spec/ruby/core/exception/source_encoding_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' describe "Encoding::UndefinedConversionError#source_encoding" do - it "needs to be reviewed for spec completeness" + it "returns the source encoding" do + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xa0") + rescue Encoding::UndefinedConversionError => e + e.source_encoding.should == Encoding::UTF_8 + end + end end describe "Encoding::InvalidByteSequenceError#source_encoding" do - it "needs to be reviewed for spec completeness" + it "returns the source encoding" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("\xa0") + rescue Encoding::InvalidByteSequenceError => e + e.source_encoding.should == Encoding::EUC_JP + end + end end diff --git a/spec/ruby/core/exception/standard_error_spec.rb b/spec/ruby/core/exception/standard_error_spec.rb index 1b469b5090..17e98ce7f0 100644 --- a/spec/ruby/core/exception/standard_error_spec.rb +++ b/spec/ruby/core/exception/standard_error_spec.rb @@ -1,56 +1,23 @@ require_relative '../../spec_helper' describe "StandardError" do - it "is a superclass of ArgumentError" do - StandardError.should be_ancestor_of(ArgumentError) - end - - it "is a superclass of IOError" do - StandardError.should be_ancestor_of(IOError) - end - - it "is a superclass of IndexError" do - StandardError.should be_ancestor_of(IndexError) - end - - it "is a superclass of LocalJumpError" do - StandardError.should be_ancestor_of(LocalJumpError) - end - - it "is a superclass of NameError" do - StandardError.should be_ancestor_of(NameError) - end - - it "is a superclass of RangeError" do - StandardError.should be_ancestor_of(RangeError) - end - - it "is a superclass of RegexpError" do - StandardError.should be_ancestor_of(RegexpError) - end - - it "is a superclass of RuntimeError" do - StandardError.should be_ancestor_of(RuntimeError) - end - - it "is a superclass of SystemCallError" do - StandardError.should be_ancestor_of(SystemCallError.new("").class) - end - it "is a superclass of ThreadError" do - StandardError.should be_ancestor_of(ThreadError) - end - - it "is a superclass of TypeError" do - StandardError.should be_ancestor_of(TypeError) + it "rescues StandardError" do + begin + raise StandardError + rescue => exception + exception.class.should == StandardError + end end - it "is a superclass of ZeroDivisionError" do - StandardError.should be_ancestor_of(ZeroDivisionError) + it "rescues subclass of StandardError" do + begin + raise RuntimeError + rescue => exception + exception.class.should == RuntimeError + end end - ruby_version_is '2.5' do - it "is a superclass of FrozenError" do - StandardError.should be_ancestor_of(FrozenError) - end + it "does not rescue superclass of StandardError" do + -> { begin; raise Exception; rescue; end }.should raise_error(Exception) end end diff --git a/spec/ruby/core/exception/system_call_error_spec.rb b/spec/ruby/core/exception/system_call_error_spec.rb index c07c8af72c..c510ae440f 100644 --- a/spec/ruby/core/exception/system_call_error_spec.rb +++ b/spec/ruby/core/exception/system_call_error_spec.rb @@ -20,41 +20,82 @@ describe "SystemCallError" do end describe "SystemCallError.new" do + before :all do + @example_errno = Errno::EINVAL::Errno + @example_errno_class = Errno::EINVAL + @last_known_errno = Errno.constants.size - 1 + @unknown_errno = Errno.constants.size + end + it "requires at least one argument" do -> { SystemCallError.new }.should raise_error(ArgumentError) end it "accepts single Fixnum argument as errno" do SystemCallError.new(-2**24).errno.should == -2**24 - SystemCallError.new(42).errno.should == 42 + SystemCallError.new(-1).errno.should == -1 + SystemCallError.new(0).errno.should == 0 + SystemCallError.new(@last_known_errno).errno.should == @last_known_errno + SystemCallError.new(@unknown_errno).errno.should == @unknown_errno SystemCallError.new(2**24).errno.should == 2**24 end + it "constructs a SystemCallError for an unknown error number" do + SystemCallError.new(-2**24).should be_an_instance_of(SystemCallError) + SystemCallError.new(-1).should be_an_instance_of(SystemCallError) + SystemCallError.new(@unknown_errno).should be_an_instance_of(SystemCallError) + SystemCallError.new(2**24).should be_an_instance_of(SystemCallError) + end + it "constructs the appropriate Errno class" do - # EINVAL should be more or less mortable across the platforms, - # so let's use it then. - SystemCallError.new(22).should be_kind_of(SystemCallError) - SystemCallError.new(22).should be_an_instance_of(Errno::EINVAL) - SystemCallError.new(2**28).should be_an_instance_of(SystemCallError) + e = SystemCallError.new(@example_errno) + e.should be_kind_of(SystemCallError) + e.should be_an_instance_of(@example_errno_class) end it "accepts an optional custom message preceding the errno" do - exc = SystemCallError.new("custom message", 22) - exc.should be_an_instance_of(Errno::EINVAL) - exc.errno.should == 22 - exc.message.should == "Invalid argument - custom message" + exc = SystemCallError.new("custom message", @example_errno) + exc.should be_an_instance_of(@example_errno_class) + exc.errno.should == @example_errno + exc.message.should == 'Invalid argument - custom message' end it "accepts an optional third argument specifying the location" do - exc = SystemCallError.new("custom message", 22, "location") - exc.should be_an_instance_of(Errno::EINVAL) - exc.errno.should == 22 - exc.message.should == "Invalid argument @ location - custom message" + exc = SystemCallError.new("custom message", @example_errno, "location") + exc.should be_an_instance_of(@example_errno_class) + exc.errno.should == @example_errno + exc.message.should == 'Invalid argument @ location - custom message' + end + + it "coerces location if it is not a String" do + e = SystemCallError.new('foo', 1, :not_a_string) + e.message.should =~ /@ not_a_string - foo/ end it "returns an arity of -1 for the initialize method" do SystemCallError.instance_method(:initialize).arity.should == -1 end + + it "converts to Integer if errno is a Float" do + SystemCallError.new('foo', 2.0).should == SystemCallError.new('foo', 2) + SystemCallError.new('foo', 2.9).should == SystemCallError.new('foo', 2) + end + + it "converts to Integer if errno is a Complex convertible to Integer" do + SystemCallError.new('foo', Complex(2.9, 0)).should == SystemCallError.new('foo', 2) + end + + it "raises TypeError if message is not a String" do + -> { SystemCallError.new(:foo, 1) }.should raise_error(TypeError, /no implicit conversion of Symbol into String/) + end + + it "raises TypeError if errno is not an Integer" do + -> { SystemCallError.new('foo', 'bar') }.should raise_error(TypeError, /no implicit conversion of String into Integer/) + end + + it "raises RangeError if errno is a Complex not convertible to Integer" do + -> { SystemCallError.new('foo', Complex(2.9, 1)) }.should raise_error(RangeError, /can't convert/) + end end describe "SystemCallError#errno" do diff --git a/spec/ruby/core/exception/to_s_spec.rb b/spec/ruby/core/exception/to_s_spec.rb index 8570b18cfd..4c4c7ab432 100644 --- a/spec/ruby/core/exception/to_s_spec.rb +++ b/spec/ruby/core/exception/to_s_spec.rb @@ -19,5 +19,19 @@ describe "Exception#to_s" do end describe "NameError#to_s" do - it "needs to be reviewed for spec completeness" + it "raises its own message for an undefined variable" do + begin + puts not_defined + rescue => exception + exception.message.should =~ /undefined local variable or method `not_defined'/ + end + end + + it "raises its own message for an undefined constant" do + begin + puts NotDefined + rescue => exception + exception.message.should =~ /uninitialized constant NotDefined/ + end + end end diff --git a/spec/ruby/core/file/ftype_spec.rb b/spec/ruby/core/file/ftype_spec.rb index 20e9af7e5f..8ff70baa80 100644 --- a/spec/ruby/core/file/ftype_spec.rb +++ b/spec/ruby/core/file/ftype_spec.rb @@ -12,8 +12,9 @@ describe "File.ftype" do end it "raises Errno::ENOENT if the file is not valid" do - l = -> { File.ftype("/#{$$}#{Time.now.to_f}") } - l.should raise_error(Errno::ENOENT) + -> { + File.ftype("/#{$$}#{Time.now.to_f}") + }.should raise_error(Errno::ENOENT) end it "returns a String" do diff --git a/spec/ruby/core/file/lutime_spec.rb b/spec/ruby/core/file/lutime_spec.rb new file mode 100644 index 0000000000..7449bc4389 --- /dev/null +++ b/spec/ruby/core/file/lutime_spec.rb @@ -0,0 +1,40 @@ +require_relative '../../spec_helper' + +ruby_version_is "2.5" do + describe "File.lutime" do + platform_is_not :windows do + before :each do + @atime = Time.utc(2000) + @mtime = Time.utc(2001) + @file = tmp("specs_lutime_file") + @symlink = tmp("specs_lutime_symlink") + touch @file + File.symlink(@file, @symlink) + end + + after :each do + rm_r @file, @symlink + end + + it "sets the access and modification time for a regular file" do + File.lutime(@atime, @mtime, @file) + stat = File.stat(@file) + stat.atime.should == @atime + stat.mtime.should === @mtime + end + + it "sets the access and modification time for a symlink" do + original = File.stat(@file) + + File.lutime(@atime, @mtime, @symlink) + stat = File.lstat(@symlink) + stat.atime.should == @atime + stat.mtime.should === @mtime + + file = File.stat(@file) + file.atime.should == original.atime + file.mtime.should == original.mtime + end + end + end +end diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb index 93f1972ac6..e3c5618795 100644 --- a/spec/ruby/core/file/open_spec.rb +++ b/spec/ruby/core/file/open_spec.rb @@ -623,6 +623,14 @@ describe "File.open" do end end + ruby_version_is "2.5" do + it "raises ArgumentError if mixing :newline and binary mode" do + -> { + File.open(@file, "rb", newline: :universal) {} + }.should raise_error(ArgumentError, "newline decorator with binary mode") + end + end + ruby_version_is "2.6" do context "'x' flag" do before :each do diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb index 4205d95c83..2dc993a176 100644 --- a/spec/ruby/core/float/comparison_spec.rb +++ b/spec/ruby/core/float/comparison_spec.rb @@ -16,6 +16,38 @@ describe "Float#<=>" do (1.0 <=> "1").should be_nil end + it "compares using #coerce when argument is not a Float" do + klass = Class.new do + attr_reader :call_count + def coerce(other) + @call_count ||= 0 + @call_count += 1 + [other, 42.0] + end + end + + coercible = klass.new + (2.33 <=> coercible).should == -1 + (42.0 <=> coercible).should == 0 + (43.0 <=> coercible).should == 1 + coercible.call_count.should == 3 + end + + ruby_version_is "2.5" do + it "raises TypeError when #coerce misbehaves" do + klass = Class.new do + def coerce(other) + :incorrect + end + end + + bad_coercible = klass.new + -> { + 4.2 <=> bad_coercible + }.should raise_error(TypeError, "coerce must return [x, y]") + end + end + # The 4 tests below are taken from matz's revision 23730 for Ruby trunk # it "returns 1 when self is Infinity and other is a Bignum" do diff --git a/spec/ruby/core/integer/constants_spec.rb b/spec/ruby/core/integer/constants_spec.rb new file mode 100644 index 0000000000..3b8b01e330 --- /dev/null +++ b/spec/ruby/core/integer/constants_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' + +describe "Fixnum" do + it "is unified into Integer" do + suppress_warning do + Fixnum.should equal(Integer) + end + end + + it "is deprecated" do + -> { Fixnum }.should complain(/constant ::Fixnum is deprecated/) + end +end + +describe "Bignum" do + it "is unified into Integer" do + suppress_warning do + Bignum.should equal(Integer) + end + end + + it "is deprecated" do + -> { Bignum }.should complain(/constant ::Bignum is deprecated/) + end +end diff --git a/spec/ruby/core/io/popen_spec.rb b/spec/ruby/core/io/popen_spec.rb index 622b3a9394..4f873e61cd 100644 --- a/spec/ruby/core/io/popen_spec.rb +++ b/spec/ruby/core/io/popen_spec.rb @@ -1,13 +1,22 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative '../process/fixtures/common' describe "IO.popen" do + ProcessSpecs.use_system_ruby(self) + before :each do + @fname = tmp("IO_popen_spec") @io = nil + @var = "$FOO" + platform_is :windows do + @var = "%FOO%" + end end after :each do - @io.close if @io + @io.close if @io and !@io.closed? + rm_r @fname end it "returns an open IO" do @@ -16,27 +25,15 @@ describe "IO.popen" do end it "reads a read-only pipe" do - @io = IO.popen(ruby_cmd('puts "foo"'), "r") + @io = IO.popen('echo foo', "r") @io.read.should == "foo\n" end it "raises IOError when writing a read-only pipe" do - @io = IO.popen(ruby_cmd('puts "foo"'), "r") + @io = IO.popen('echo foo', "r") -> { @io.write('bar') }.should raise_error(IOError) @io.read.should == "foo\n" end -end - -describe "IO.popen" do - before :each do - @fname = tmp("IO_popen_spec") - @io = nil - end - - after :each do - @io.close if @io and !@io.closed? - rm_r @fname - end it "sees an infinitely looping subprocess exit when read pipe is closed" do io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r' @@ -97,16 +94,6 @@ describe "IO.popen" do mode.should_receive(:to_str).and_return("r") @io = IO.popen(ruby_cmd('exit 0'), mode) end -end - -describe "IO.popen" do - before :each do - @io = nil - end - - after :each do - @io.close if @io - end describe "with a block" do it "yields an open IO to the block" do @@ -171,13 +158,13 @@ describe "IO.popen" do context "with a leading ENV Hash" do it "accepts a single String command" do - IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]')) do |io| + IO.popen({"FOO" => "bar"}, "echo #{@var}") do |io| io.read.should == "bar\n" end end it "accepts a single String command, and an IO mode" do - IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]'), "r") do |io| + IO.popen({"FOO" => "bar"}, "echo #{@var}", "r") do |io| io.read.should == "bar\n" end end diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb index 843b5d10a2..a82b4fba74 100644 --- a/spec/ruby/core/kernel/rand_spec.rb +++ b/spec/ruby/core/kernel/rand_spec.rb @@ -138,6 +138,16 @@ describe "Kernel.rand" do rand(KernelSpecs::CustomRangeFloat.new(1.0)..KernelSpecs::CustomRangeFloat.new(42.0)).should be_an_instance_of(KernelSpecs::CustomRangeFloat) rand(Time.now..Time.now).should be_an_instance_of(Time) end + + it "is random on boot" do + results = 2.times.map { + out = ruby_exe('p rand', options: '--disable-gems') + Float(out) + } + results.size.should == 2 + # this is technically flaky, but very unlikely in a good distribution + results[0].should_not == results[1] + end end describe "Kernel#rand" do diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb index 14bf5f27da..120619abef 100644 --- a/spec/ruby/core/kernel/shared/load.rb +++ b/spec/ruby/core/kernel/shared/load.rb @@ -1,3 +1,5 @@ +main = self + describe :kernel_load, shared: true do before :each do CodeLoadingSpecs.spec_setup @@ -95,13 +97,45 @@ describe :kernel_load, shared: true do @object.load(path, true) Object.const_defined?(:LoadSpecWrap).should be_false + + wrap_module = ScratchPad.recorded[1] + wrap_module.should be_an_instance_of(Module) end it "allows referencing outside namespaces" do path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true) - ScratchPad.recorded.first.should be_an_instance_of(Class) + ScratchPad.recorded[0].should equal(String) + end + + it "sets self as a copy of the top-level main" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + top_level = ScratchPad.recorded[2] + top_level.to_s.should == "main" + top_level.method(:to_s).owner.should == top_level.singleton_class + top_level.should_not equal(main) + top_level.should be_an_instance_of(Object) + end + + it "includes modules included in main's singleton class in self's class" do + mod = Module.new + main.extend(mod) + + main_ancestors = main.singleton_class.ancestors[1..-1] + main_ancestors.first.should == mod + + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + top_level = ScratchPad.recorded[2] + top_level_ancestors = top_level.singleton_class.ancestors[-main_ancestors.size..-1] + top_level_ancestors.should == main_ancestors + + wrap_module = ScratchPad.recorded[1] + top_level.singleton_class.ancestors.should == [top_level.singleton_class, wrap_module, *main_ancestors] end describe "with top-level methods" do diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb index 3a7a083dd8..fc78ca4ff9 100644 --- a/spec/ruby/core/marshal/dump_spec.rb +++ b/spec/ruby/core/marshal/dump_spec.rb @@ -473,6 +473,12 @@ describe "Marshal.dump" do Marshal.dump(obj).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt[\x06\"\x12foo/bar.rb:10" end + it "dumps instance variables if they exist" do + obj = Exception.new("foo") + obj.instance_variable_set(:@ivar, 1) + Marshal.dump(obj).should == "\x04\bo:\x0EException\b:\tmesg\"\bfoo:\abt0:\n@ivari\x06" + end + it "dumps the cause for the exception" do exc = nil begin diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb index b90a6a99ce..302d3d5bda 100644 --- a/spec/ruby/core/marshal/shared/load.rb +++ b/spec/ruby/core/marshal/shared/load.rb @@ -531,6 +531,19 @@ describe :marshal_load, shared: true do loaded.message.should == obj.message loaded.backtrace.should == obj.backtrace end + + it "loads an marshalled exception with ivars" do + s = 'hi' + arr = [:so, :so, s, s] + obj = Exception.new("foo") + obj.instance_variable_set :@arr, arr + + loaded = Marshal.send(@method, "\x04\bo:\x0EException\b:\tmesg\"\bfoo:\abt0:\t@arr[\t:\aso;\t\"\ahi@\b") + new_arr = loaded.instance_variable_get :@arr + + loaded.message.should == obj.message + new_arr.should == arr + end end describe "for an Object" do diff --git a/spec/ruby/core/matchdata/allocate_spec.rb b/spec/ruby/core/matchdata/allocate_spec.rb new file mode 100644 index 0000000000..9f3ada4018 --- /dev/null +++ b/spec/ruby/core/matchdata/allocate_spec.rb @@ -0,0 +1,10 @@ +require_relative '../../spec_helper' + +describe "MatchData.allocate" do + ruby_version_is "2.7" do + it "is undefined" do + # https://bugs.ruby-lang.org/issues/16294 + -> { MatchData.allocate }.should raise_error(NoMethodError) + end + end +end diff --git a/spec/ruby/core/matchdata/dup_spec.rb b/spec/ruby/core/matchdata/dup_spec.rb new file mode 100644 index 0000000000..70877f07eb --- /dev/null +++ b/spec/ruby/core/matchdata/dup_spec.rb @@ -0,0 +1,14 @@ +require_relative '../../spec_helper' + +describe "MatchData#dup" do + it "duplicates the match data" do + original = /ll/.match("hello") + original.instance_variable_set(:@custom_ivar, 42) + duplicate = original.dup + + duplicate.instance_variable_get(:@custom_ivar).should == 42 + original.regexp.should == duplicate.regexp + original.string.should == duplicate.string + original.offset(0).should == duplicate.offset(0) + end +end diff --git a/spec/ruby/core/matchdata/regexp_spec.rb b/spec/ruby/core/matchdata/regexp_spec.rb index 7a4783434c..099b59c559 100644 --- a/spec/ruby/core/matchdata/regexp_spec.rb +++ b/spec/ruby/core/matchdata/regexp_spec.rb @@ -11,6 +11,12 @@ describe "MatchData#regexp" do m.regexp.should == /hay/ end + it "returns the same Regexp used to match" do + r = /hay/ + m = 'haystack'.match(r) + m.regexp.object_id.should == r.object_id + end + it "returns a Regexp for the result of gsub(String)" do 'he[[o'.gsub('[', ']') $~.regexp.should == /\[/ diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb index df434e8e60..db95704cc7 100644 --- a/spec/ruby/core/module/autoload_spec.rb +++ b/spec/ruby/core/module/autoload_spec.rb @@ -652,6 +652,27 @@ describe "Module#autoload" do ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class) end + it "does not call #require a second time and does not warn if feature sets and trigger autoload on itself" do + main = TOPLEVEL_BINDING.eval("self") + main.should_not_receive(:require) + + -> { + Kernel.require fixture(__FILE__, "autoload_self_during_require.rb") + }.should_not complain(verbose: true) + ModuleSpecs::Autoload::AutoloadSelfDuringRequire.should be_kind_of(Class) + end + + it "handles multiple autoloads in the same file" do + $LOAD_PATH.unshift(File.expand_path('../fixtures/multi', __FILE__)) + begin + require 'foo/bar_baz' + ModuleSpecs::Autoload::Foo::Bar.should be_kind_of(Class) + ModuleSpecs::Autoload::Foo::Baz.should be_kind_of(Class) + ensure + $LOAD_PATH.shift + end + end + it "calls #to_path on non-string filenames" do p = mock('path') p.should_receive(:to_path).and_return @non_existent diff --git a/spec/ruby/core/module/fixtures/autoload_self_during_require.rb b/spec/ruby/core/module/fixtures/autoload_self_during_require.rb new file mode 100644 index 0000000000..f4a514a807 --- /dev/null +++ b/spec/ruby/core/module/fixtures/autoload_self_during_require.rb @@ -0,0 +1,5 @@ +module ModuleSpecs::Autoload + autoload :AutoloadSelfDuringRequire, __FILE__ + class AutoloadSelfDuringRequire + end +end diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index ef70eaf9cf..40777cdbbd 100644 --- a/spec/ruby/core/module/fixtures/classes.rb +++ b/spec/ruby/core/module/fixtures/classes.rb @@ -6,6 +6,9 @@ module ModuleSpecs CONST = :plain_constant + class NamedClass + end + module PrivConstModule PRIVATE_CONSTANT = 1 private_constant :PRIVATE_CONSTANT diff --git a/spec/ruby/core/module/fixtures/multi/foo.rb b/spec/ruby/core/module/fixtures/multi/foo.rb new file mode 100644 index 0000000000..549996f08f --- /dev/null +++ b/spec/ruby/core/module/fixtures/multi/foo.rb @@ -0,0 +1,6 @@ +module ModuleSpecs::Autoload + module Foo + autoload :Bar, 'foo/bar_baz' + autoload :Baz, 'foo/bar_baz' + end +end diff --git a/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb b/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb new file mode 100644 index 0000000000..53d3849e1f --- /dev/null +++ b/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb @@ -0,0 +1,11 @@ +require 'foo' + +module ModuleSpecs::Autoload + module Foo + class Bar + end + + class Baz + end + end +end diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb index c39d59e05d..a04b1a54a0 100644 --- a/spec/ruby/core/module/shared/set_visibility.rb +++ b/spec/ruby/core/module/shared/set_visibility.rb @@ -5,6 +5,23 @@ describe :set_visibility, shared: true do Module.should have_private_instance_method(@method, false) end + describe "with argument" do + it "does not clone method from the ancestor when setting to the same visibility in a child" do + visibility = @method + parent = Module.new { + def test_method; end + send(visibility, :test_method) + } + + child = Module.new { + include parent + send(visibility, :test_method) + } + + child.should_not send(:"have_#{visibility}_instance_method", :test_method, false) + end + end + describe "without arguments" do it "sets visibility to following method definitions" do visibility = @method diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb index bebdcf6e1b..29f6ecf726 100644 --- a/spec/ruby/core/module/to_s_spec.rb +++ b/spec/ruby/core/module/to_s_spec.rb @@ -2,17 +2,44 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Module#to_s" do + it 'returns the name of the module if it has a name' do + Enumerable.to_s.should == 'Enumerable' + String.to_s.should == 'String' + end + it "returns the full constant path leading to the module" do ModuleSpecs::LookupMod.to_s.should == "ModuleSpecs::LookupMod" end it "works with an anonymous module" do m = Module.new - m.to_s.should =~ /#<Module:0x[0-9a-f]+>/ + m.to_s.should =~ /\A#<Module:0x\h+>\z/ end it "works with an anonymous class" do c = Class.new - c.to_s.should =~ /#<Class:0x[0-9a-f]+>/ + c.to_s.should =~ /\A#<Class:0x\h+>\z/ + end + + it 'for the singleton class of an object of an anonymous class' do + klass = Class.new + obj = klass.new + sclass = obj.singleton_class + sclass.to_s.should == "#<Class:#{obj}>" + sclass.to_s.should =~ /\A#<Class:#<#{klass}:0x\h+>>\z/ + sclass.to_s.should =~ /\A#<Class:#<#<Class:0x\h+>:0x\h+>>\z/ + end + + it 'for a singleton class of a module includes the module name' do + ModuleSpecs.singleton_class.to_s.should == '#<Class:ModuleSpecs>' + end + + it 'for a metaclass includes the class name' do + ModuleSpecs::NamedClass.singleton_class.to_s.should == '#<Class:ModuleSpecs::NamedClass>' + end + + it 'for objects includes class name and object ID' do + obj = ModuleSpecs::NamedClass.new + obj.singleton_class.to_s.should =~ /\A#<Class:#<ModuleSpecs::NamedClass:0x\h+>>\z/ end end diff --git a/spec/ruby/core/module/undef_method_spec.rb b/spec/ruby/core/module/undef_method_spec.rb index 9b2c9240f4..b101778962 100644 --- a/spec/ruby/core/module/undef_method_spec.rb +++ b/spec/ruby/core/module/undef_method_spec.rb @@ -56,8 +56,37 @@ describe "Module#undef_method" do @module.send(:undef_method, :method_to_undef).should equal(@module) end - it "raises a NameError when passed a missing name" do - -> { @module.send :undef_method, :not_exist }.should raise_error(NameError) { |e| + it "raises a NameError when passed a missing name for a module" do + -> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for module `#{@module}'/) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + it "raises a NameError when passed a missing name for a class" do + klass = Class.new + -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{klass}'/) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + it "raises a NameError when passed a missing name for a singleton class" do + klass = Class.new + obj = klass.new + sclass = obj.singleton_class + + -> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{sclass}'/) { |e| + e.message.should include('`#<Class:#<#<Class:') + + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + it "raises a NameError when passed a missing name for a metaclass" do + klass = String.singleton_class + -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `String'/) { |e| # a NameError and not a NoMethodError e.class.should == NameError } diff --git a/spec/ruby/core/process/clock_getres_spec.rb b/spec/ruby/core/process/clock_getres_spec.rb index 7112b0520a..f1ecb74010 100644 --- a/spec/ruby/core/process/clock_getres_spec.rb +++ b/spec/ruby/core/process/clock_getres_spec.rb @@ -8,10 +8,7 @@ describe "Process.clock_getres" do # NOTE: Look at fixtures/clocks.rb for clock and OS-specific exclusions ProcessSpecs.clock_constants_for_resolution_checks.each do |name, value| it "matches the clock in practice for Process::#{name}" do - times = [] - 10_000.times do - times << Process.clock_gettime(value, :nanosecond) - end + times = 10_000.times.map { Process.clock_gettime(value, :nanosecond) } reported = Process.clock_getres(value, :nanosecond) # The clock should not be more accurate than reported (times should be diff --git a/spec/ruby/core/process/clock_gettime_spec.rb b/spec/ruby/core/process/clock_gettime_spec.rb index d3f973d7ae..59e1406e02 100644 --- a/spec/ruby/core/process/clock_gettime_spec.rb +++ b/spec/ruby/core/process/clock_gettime_spec.rb @@ -41,4 +41,97 @@ describe "Process.clock_gettime" do t2.should be_close(t1, TIME_TOLERANCE) end end + + describe "supports the platform clocks mentioned in the documentation" do + it "CLOCK_REALTIME" do + Process.clock_gettime(Process::CLOCK_REALTIME).should be_an_instance_of(Float) + end + + it "CLOCK_MONOTONIC" do + Process.clock_gettime(Process::CLOCK_MONOTONIC).should be_an_instance_of(Float) + end + + # These specs need macOS 10.12+ / darwin 16+ + guard_not -> { platform_is_not(:darwin) or RUBY_PLATFORM[/darwin\d+/].to_i >= 16 } do + platform_is :linux, :openbsd, :darwin do + it "CLOCK_PROCESS_CPUTIME_ID" do + Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID).should be_an_instance_of(Float) + end + end + + platform_is :linux, :freebsd, :openbsd, :darwin do + it "CLOCK_THREAD_CPUTIME_ID" do + Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID).should be_an_instance_of(Float) + end + end + + platform_is :freebsd, :openbsd do + it "CLOCK_VIRTUAL" do + Process.clock_gettime(Process::CLOCK_VIRTUAL).should be_an_instance_of(Float) + end + + it "CLOCK_PROF" do + Process.clock_gettime(Process::CLOCK_PROF).should be_an_instance_of(Float) + end + + it "CLOCK_UPTIME" do + Process.clock_gettime(Process::CLOCK_UPTIME).should be_an_instance_of(Float) + end + end + + platform_is :linux, :darwin do + it "CLOCK_MONOTONIC_RAW" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW).should be_an_instance_of(Float) + end + end + + platform_is :darwin do + it "CLOCK_MONOTONIC_RAW_APPROX" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW_APPROX).should be_an_instance_of(Float) + end + + it "CLOCK_UPTIME_RAW and CLOCK_UPTIME_RAW_APPROX" do + Process.clock_gettime(Process::CLOCK_UPTIME_RAW).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_UPTIME_RAW_APPROX).should be_an_instance_of(Float) + end + end + + platform_is :freebsd do + it "CLOCK_REALTIME_FAST and CLOCK_REALTIME_PRECISE" do + Process.clock_gettime(Process::CLOCK_REALTIME_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_REALTIME_PRECISE).should be_an_instance_of(Float) + end + + it "CLOCK_MONOTONIC_FAST and CLOCK_MONOTONIC_PRECISE" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_MONOTONIC_PRECISE).should be_an_instance_of(Float) + end + + it "CLOCK_UPTIME_FAST and CLOCK_UPTIME_PRECISE" do + Process.clock_gettime(Process::CLOCK_UPTIME_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_UPTIME_PRECISE).should be_an_instance_of(Float) + end + + it "CLOCK_SECOND" do + Process.clock_gettime(Process::CLOCK_SECOND).should be_an_instance_of(Float) + end + end + + platform_is :linux do + it "CLOCK_REALTIME_COARSE and CLOCK_REALTIME_ALARM" do + Process.clock_gettime(Process::CLOCK_REALTIME_COARSE).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_REALTIME_ALARM).should be_an_instance_of(Float) + end + + it "CLOCK_MONOTONIC_COARSE" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_COARSE).should be_an_instance_of(Float) + end + + it "CLOCK_BOOTTIME and CLOCK_BOOTTIME_ALARM" do + Process.clock_gettime(Process::CLOCK_BOOTTIME).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_BOOTTIME_ALARM).should be_an_instance_of(Float) + end + end + end + end end diff --git a/spec/ruby/core/process/exec_spec.rb b/spec/ruby/core/process/exec_spec.rb index 848990c6dc..5a6e3fc1a4 100644 --- a/spec/ruby/core/process/exec_spec.rb +++ b/spec/ruby/core/process/exec_spec.rb @@ -200,9 +200,9 @@ describe "Process.exec" do it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do map_fd_fixture = fixture __FILE__, "map_fd.rb" cmd = <<-EOC - f = File.open("#{@name}", "w+") + f = File.open(#{@name.inspect}, "w+") child_fd = f.fileno + 1 - File.open("#{@child_fd_file}", "w") { |io| io.print child_fd } + File.open(#{@child_fd_file.inspect}, "w") { |io| io.print child_fd } Process.exec "#{ruby_cmd(map_fd_fixture)} \#{child_fd}", { child_fd => f } EOC @@ -212,6 +212,35 @@ describe "Process.exec" do File.read(@name).should == "writing to fd: #{child_fd}" end + + it "lets the process after exec have specified file descriptor despite close_on_exec" do + map_fd_fixture = fixture __FILE__, "map_fd.rb" + cmd = <<-EOC + f = File.open(#{@name.inspect}, 'w+') + puts(f.fileno, f.close_on_exec?) + STDOUT.flush + Process.exec("#{ruby_cmd(map_fd_fixture)} \#{f.fileno}", f.fileno => f.fileno) + EOC + + output = ruby_exe(cmd, escape: true) + child_fd, close_on_exec = output.split + + child_fd.to_i.should > STDERR.fileno + close_on_exec.should == 'true' + File.read(@name).should == "writing to fd: #{child_fd}" + end + + it "sets close_on_exec to false on specified fd even when it fails" do + cmd = <<-EOC + f = File.open(#{__FILE__.inspect}, 'r') + puts(f.close_on_exec?) + Process.exec('/', f.fileno => f.fileno) rescue nil + puts(f.close_on_exec?) + EOC + + output = ruby_exe(cmd, escape: true) + output.split.should == ['true', 'false'] + end end end end diff --git a/spec/ruby/core/process/fixtures/common.rb b/spec/ruby/core/process/fixtures/common.rb index bdbf1e654b..f49513d262 100644 --- a/spec/ruby/core/process/fixtures/common.rb +++ b/spec/ruby/core/process/fixtures/common.rb @@ -3,11 +3,15 @@ module ProcessSpecs if defined?(MSpecScript::SYSTEM_RUBY) context.send(:before, :all) do @ruby = ::RUBY_EXE - Object.const_set(:RUBY_EXE, MSpecScript::SYSTEM_RUBY) + suppress_warning { + Object.const_set(:RUBY_EXE, MSpecScript::SYSTEM_RUBY) + } end context.send(:after, :all) do - Object.const_set(:RUBY_EXE, @ruby) + suppress_warning { + Object.const_set(:RUBY_EXE, @ruby) + } end end end diff --git a/spec/ruby/core/process/status/equal_value_spec.rb b/spec/ruby/core/process/status/equal_value_spec.rb index 9e9a2d0a2b..444ce1775b 100644 --- a/spec/ruby/core/process/status/equal_value_spec.rb +++ b/spec/ruby/core/process/status/equal_value_spec.rb @@ -1,5 +1,15 @@ require_relative '../../../spec_helper' describe "Process::Status#==" do - it "needs to be reviewed for spec completeness" + it "returns true when compared to the integer status of an exited child" do + ruby_exe("exit(29)") + $?.to_i.should == $? + $?.should == $?.to_i + end + + it "returns true when compared to the integer status of a terminated child" do + ruby_exe("Process.kill(:KILL, $$); exit(29)") + $?.to_i.should == $? + $?.should == $?.to_i + end end diff --git a/spec/ruby/core/process/status/exitstatus_spec.rb b/spec/ruby/core/process/status/exitstatus_spec.rb index cd46b2081f..d6c6965b9e 100644 --- a/spec/ruby/core/process/status/exitstatus_spec.rb +++ b/spec/ruby/core/process/status/exitstatus_spec.rb @@ -11,7 +11,7 @@ describe "Process::Status#exitstatus" do describe "for a child that raised SignalException" do before :each do - ruby_exe("raise SignalException, 'SIGTERM'") + ruby_exe("Process.kill(:KILL, $$); exit(42)") end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/termsig_spec.rb b/spec/ruby/core/process/status/termsig_spec.rb index 1482d27146..204708bc1b 100644 --- a/spec/ruby/core/process/status/termsig_spec.rb +++ b/spec/ruby/core/process/status/termsig_spec.rb @@ -1,9 +1,7 @@ require_relative '../../../spec_helper' describe "Process::Status#termsig" do - describe "for a child that exited normally" do - before :each do ruby_exe("exit(0)") end @@ -26,26 +24,20 @@ describe "Process::Status#termsig" do end describe "for a child that was sent a signal" do - before :each do ruby_exe("Process.kill(:KILL, $$); exit(42)") end platform_is_not :windows do - it "returns the signal" do $?.termsig.should == Signal.list["KILL"] end - end platform_is :windows do - it "always returns nil" do $?.termsig.should be_nil end - end - end end diff --git a/spec/ruby/core/process/status/to_i_spec.rb b/spec/ruby/core/process/status/to_i_spec.rb index 37b7bdb1e4..a284f64f86 100644 --- a/spec/ruby/core/process/status/to_i_spec.rb +++ b/spec/ruby/core/process/status/to_i_spec.rb @@ -1,5 +1,13 @@ require_relative '../../../spec_helper' describe "Process::Status#to_i" do - it "needs to be reviewed for spec completeness" + it "returns an integer when the child exits" do + ruby_exe('exit 48') + $?.to_i.should be_an_instance_of(Integer) + end + + it "returns an integer when the child is signaled" do + ruby_exe('raise SignalException, "TERM"') + $?.to_i.should be_an_instance_of(Integer) + end end diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb index 07b4fa5c9f..f148954262 100644 --- a/spec/ruby/core/process/times_spec.rb +++ b/spec/ruby/core/process/times_spec.rb @@ -7,21 +7,19 @@ describe "Process.times" do it "returns current cpu times" do t = Process.times + user = t.utime - # Do busy work for a wall-clock interval. - start = Time.now - 1 until (Time.now - start) > 0.5 + 1 until Process.times.utime > user + Process.times.utime.should > user + end - # Ensure times is larger. NOTE that there is no - # guarantee of an upper bound since anything may be - # happening at the OS level, so we ONLY check that at - # least an interval has elapsed. Also, we are assuming - # there is a correlation between wall clock time and - # process time. In practice, there is an observed - # discrepancy often 10% or greater. In other words, - # this is a very fuzzy test. - t2 = Process.times - diff = (t2.utime + t2.stime) - (t.utime + t.stime) - diff.should > 0 + ruby_version_is "2.5" do + platform_is_not :windows do + it "uses getrusage when available to improve precision beyond milliseconds" do + times = 100.times.map { Process.times } + times.count { |t| ((t.utime * 1e6).to_i % 1000) > 0 }.should > 0 + times.count { |t| ((t.stime * 1e6).to_i % 1000) > 0 }.should > 0 + end + end end end diff --git a/spec/ruby/core/process/tms/cstime_spec.rb b/spec/ruby/core/process/tms/cstime_spec.rb deleted file mode 100644 index 207d4391c0..0000000000 --- a/spec/ruby/core/process/tms/cstime_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms#cstime" do - it "needs to be reviewed for spec completeness" -end - -describe "Process::Tms#cstime=" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/cutime_spec.rb b/spec/ruby/core/process/tms/cutime_spec.rb deleted file mode 100644 index 390280f005..0000000000 --- a/spec/ruby/core/process/tms/cutime_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms#cutime" do - it "needs to be reviewed for spec completeness" -end - -describe "Process::Tms#cutime=" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/element_reference_spec.rb b/spec/ruby/core/process/tms/element_reference_spec.rb deleted file mode 100644 index 84a34089ae..0000000000 --- a/spec/ruby/core/process/tms/element_reference_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms.[]" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/members_spec.rb b/spec/ruby/core/process/tms/members_spec.rb deleted file mode 100644 index 005a8baec1..0000000000 --- a/spec/ruby/core/process/tms/members_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms.members" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/new_spec.rb b/spec/ruby/core/process/tms/new_spec.rb deleted file mode 100644 index 9dd1f5a8f2..0000000000 --- a/spec/ruby/core/process/tms/new_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms.new" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/stime_spec.rb b/spec/ruby/core/process/tms/stime_spec.rb deleted file mode 100644 index 4104b625e2..0000000000 --- a/spec/ruby/core/process/tms/stime_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms#stime" do - it "needs to be reviewed for spec completeness" -end - -describe "Process::Tms#stime=" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/process/tms/utime_spec.rb b/spec/ruby/core/process/tms/utime_spec.rb deleted file mode 100644 index 28371590e9..0000000000 --- a/spec/ruby/core/process/tms/utime_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Process::Tms#utime" do - it "needs to be reviewed for spec completeness" -end - -describe "Process::Tms#utime=" do - it "needs to be reviewed for spec completeness" -end diff --git a/spec/ruby/core/regexp/match_spec.rb b/spec/ruby/core/regexp/match_spec.rb index 0ce8347d2d..f31b893f5e 100644 --- a/spec/ruby/core/regexp/match_spec.rb +++ b/spec/ruby/core/regexp/match_spec.rb @@ -38,6 +38,10 @@ describe "Regexp#match" do -> { Regexp.allocate.match('foo') }.should raise_error(TypeError) end + it "raises TypeError on an uninitialized Regexp" do + -> { Regexp.allocate.match('foo'.encode("UTF-16LE")) }.should raise_error(TypeError) + end + describe "with [string, position]" do describe "when given a positive position" do it "matches the input at a given position" do diff --git a/spec/ruby/core/struct/clone_spec.rb b/spec/ruby/core/struct/clone_spec.rb new file mode 100644 index 0000000000..40c4d52d57 --- /dev/null +++ b/spec/ruby/core/struct/clone_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/dup' + +describe "Struct-based class#clone" do + it_behaves_like :struct_dup, :clone +end diff --git a/spec/ruby/core/struct/dup_spec.rb b/spec/ruby/core/struct/dup_spec.rb index cb54b7ceee..8b50c39014 100644 --- a/spec/ruby/core/struct/dup_spec.rb +++ b/spec/ruby/core/struct/dup_spec.rb @@ -1,8 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/dup' describe "Struct-based class#dup" do + it_behaves_like :struct_dup, :dup + # From https://github.com/jruby/jruby/issues/3686 it "retains an included module in the ancestor chain for the struct's singleton class" do klass = Struct.new(:foo) diff --git a/spec/ruby/core/struct/instance_variable_get_spec.rb b/spec/ruby/core/struct/instance_variable_get_spec.rb new file mode 100644 index 0000000000..e4a3ea87dc --- /dev/null +++ b/spec/ruby/core/struct/instance_variable_get_spec.rb @@ -0,0 +1,16 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Struct#instance_variable_get" do + it "returns nil for attributes" do + car = StructClasses::Car.new("Hugo", "Foo", "1972") + car.instance_variable_get(:@make).should be_nil + end + + it "returns a user value for variables with the same name as attributes" do + car = StructClasses::Car.new("Hugo", "Foo", "1972") + car.instance_variable_set :@make, "explicit" + car.instance_variable_get(:@make).should == "explicit" + car.make.should == "Hugo" + end +end diff --git a/spec/ruby/core/struct/new_spec.rb b/spec/ruby/core/struct/new_spec.rb index f888106950..564c49af01 100644 --- a/spec/ruby/core/struct/new_spec.rb +++ b/spec/ruby/core/struct/new_spec.rb @@ -74,6 +74,10 @@ describe "Struct.new" do end end + it "raises ArgumentError when there is a duplicate member" do + -> { Struct.new(:foo, :foo) }.should raise_error(ArgumentError, "duplicate member: foo") + end + it "raises a TypeError if object is not a Symbol" do obj = mock(':ruby') def obj.to_sym() :ruby end @@ -147,7 +151,6 @@ describe "Struct.new" do context "keyword_init: true option" do before :all do @struct_with_kwa = Struct.new(:name, :legs, keyword_init: true) - @struct_without_kwa = Struct.new(:name, :legs, keyword_init: false) end it "creates a class that accepts keyword arguments to initialize" do @@ -156,6 +159,10 @@ describe "Struct.new" do obj.legs.should == 4 end + it "raises when there is a duplicate member" do + -> { Struct.new(:foo, :foo, keyword_init: true) }.should raise_error(ArgumentError, "duplicate member: foo") + end + describe "new class instantiation" do it "accepts arguments as hash as well" do obj = @struct_with_kwa.new({name: "elefant", legs: 4}) @@ -163,6 +170,18 @@ describe "Struct.new" do obj.legs.should == 4 end + it "allows missing arguments" do + obj = @struct_with_kwa.new(name: "elefant") + obj.name.should == "elefant" + obj.legs.should be_nil + end + + it "allows no arguments" do + obj = @struct_with_kwa.new + obj.name.should be_nil + obj.legs.should be_nil + end + it "raises ArgumentError when passed not declared keyword argument" do -> { @struct_with_kwa.new(name: "elefant", legs: 4, foo: "bar") @@ -184,6 +203,10 @@ describe "Struct.new" do end context "keyword_init: false option" do + before :all do + @struct_without_kwa = Struct.new(:name, :legs, keyword_init: false) + end + it "behaves like it does without :keyword_init option" do obj = @struct_without_kwa.new("elefant", 4) obj.name.should == "elefant" diff --git a/spec/ruby/core/struct/shared/dup.rb b/spec/ruby/core/struct/shared/dup.rb new file mode 100644 index 0000000000..994f3f443e --- /dev/null +++ b/spec/ruby/core/struct/shared/dup.rb @@ -0,0 +1,9 @@ +describe :struct_dup, shared: true do + it "duplicates members" do + klass = Struct.new(:foo, :bar) + instance = klass.new(14, 2) + duped = instance.send(@method) + duped.foo.should == 14 + duped.bar.should == 2 + end +end diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb index dcdd83a085..01ee47faa1 100644 --- a/spec/ruby/core/time/new_spec.rb +++ b/spec/ruby/core/time/new_spec.rb @@ -113,6 +113,12 @@ describe "Time.new with a utc_offset argument" do it "raises ArgumentError if the utc_offset argument is greater than or equal to 10e9" do -> { Time.new(2000, 1, 1, 0, 0, 0, 1000000000) }.should raise_error(ArgumentError) end + + it "raises ArgumentError if the month is greater than 12" do + # For some reason MRI uses a different message for month in 13-15 and month>=16 + -> { Time.new(2000, 13, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, /(mon|argument) out of range/) + -> { Time.new(2000, 16, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, "argument out of range") + end end ruby_version_is "2.6" do |