From 607df5d706d2b24443c36f509c2aa25c6b2fa880 Mon Sep 17 00:00:00 2001 From: Andrii Konchyn Date: Mon, 1 Jun 2026 17:24:14 +0300 Subject: [PATCH 1/4] Update to ruby/spec@6b3b96d --- spec/ruby/.rubocop.yml | 4 - spec/ruby/.rubocop_todo.yml | 36 +- spec/ruby/CONTRIBUTING.md | 24 +- spec/ruby/bin/rubocop | 2 +- .../fixtures/bin/bad_embedded_ruby.txt | 2 +- .../fixtures/bin/embedded_ruby.txt | 2 +- spec/ruby/core/argf/each_byte_spec.rb | 58 +- spec/ruby/core/argf/each_char_spec.rb | 58 +- spec/ruby/core/argf/each_codepoint_spec.rb | 58 +- spec/ruby/core/argf/each_line_spec.rb | 62 +- spec/ruby/core/argf/each_spec.rb | 5 +- spec/ruby/core/argf/eof_spec.rb | 28 +- spec/ruby/core/argf/filename_spec.rb | 28 +- spec/ruby/core/argf/fileno_spec.rb | 24 +- spec/ruby/core/argf/inspect_spec.rb | 7 + spec/ruby/core/argf/path_spec.rb | 5 +- spec/ruby/core/argf/pos_spec.rb | 31 +- spec/ruby/core/argf/readlines_spec.rb | 22 +- spec/ruby/core/argf/shared/each_byte.rb | 58 -- spec/ruby/core/argf/shared/each_char.rb | 58 -- spec/ruby/core/argf/shared/each_codepoint.rb | 58 -- spec/ruby/core/argf/shared/each_line.rb | 62 -- spec/ruby/core/argf/shared/eof.rb | 24 - spec/ruby/core/argf/shared/filename.rb | 28 - spec/ruby/core/argf/shared/fileno.rb | 24 - spec/ruby/core/argf/shared/pos.rb | 31 - spec/ruby/core/argf/shared/readlines.rb | 22 - spec/ruby/core/argf/tell_spec.rb | 5 +- spec/ruby/core/argf/to_a_spec.rb | 5 +- spec/ruby/core/argf/to_i_spec.rb | 5 +- spec/ruby/core/array/append_spec.rb | 5 +- spec/ruby/core/array/collect_spec.rb | 138 ++- .../ruby/core/array/element_reference_spec.rb | 857 +++++++++++++++++- spec/ruby/core/array/filter_spec.rb | 11 +- spec/ruby/core/array/find_index_spec.rb | 40 +- spec/ruby/core/array/index_spec.rb | 5 +- spec/ruby/core/array/inspect_spec.rb | 105 ++- spec/ruby/core/array/join_spec.rb | 98 +- spec/ruby/core/array/length_spec.rb | 11 +- spec/ruby/core/array/map_spec.rb | 10 +- spec/ruby/core/array/prepend_spec.rb | 6 +- spec/ruby/core/array/push_spec.rb | 33 +- spec/ruby/core/array/replace_spec.rb | 60 +- spec/ruby/core/array/select_spec.rb | 33 +- spec/ruby/core/array/shared/collect.rb | 141 --- spec/ruby/core/array/shared/index.rb | 41 - spec/ruby/core/array/shared/inspect.rb | 107 --- spec/ruby/core/array/shared/join.rb | 97 -- spec/ruby/core/array/shared/length.rb | 11 - spec/ruby/core/array/shared/push.rb | 33 - spec/ruby/core/array/shared/replace.rb | 60 -- spec/ruby/core/array/shared/select.rb | 35 - spec/ruby/core/array/shared/slice.rb | 857 ------------------ spec/ruby/core/array/shared/unshift.rb | 64 -- spec/ruby/core/array/size_spec.rb | 6 +- spec/ruby/core/array/slice_spec.rb | 5 +- spec/ruby/core/array/to_s_spec.rb | 7 +- spec/ruby/core/array/unshift_spec.rb | 64 +- spec/ruby/core/complex/abs_spec.rb | 10 +- spec/ruby/core/complex/angle_spec.rb | 5 +- spec/ruby/core/complex/arg_spec.rb | 9 +- spec/ruby/core/complex/conj_spec.rb | 5 +- spec/ruby/core/complex/conjugate_spec.rb | 8 +- spec/ruby/core/complex/divide_spec.rb | 82 +- spec/ruby/core/complex/imag_spec.rb | 5 +- spec/ruby/core/complex/imaginary_spec.rb | 8 +- spec/ruby/core/complex/magnitude_spec.rb | 5 +- spec/ruby/core/complex/phase_spec.rb | 5 +- spec/ruby/core/complex/quo_spec.rb | 5 +- spec/ruby/core/complex/rect_spec.rb | 9 +- spec/ruby/core/complex/rectangular_spec.rb | 110 ++- spec/ruby/core/complex/shared/abs.rb | 10 - spec/ruby/core/complex/shared/arg.rb | 9 - spec/ruby/core/complex/shared/conjugate.rb | 8 - spec/ruby/core/complex/shared/divide.rb | 82 -- spec/ruby/core/complex/shared/image.rb | 8 - spec/ruby/core/complex/shared/rect.rb | 94 -- spec/ruby/core/data/deconstruct_keys_spec.rb | 2 +- spec/ruby/core/data/initialize_spec.rb | 48 + spec/ruby/core/data/inspect_spec.rb | 61 +- spec/ruby/core/data/shared/inspect.rb | 62 -- spec/ruby/core/data/to_s_spec.rb | 7 +- spec/ruby/core/dir/exist_spec.rb | 57 +- spec/ruby/core/dir/getwd_spec.rb | 12 +- spec/ruby/core/dir/open_spec.rb | 73 +- spec/ruby/core/dir/path_spec.rb | 26 +- spec/ruby/core/dir/pos_spec.rb | 23 +- spec/ruby/core/dir/pwd_spec.rb | 45 +- spec/ruby/core/dir/shared/exist.rb | 57 -- spec/ruby/core/dir/shared/open.rb | 73 -- spec/ruby/core/dir/shared/path.rb | 30 - spec/ruby/core/dir/shared/pos.rb | 27 - spec/ruby/core/dir/shared/pwd.rb | 45 - spec/ruby/core/dir/tell_spec.rb | 27 +- spec/ruby/core/dir/to_path_spec.rb | 12 +- spec/ruby/core/encoding/name_spec.rb | 13 +- spec/ruby/core/encoding/shared/name.rb | 15 - spec/ruby/core/encoding/to_s_spec.rb | 5 +- .../core/enumerable/shared/value_packing.rb | 26 + spec/ruby/core/enumerable/take_spec.rb | 8 + .../core/enumerator/generator/each_spec.rb | 40 - .../enumerator/generator/initialize_spec.rb | 26 - spec/ruby/core/enumerator/lazy/take_spec.rb | 8 + spec/ruby/core/enumerator/new_spec.rb | 59 +- spec/ruby/core/enumerator/produce_spec.rb | 44 + .../ruby/core/enumerator/product/each_spec.rb | 14 + .../ruby/core/enumerator/product/size_spec.rb | 10 + .../core/enumerator/yielder/append_spec.rb | 35 - .../enumerator/yielder/initialize_spec.rb | 18 - .../core/enumerator/yielder/to_proc_spec.rb | 16 - .../core/enumerator/yielder/yield_spec.rb | 33 - spec/ruby/core/hash/each_pair_spec.rb | 106 ++- spec/ruby/core/hash/each_spec.rb | 10 +- spec/ruby/core/hash/element_set_spec.rb | 118 ++- spec/ruby/core/hash/filter_spec.rb | 9 +- spec/ruby/core/hash/has_key_spec.rb | 6 +- spec/ruby/core/hash/has_value_spec.rb | 15 +- spec/ruby/core/hash/include_spec.rb | 39 +- spec/ruby/core/hash/inspect_spec.rb | 122 ++- spec/ruby/core/hash/key_spec.rb | 30 +- spec/ruby/core/hash/length_spec.rb | 6 +- spec/ruby/core/hash/member_spec.rb | 6 +- spec/ruby/core/hash/merge_spec.rb | 6 +- spec/ruby/core/hash/select_spec.rb | 108 ++- spec/ruby/core/hash/shared/each.rb | 105 --- spec/ruby/core/hash/shared/index.rb | 37 - spec/ruby/core/hash/shared/key.rb | 38 - spec/ruby/core/hash/shared/length.rb | 12 - spec/ruby/core/hash/shared/select.rb | 112 --- spec/ruby/core/hash/shared/store.rb | 115 --- spec/ruby/core/hash/shared/to_s.rb | 124 --- spec/ruby/core/hash/shared/update.rb | 76 -- spec/ruby/core/hash/shared/value.rb | 14 - spec/ruby/core/hash/shared/values_at.rb | 9 - spec/ruby/core/hash/size_spec.rb | 13 +- spec/ruby/core/hash/store_spec.rb | 6 +- spec/ruby/core/hash/to_s_spec.rb | 6 +- spec/ruby/core/hash/update_spec.rb | 76 +- spec/ruby/core/hash/value_spec.rb | 6 +- spec/ruby/core/hash/values_at_spec.rb | 10 +- spec/ruby/core/io/buffer/for_spec.rb | 1 + spec/ruby/core/io/buffer/map_spec.rb | 4 +- spec/ruby/core/io/buffer/shared_spec.rb | 4 +- spec/ruby/core/io/buffer/transfer_spec.rb | 3 +- spec/ruby/core/io/buffer/valid_spec.rb | 11 - spec/ruby/core/kernel/Integer_spec.rb | 43 +- .../ruby/core/kernel/caller_locations_spec.rb | 5 +- spec/ruby/core/kernel/caller_spec.rb | 5 +- spec/ruby/core/kernel/inspect_spec.rb | 13 + spec/ruby/core/kernel/require_spec.rb | 28 +- spec/ruby/core/kernel/shared/sprintf.rb | 44 +- spec/ruby/core/method/inspect_spec.rb | 2 + spec/ruby/core/method/original_name_spec.rb | 16 + .../core/method/shared/aliased_inspect.rb | 31 + spec/ruby/core/method/to_s_spec.rb | 2 + spec/ruby/core/mutex/sleep_spec.rb | 8 + spec/ruby/core/process/constants_spec.rb | 46 +- spec/ruby/core/string/shared/each_line.rb | 36 +- spec/ruby/core/thread/raise_spec.rb | 25 + spec/ruby/core/unboundmethod/inspect_spec.rb | 2 + .../core/unboundmethod/original_name_spec.rb | 16 + spec/ruby/core/unboundmethod/to_s_spec.rb | 2 + spec/ruby/language/array_spec.rb | 10 +- spec/ruby/language/case_spec.rb | 35 + spec/ruby/language/defined_spec.rb | 16 + spec/ruby/language/fixtures/defined.rb | 27 + spec/ruby/language/lambda_spec.rb | 6 + .../library/socket/ipsocket/inspect_spec.rb | 24 + spec/ruby/library/socket/socket/tcp_spec.rb | 24 +- spec/ruby/library/socket/spec_helper.rb | 20 +- .../library/socket/tcpsocket/shared/new.rb | 20 +- .../library/socket/udpsocket/inspect_spec.rb | 17 - spec/ruby/optional/capi/ext/gc_spec.c | 29 + spec/ruby/optional/capi/gc_spec.rb | 18 + spec/ruby/optional/capi/string_spec.rb | 6 +- spec/ruby/shared/kernel/raise.rb | 2 +- 176 files changed, 3735 insertions(+), 3516 deletions(-) create mode 100644 spec/ruby/core/argf/inspect_spec.rb delete mode 100644 spec/ruby/core/argf/shared/each_byte.rb delete mode 100644 spec/ruby/core/argf/shared/each_char.rb delete mode 100644 spec/ruby/core/argf/shared/each_codepoint.rb delete mode 100644 spec/ruby/core/argf/shared/each_line.rb delete mode 100644 spec/ruby/core/argf/shared/eof.rb delete mode 100644 spec/ruby/core/argf/shared/filename.rb delete mode 100644 spec/ruby/core/argf/shared/fileno.rb delete mode 100644 spec/ruby/core/argf/shared/pos.rb delete mode 100644 spec/ruby/core/argf/shared/readlines.rb delete mode 100644 spec/ruby/core/array/shared/collect.rb delete mode 100644 spec/ruby/core/array/shared/index.rb delete mode 100644 spec/ruby/core/array/shared/inspect.rb delete mode 100644 spec/ruby/core/array/shared/length.rb delete mode 100644 spec/ruby/core/array/shared/push.rb delete mode 100644 spec/ruby/core/array/shared/replace.rb delete mode 100644 spec/ruby/core/array/shared/select.rb delete mode 100644 spec/ruby/core/array/shared/slice.rb delete mode 100644 spec/ruby/core/array/shared/unshift.rb delete mode 100644 spec/ruby/core/complex/shared/abs.rb delete mode 100644 spec/ruby/core/complex/shared/arg.rb delete mode 100644 spec/ruby/core/complex/shared/conjugate.rb delete mode 100644 spec/ruby/core/complex/shared/divide.rb delete mode 100644 spec/ruby/core/complex/shared/image.rb delete mode 100644 spec/ruby/core/complex/shared/rect.rb delete mode 100644 spec/ruby/core/data/shared/inspect.rb delete mode 100644 spec/ruby/core/dir/shared/exist.rb delete mode 100644 spec/ruby/core/dir/shared/open.rb delete mode 100644 spec/ruby/core/dir/shared/path.rb delete mode 100644 spec/ruby/core/dir/shared/pwd.rb delete mode 100644 spec/ruby/core/encoding/shared/name.rb create mode 100644 spec/ruby/core/enumerable/shared/value_packing.rb delete mode 100644 spec/ruby/core/enumerator/generator/each_spec.rb delete mode 100644 spec/ruby/core/enumerator/generator/initialize_spec.rb delete mode 100644 spec/ruby/core/enumerator/yielder/append_spec.rb delete mode 100644 spec/ruby/core/enumerator/yielder/initialize_spec.rb delete mode 100644 spec/ruby/core/enumerator/yielder/to_proc_spec.rb delete mode 100644 spec/ruby/core/enumerator/yielder/yield_spec.rb delete mode 100644 spec/ruby/core/hash/shared/each.rb delete mode 100644 spec/ruby/core/hash/shared/index.rb delete mode 100644 spec/ruby/core/hash/shared/key.rb delete mode 100644 spec/ruby/core/hash/shared/length.rb delete mode 100644 spec/ruby/core/hash/shared/select.rb delete mode 100644 spec/ruby/core/hash/shared/store.rb delete mode 100644 spec/ruby/core/hash/shared/to_s.rb delete mode 100644 spec/ruby/core/hash/shared/update.rb delete mode 100644 spec/ruby/core/hash/shared/value.rb delete mode 100644 spec/ruby/core/hash/shared/values_at.rb create mode 100644 spec/ruby/core/method/shared/aliased_inspect.rb create mode 100644 spec/ruby/library/socket/ipsocket/inspect_spec.rb delete mode 100644 spec/ruby/library/socket/udpsocket/inspect_spec.rb diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml index 0b59a11512f445..0b5dcb80a23c9f 100644 --- a/spec/ruby/.rubocop.yml +++ b/spec/ruby/.rubocop.yml @@ -54,10 +54,6 @@ Lint/RedundantRequireStatement: - library/fiber/**/*.rb - optional/capi/fiber_spec.rb -Lint/RedundantSafeNavigation: - Exclude: - - language/safe_navigator_spec.rb - Lint/RedundantSplatExpansion: Enabled: false diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml index bd30f3f14af1ba..f998002c6de44a 100644 --- a/spec/ruby/.rubocop_todo.yml +++ b/spec/ruby/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-10-12 16:01:45 UTC using RuboCop version 1.66.1. +# on 2026-05-29 08:10:07 UTC using RuboCop version 1.86.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -11,17 +11,22 @@ Lint/DuplicateCaseCondition: Exclude: - 'language/case_spec.rb' -# Offense count: 6 +# Offense count: 20 Lint/DuplicateMethods: Exclude: - 'core/array/fixtures/encoded_strings.rb' - 'core/method/fixtures/classes.rb' + - 'core/module/const_added_spec.rb' + - 'core/module/define_method_spec.rb' - 'core/module/fixtures/classes.rb' + - 'core/module/method_added_spec.rb' + - 'core/module/name_spec.rb' + - 'core/proc/new_spec.rb' - 'core/unboundmethod/fixtures/classes.rb' - 'fixtures/class.rb' + - 'language/assignments_spec.rb' # Offense count: 8 -# This cop supports safe autocorrection (--autocorrect). Lint/EnsureReturn: Exclude: - 'language/fixtures/ensure.rb' @@ -39,12 +44,12 @@ Lint/FloatOutOfRange: Exclude: - 'core/string/modulo_spec.rb' -# Offense count: 2 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Lint/ImplicitStringConcatenation: Exclude: - - 'language/string_spec.rb' - 'core/string/chilled_string_spec.rb' + - 'language/string_spec.rb' # Offense count: 4 Lint/IneffectiveAccessModifier: @@ -53,12 +58,11 @@ Lint/IneffectiveAccessModifier: - 'core/module/fixtures/classes.rb' - 'language/fixtures/private.rb' -# Offense count: 71 +# Offense count: 12 # This cop supports safe autocorrection (--autocorrect). Lint/LiteralInInterpolation: Exclude: - 'core/module/refine_spec.rb' - - 'core/regexp/shared/new.rb' - 'core/string/shared/to_sym.rb' - 'language/alias_spec.rb' - 'language/defined_spec.rb' @@ -80,6 +84,16 @@ Lint/ParenthesesAsGroupedExpression: - 'language/block_spec.rb' - 'language/method_spec.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods. +# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? +# AdditionalNilMethods: present?, blank?, try, try! +Lint/RedundantSafeNavigation: + Exclude: + - 'language/safe_navigator_spec.rb' + - 'language/fixtures/rescue_captures.rb' + # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantStringCoercion: @@ -87,6 +101,7 @@ Lint/RedundantStringCoercion: - 'core/io/print_spec.rb' # Offense count: 1 +# Configuration parameters: AllowRBSInlineAnnotation. Lint/SelfAssignment: Exclude: - 'core/gc/auto_compact_spec.rb' @@ -97,7 +112,7 @@ Lint/ShadowedArgument: Exclude: - 'language/fixtures/super.rb' -# Offense count: 45 +# Offense count: 49 # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Enabled: false @@ -110,13 +125,14 @@ Lint/UnderscorePrefixedVariableName: - 'core/io/popen_spec.rb' - 'language/block_spec.rb' -# Offense count: 7 +# Offense count: 9 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, ContextCreatingMethods, MethodCreatingMethods. +# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'core/module/define_method_spec.rb' - 'core/module/fixtures/classes.rb' - 'core/module/module_function_spec.rb' - 'core/module/private_class_method_spec.rb' + - 'language/fixtures/def.rb' - 'language/fixtures/send.rb' diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md index 0b0f2514409681..a474e205f09e2b 100644 --- a/spec/ruby/CONTRIBUTING.md +++ b/spec/ruby/CONTRIBUTING.md @@ -229,7 +229,7 @@ to avoid duplication of specs, we have shared specs that are re-used in other sp bit tricky however, so let's go over it. Commonly, if a shared spec is only reused within its own module, the shared spec will live within a -shared directory inside that module's directory. For example, the `core/hash/shared/key.rb` spec is +shared directory inside that module's directory. For example, the `core/hash/shared/iteration.rb` spec is only used by `Hash` specs, and so it lives inside `core/hash/shared/`. When a shared spec is used across multiple modules or classes, it lives within the `shared/` directory. @@ -243,25 +243,25 @@ variables from the implementor spec: `@method` and `@object`, which the implemen Here's an example of a snippet of a shared spec and two specs which integrates it: ```ruby -# core/hash/shared/key.rb -describe :hash_key_p, shared: true do - it "returns true if the key's matching value was false" do - { xyz: false }.send(@method, :xyz).should == true +# core/hash/shared/iteration.rb +describe :hash_iteration_no_block, shared: true do + it "returns an Enumerator if called on a non-empty hash without a block" do + { 1 => 2 }.send(@method).should.instance_of?(Enumerator) end end -# core/hash/key_spec.rb -describe "Hash#key?" do - it_behaves_like :hash_key_p, :key? +# core/hash/select_spec.rb +describe "Hash#select" do + it_behaves_like :hash_iteration_no_block, :select end -# core/hash/include_spec.rb -describe "Hash#include?" do - it_behaves_like :hash_key_p, :include? +# core/hash/reject_spec.rb +describe "Hash#reject" do + it_behaves_like :hash_iteration_no_block, :reject end ``` -In the example, the first `describe` defines the shared spec `:hash_key_p`, which defines a spec that +In the example, the first `describe` defines the shared spec `:hash_iteration_no_block`, which defines a spec that calls the `@method` method with an expectation. In the implementor spec, we use `it_behaves_like` to integrate the shared spec. `it_behaves_like` takes 3 parameters: the key of the shared spec, a method, and an object. These last two parameters are accessible via `@method` and `@object` in the shared spec. diff --git a/spec/ruby/bin/rubocop b/spec/ruby/bin/rubocop index 38254f13e48771..0937c479063430 100755 --- a/spec/ruby/bin/rubocop +++ b/spec/ruby/bin/rubocop @@ -6,7 +6,7 @@ require 'bundler/inline' gemfile do source 'https://rubygems.org' - gem 'rubocop', '1.66.1' + gem 'rubocop', '1.86.2' end exec(Gem.bin_path('rubocop', 'rubocop'), *ARGV) diff --git a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt index a2b7ad085f5515..61b946977a75d0 100644 --- a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt +++ b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt @@ -1,3 +1,3 @@ -@@@This line is not value Ruby +@@@This line is not valid Ruby #!rub_y puts 'success' diff --git a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt index 1da779b1b96b37..0ec0f358db7458 100644 --- a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt +++ b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt @@ -1,3 +1,3 @@ -@@@This line is not value Ruby +@@@This line is not valid Ruby #!ruby puts 'success' diff --git a/spec/ruby/core/argf/each_byte_spec.rb b/spec/ruby/core/argf/each_byte_spec.rb index c5cce9f2509f97..d9e4e7fe5b9679 100644 --- a/spec/ruby/core/argf/each_byte_spec.rb +++ b/spec/ruby/core/argf/each_byte_spec.rb @@ -1,6 +1,60 @@ require_relative '../../spec_helper' -require_relative 'shared/each_byte' describe "ARGF.each_byte" do - it_behaves_like :argf_each_byte, :each_byte + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @bytes = [] + File.read(@file1_name).each_byte { |b| @bytes << b } + File.read(@file2_name).each_byte { |b| @bytes << b } + end + + it "yields each byte of all streams to the passed block" do + argf [@file1_name, @file2_name] do + bytes = [] + @argf.each_byte { |b| bytes << b } + bytes.should == @bytes + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.each_byte {}.should.equal?(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf [@file1_name, @file2_name] do + enum = @argf.each_byte + enum.should.instance_of?(Enumerator) + + bytes = [] + enum.each { |b| bytes << b } + bytes.should == @bytes + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + enum = @argf.each_byte + enum.should.instance_of?(Enumerator) + + bytes = [] + enum.each { |b| bytes << b } + bytes.should == @bytes + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.each_byte.size.should == nil + end + end + end + end + end end diff --git a/spec/ruby/core/argf/each_char_spec.rb b/spec/ruby/core/argf/each_char_spec.rb index 724e5e6e3e1581..62d19b6574619b 100644 --- a/spec/ruby/core/argf/each_char_spec.rb +++ b/spec/ruby/core/argf/each_char_spec.rb @@ -1,6 +1,60 @@ require_relative '../../spec_helper' -require_relative 'shared/each_char' describe "ARGF.each_char" do - it_behaves_like :argf_each_char, :each_char + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @chars = [] + File.read(@file1_name).each_char { |c| @chars << c } + File.read(@file2_name).each_char { |c| @chars << c } + end + + it "yields each char of all streams to the passed block" do + argf [@file1_name, @file2_name] do + chars = [] + @argf.each_char { |c| chars << c } + chars.should == @chars + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.each_char {}.should.equal?(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf [@file1_name, @file2_name] do + enum = @argf.each_char + enum.should.instance_of?(Enumerator) + + chars = [] + enum.each { |c| chars << c } + chars.should == @chars + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + enum = @argf.each_char + enum.should.instance_of?(Enumerator) + + chars = [] + enum.each { |c| chars << c } + chars.should == @chars + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.each_char.size.should == nil + end + end + end + end + end end diff --git a/spec/ruby/core/argf/each_codepoint_spec.rb b/spec/ruby/core/argf/each_codepoint_spec.rb index 0bf8bf9764bbe9..ad55785cbace0b 100644 --- a/spec/ruby/core/argf/each_codepoint_spec.rb +++ b/spec/ruby/core/argf/each_codepoint_spec.rb @@ -1,6 +1,60 @@ require_relative '../../spec_helper' -require_relative 'shared/each_codepoint' describe "ARGF.each_codepoint" do - it_behaves_like :argf_each_codepoint, :each_codepoint + before :each do + file1_name = fixture __FILE__, "file1.txt" + file2_name = fixture __FILE__, "file2.txt" + @filenames = [file1_name, file2_name] + + @codepoints = File.read(file1_name).codepoints + @codepoints.concat File.read(file2_name).codepoints + end + + it "is a public method" do + argf @filenames do + @argf.public_methods(false).should.include?(:each_codepoint) + end + end + + it "does not require arguments" do + argf @filenames do + @argf.method(:each_codepoint).arity.should == 0 + end + end + + it "returns self when passed a block" do + argf @filenames do + @argf.each_codepoint {}.should.equal?(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf @filenames do + @argf.each_codepoint.should.instance_of?(Enumerator) + end + end + + it "yields each codepoint of all streams" do + argf @filenames do + @argf.each_codepoint.to_a.should == @codepoints + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf @filenames do + @argf.each_codepoint.should.instance_of?(Enumerator) + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf @filenames do + @argf.each_codepoint.size.should == nil + end + end + end + end + end end diff --git a/spec/ruby/core/argf/each_line_spec.rb b/spec/ruby/core/argf/each_line_spec.rb index 52a7e5c4119307..fc4d6433b881a4 100644 --- a/spec/ruby/core/argf/each_line_spec.rb +++ b/spec/ruby/core/argf/each_line_spec.rb @@ -1,6 +1,64 @@ require_relative '../../spec_helper' -require_relative 'shared/each_line' describe "ARGF.each_line" do - it_behaves_like :argf_each_line, :each_line + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @lines = File.readlines @file1_name + @lines += File.readlines @file2_name + end + + it "is a public method" do + argf [@file1_name, @file2_name] do + @argf.public_methods(false).should.include?(:each_line) + end + end + + it "requires multiple arguments" do + argf [@file1_name, @file2_name] do + @argf.method(:each_line).arity.should < 0 + end + end + + it "reads each line of files" do + argf [@file1_name, @file2_name] do + lines = [] + @argf.each_line { |b| lines << b } + lines.should == @lines + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.each_line {}.should.equal?(@argf) + end + end + + describe "with a separator" do + it "yields each separated section of all streams" do + argf [@file1_name, @file2_name] do + @argf.send(:each_line, '.').to_a.should == + (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.')) + end + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + @argf.each_line.should.instance_of?(Enumerator) + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.each_line.size.should == nil + end + end + end + end + end end diff --git a/spec/ruby/core/argf/each_spec.rb b/spec/ruby/core/argf/each_spec.rb index 5742ba43bdf85d..25f60b31d29dec 100644 --- a/spec/ruby/core/argf/each_spec.rb +++ b/spec/ruby/core/argf/each_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/each_line' describe "ARGF.each" do - it_behaves_like :argf_each_line, :each + it "is an alias of ARGF.each_line" do + ARGF.method(:each).should == ARGF.method(:each_line) + end end diff --git a/spec/ruby/core/argf/eof_spec.rb b/spec/ruby/core/argf/eof_spec.rb index 518f6e566e2c75..940d104d693d55 100644 --- a/spec/ruby/core/argf/eof_spec.rb +++ b/spec/ruby/core/argf/eof_spec.rb @@ -1,10 +1,32 @@ require_relative '../../spec_helper' -require_relative 'shared/eof' describe "ARGF.eof" do - it_behaves_like :argf_eof, :eof + it "is an alias of ARGF.eof?" do + ARGF.method(:eof).should == ARGF.method(:eof?) + end end describe "ARGF.eof?" do - it_behaves_like :argf_eof, :eof? + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns true when reaching the end of a file" do + argf [@file1, @file2] do + result = [] + while @argf.gets + result << @argf.eof? + end + result.should == [false, true, false, true] + end + end + + it "raises IOError when called on a closed stream" do + argf [@file1] do + @argf.read + -> { @argf.eof? }.should.raise(IOError) + end + end end diff --git a/spec/ruby/core/argf/filename_spec.rb b/spec/ruby/core/argf/filename_spec.rb index 7c0446269d1483..f4b6e922c6bdc9 100644 --- a/spec/ruby/core/argf/filename_spec.rb +++ b/spec/ruby/core/argf/filename_spec.rb @@ -1,6 +1,30 @@ require_relative '../../spec_helper' -require_relative 'shared/filename' describe "ARGF.filename" do - it_behaves_like :argf_filename, :filename + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the current file name on each file" do + argf [@file1, @file2] do + result = [] + # returns first current file even when not yet open + result << @argf.filename + result << @argf.filename while @argf.gets + # returns last current file even when closed + result << @argf.filename + + result.map! { |f| File.expand_path(f) } + result.should == [@file1, @file1, @file1, @file2, @file2, @file2] + end + end + + # NOTE: this test assumes that fixtures files have two lines each + it "sets the $FILENAME global variable with the current file name on each file" do + script = fixture __FILE__, "filename.rb" + out = ruby_exe(script, args: [@file1, @file2]) + out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n" + end end diff --git a/spec/ruby/core/argf/fileno_spec.rb b/spec/ruby/core/argf/fileno_spec.rb index 29d50c35829657..99245f043c306b 100644 --- a/spec/ruby/core/argf/fileno_spec.rb +++ b/spec/ruby/core/argf/fileno_spec.rb @@ -1,6 +1,26 @@ require_relative '../../spec_helper' -require_relative 'shared/fileno' describe "ARGF.fileno" do - it_behaves_like :argf_fileno, :fileno + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the current file number on each file" do + argf [@file1, @file2] do + result = [] + # returns first current file even when not yet open + result << @argf.fileno while @argf.gets + # returns last current file even when closed + result.map { |d| d.class }.should == [Integer, Integer, Integer, Integer] + end + end + + it "raises an ArgumentError when called on a closed stream" do + argf [@file1] do + @argf.read + -> { @argf.fileno }.should.raise(ArgumentError) + end + end end diff --git a/spec/ruby/core/argf/inspect_spec.rb b/spec/ruby/core/argf/inspect_spec.rb new file mode 100644 index 00000000000000..df0e3ba8dca82d --- /dev/null +++ b/spec/ruby/core/argf/inspect_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "ARGF.inspect" do + it "is an alias of ARGF.to_s" do + ARGF.method(:inspect).should == ARGF.method(:to_s) + end +end diff --git a/spec/ruby/core/argf/path_spec.rb b/spec/ruby/core/argf/path_spec.rb index 7120f7d0e33874..2f7b91999f0b52 100644 --- a/spec/ruby/core/argf/path_spec.rb +++ b/spec/ruby/core/argf/path_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/filename' describe "ARGF.path" do - it_behaves_like :argf_filename, :path + it "is an alias of ARGF.filename" do + ARGF.method(:path).should == ARGF.method(:filename) + end end diff --git a/spec/ruby/core/argf/pos_spec.rb b/spec/ruby/core/argf/pos_spec.rb index fb3f25b945b955..1ff16e4f17bca9 100644 --- a/spec/ruby/core/argf/pos_spec.rb +++ b/spec/ruby/core/argf/pos_spec.rb @@ -1,8 +1,35 @@ require_relative '../../spec_helper' -require_relative 'shared/pos' describe "ARGF.pos" do - it_behaves_like :argf_pos, :pos + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "gives the correct position for each read operation" do + argf [@file1, @file2] do + size1 = File.size(@file1) + size2 = File.size(@file2) + + @argf.read(2) + @argf.pos.should == 2 + @argf.read(size1-2) + @argf.pos.should == size1 + @argf.read(6) + @argf.pos.should == 6 + @argf.rewind + @argf.pos.should == 0 + @argf.read(size2) + @argf.pos.should == size2 + end + end + + it "raises an ArgumentError when called on a closed stream" do + argf [@file1] do + @argf.read + -> { @argf.pos }.should.raise(ArgumentError) + end + end end describe "ARGF.pos=" do diff --git a/spec/ruby/core/argf/readlines_spec.rb b/spec/ruby/core/argf/readlines_spec.rb index 30be936dab0fd4..156bb6a33f1b3f 100644 --- a/spec/ruby/core/argf/readlines_spec.rb +++ b/spec/ruby/core/argf/readlines_spec.rb @@ -1,6 +1,24 @@ require_relative '../../spec_helper' -require_relative 'shared/readlines' describe "ARGF.readlines" do - it_behaves_like :argf_readlines, :readlines + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + + @lines = File.readlines(@file1) + @lines += File.readlines(@file2) + end + + it "reads all lines of all files" do + argf [@file1, @file2] do + @argf.readlines.should == @lines + end + end + + it "returns an empty Array when end of stream reached" do + argf [@file1, @file2] do + @argf.read + @argf.readlines.should == [] + end + end end diff --git a/spec/ruby/core/argf/shared/each_byte.rb b/spec/ruby/core/argf/shared/each_byte.rb deleted file mode 100644 index 48c9ae04f88b15..00000000000000 --- a/spec/ruby/core/argf/shared/each_byte.rb +++ /dev/null @@ -1,58 +0,0 @@ -describe :argf_each_byte, shared: true do - before :each do - @file1_name = fixture __FILE__, "file1.txt" - @file2_name = fixture __FILE__, "file2.txt" - - @bytes = [] - File.read(@file1_name).each_byte { |b| @bytes << b } - File.read(@file2_name).each_byte { |b| @bytes << b } - end - - it "yields each byte of all streams to the passed block" do - argf [@file1_name, @file2_name] do - bytes = [] - @argf.send(@method) { |b| bytes << b } - bytes.should == @bytes - end - end - - it "returns self when passed a block" do - argf [@file1_name, @file2_name] do - @argf.send(@method) {}.should.equal?(@argf) - end - end - - it "returns an Enumerator when passed no block" do - argf [@file1_name, @file2_name] do - enum = @argf.send(@method) - enum.should.instance_of?(Enumerator) - - bytes = [] - enum.each { |b| bytes << b } - bytes.should == @bytes - end - end - - describe "when no block is given" do - it "returns an Enumerator" do - argf [@file1_name, @file2_name] do - enum = @argf.send(@method) - enum.should.instance_of?(Enumerator) - - bytes = [] - enum.each { |b| bytes << b } - bytes.should == @bytes - end - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - argf [@file1_name, @file2_name] do - @argf.send(@method).size.should == nil - end - end - end - end - end -end diff --git a/spec/ruby/core/argf/shared/each_char.rb b/spec/ruby/core/argf/shared/each_char.rb deleted file mode 100644 index 4b5e8452ab9193..00000000000000 --- a/spec/ruby/core/argf/shared/each_char.rb +++ /dev/null @@ -1,58 +0,0 @@ -describe :argf_each_char, shared: true do - before :each do - @file1_name = fixture __FILE__, "file1.txt" - @file2_name = fixture __FILE__, "file2.txt" - - @chars = [] - File.read(@file1_name).each_char { |c| @chars << c } - File.read(@file2_name).each_char { |c| @chars << c } - end - - it "yields each char of all streams to the passed block" do - argf [@file1_name, @file2_name] do - chars = [] - @argf.send(@method) { |c| chars << c } - chars.should == @chars - end - end - - it "returns self when passed a block" do - argf [@file1_name, @file2_name] do - @argf.send(@method) {}.should.equal?(@argf) - end - end - - it "returns an Enumerator when passed no block" do - argf [@file1_name, @file2_name] do - enum = @argf.send(@method) - enum.should.instance_of?(Enumerator) - - chars = [] - enum.each { |c| chars << c } - chars.should == @chars - end - end - - describe "when no block is given" do - it "returns an Enumerator" do - argf [@file1_name, @file2_name] do - enum = @argf.send(@method) - enum.should.instance_of?(Enumerator) - - chars = [] - enum.each { |c| chars << c } - chars.should == @chars - end - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - argf [@file1_name, @file2_name] do - @argf.send(@method).size.should == nil - end - end - end - end - end -end diff --git a/spec/ruby/core/argf/shared/each_codepoint.rb b/spec/ruby/core/argf/shared/each_codepoint.rb deleted file mode 100644 index 3137306ad5f572..00000000000000 --- a/spec/ruby/core/argf/shared/each_codepoint.rb +++ /dev/null @@ -1,58 +0,0 @@ -describe :argf_each_codepoint, shared: true do - before :each do - file1_name = fixture __FILE__, "file1.txt" - file2_name = fixture __FILE__, "file2.txt" - @filenames = [file1_name, file2_name] - - @codepoints = File.read(file1_name).codepoints - @codepoints.concat File.read(file2_name).codepoints - end - - it "is a public method" do - argf @filenames do - @argf.public_methods(false).should.include?(@method) - end - end - - it "does not require arguments" do - argf @filenames do - @argf.method(@method).arity.should == 0 - end - end - - it "returns self when passed a block" do - argf @filenames do - @argf.send(@method) {}.should.equal?(@argf) - end - end - - it "returns an Enumerator when passed no block" do - argf @filenames do - @argf.send(@method).should.instance_of?(Enumerator) - end - end - - it "yields each codepoint of all streams" do - argf @filenames do - @argf.send(@method).to_a.should == @codepoints - end - end - - describe "when no block is given" do - it "returns an Enumerator" do - argf @filenames do - @argf.send(@method).should.instance_of?(Enumerator) - end - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - argf @filenames do - @argf.send(@method).size.should == nil - end - end - end - end - end -end diff --git a/spec/ruby/core/argf/shared/each_line.rb b/spec/ruby/core/argf/shared/each_line.rb deleted file mode 100644 index 7e66e38803302c..00000000000000 --- a/spec/ruby/core/argf/shared/each_line.rb +++ /dev/null @@ -1,62 +0,0 @@ -describe :argf_each_line, shared: true do - before :each do - @file1_name = fixture __FILE__, "file1.txt" - @file2_name = fixture __FILE__, "file2.txt" - - @lines = File.readlines @file1_name - @lines += File.readlines @file2_name - end - - it "is a public method" do - argf [@file1_name, @file2_name] do - @argf.public_methods(false).should.include?(@method) - end - end - - it "requires multiple arguments" do - argf [@file1_name, @file2_name] do - @argf.method(@method).arity.should < 0 - end - end - - it "reads each line of files" do - argf [@file1_name, @file2_name] do - lines = [] - @argf.send(@method) { |b| lines << b } - lines.should == @lines - end - end - - it "returns self when passed a block" do - argf [@file1_name, @file2_name] do - @argf.send(@method) {}.should.equal?(@argf) - end - end - - describe "with a separator" do - it "yields each separated section of all streams" do - argf [@file1_name, @file2_name] do - @argf.send(@method, '.').to_a.should == - (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.')) - end - end - end - - describe "when no block is given" do - it "returns an Enumerator" do - argf [@file1_name, @file2_name] do - @argf.send(@method).should.instance_of?(Enumerator) - end - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - argf [@file1_name, @file2_name] do - @argf.send(@method).size.should == nil - end - end - end - end - end -end diff --git a/spec/ruby/core/argf/shared/eof.rb b/spec/ruby/core/argf/shared/eof.rb deleted file mode 100644 index 8b3897b95204f3..00000000000000 --- a/spec/ruby/core/argf/shared/eof.rb +++ /dev/null @@ -1,24 +0,0 @@ -describe :argf_eof, shared: true do - before :each do - @file1 = fixture __FILE__, "file1.txt" - @file2 = fixture __FILE__, "file2.txt" - end - - # NOTE: this test assumes that fixtures files have two lines each - it "returns true when reaching the end of a file" do - argf [@file1, @file2] do - result = [] - while @argf.gets - result << @argf.send(@method) - end - result.should == [false, true, false, true] - end - end - - it "raises IOError when called on a closed stream" do - argf [@file1] do - @argf.read - -> { @argf.send(@method) }.should.raise(IOError) - end - end -end diff --git a/spec/ruby/core/argf/shared/filename.rb b/spec/ruby/core/argf/shared/filename.rb deleted file mode 100644 index f47c673dc0dec7..00000000000000 --- a/spec/ruby/core/argf/shared/filename.rb +++ /dev/null @@ -1,28 +0,0 @@ -describe :argf_filename, shared: true do - before :each do - @file1 = fixture __FILE__, "file1.txt" - @file2 = fixture __FILE__, "file2.txt" - end - - # NOTE: this test assumes that fixtures files have two lines each - it "returns the current file name on each file" do - argf [@file1, @file2] do - result = [] - # returns first current file even when not yet open - result << @argf.send(@method) - result << @argf.send(@method) while @argf.gets - # returns last current file even when closed - result << @argf.send(@method) - - result.map! { |f| File.expand_path(f) } - result.should == [@file1, @file1, @file1, @file2, @file2, @file2] - end - end - - # NOTE: this test assumes that fixtures files have two lines each - it "sets the $FILENAME global variable with the current file name on each file" do - script = fixture __FILE__, "filename.rb" - out = ruby_exe(script, args: [@file1, @file2]) - out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n" - end -end diff --git a/spec/ruby/core/argf/shared/fileno.rb b/spec/ruby/core/argf/shared/fileno.rb deleted file mode 100644 index e605be46e31e4a..00000000000000 --- a/spec/ruby/core/argf/shared/fileno.rb +++ /dev/null @@ -1,24 +0,0 @@ -describe :argf_fileno, shared: true do - before :each do - @file1 = fixture __FILE__, "file1.txt" - @file2 = fixture __FILE__, "file2.txt" - end - - # NOTE: this test assumes that fixtures files have two lines each - it "returns the current file number on each file" do - argf [@file1, @file2] do - result = [] - # returns first current file even when not yet open - result << @argf.send(@method) while @argf.gets - # returns last current file even when closed - result.map { |d| d.class }.should == [Integer, Integer, Integer, Integer] - end - end - - it "raises an ArgumentError when called on a closed stream" do - argf [@file1] do - @argf.read - -> { @argf.send(@method) }.should.raise(ArgumentError) - end - end -end diff --git a/spec/ruby/core/argf/shared/pos.rb b/spec/ruby/core/argf/shared/pos.rb deleted file mode 100644 index f859d3a29d0ee9..00000000000000 --- a/spec/ruby/core/argf/shared/pos.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :argf_pos, shared: true do - before :each do - @file1 = fixture __FILE__, "file1.txt" - @file2 = fixture __FILE__, "file2.txt" - end - - it "gives the correct position for each read operation" do - argf [@file1, @file2] do - size1 = File.size(@file1) - size2 = File.size(@file2) - - @argf.read(2) - @argf.send(@method).should == 2 - @argf.read(size1-2) - @argf.send(@method).should == size1 - @argf.read(6) - @argf.send(@method).should == 6 - @argf.rewind - @argf.send(@method).should == 0 - @argf.read(size2) - @argf.send(@method).should == size2 - end - end - - it "raises an ArgumentError when called on a closed stream" do - argf [@file1] do - @argf.read - -> { @argf.send(@method) }.should.raise(ArgumentError) - end - end -end diff --git a/spec/ruby/core/argf/shared/readlines.rb b/spec/ruby/core/argf/shared/readlines.rb deleted file mode 100644 index 505fa94acbf069..00000000000000 --- a/spec/ruby/core/argf/shared/readlines.rb +++ /dev/null @@ -1,22 +0,0 @@ -describe :argf_readlines, shared: true do - before :each do - @file1 = fixture __FILE__, "file1.txt" - @file2 = fixture __FILE__, "file2.txt" - - @lines = File.readlines(@file1) - @lines += File.readlines(@file2) - end - - it "reads all lines of all files" do - argf [@file1, @file2] do - @argf.send(@method).should == @lines - end - end - - it "returns an empty Array when end of stream reached" do - argf [@file1, @file2] do - @argf.read - @argf.send(@method).should == [] - end - end -end diff --git a/spec/ruby/core/argf/tell_spec.rb b/spec/ruby/core/argf/tell_spec.rb index 16d9f2992076bb..bb28df74a252b1 100644 --- a/spec/ruby/core/argf/tell_spec.rb +++ b/spec/ruby/core/argf/tell_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/pos' describe "ARGF.tell" do - it_behaves_like :argf_pos, :tell + it "is an alias of ARGF.pos" do + ARGF.method(:tell).should == ARGF.method(:pos) + end end diff --git a/spec/ruby/core/argf/to_a_spec.rb b/spec/ruby/core/argf/to_a_spec.rb index b17a93db33d4b9..d95dc732ec9649 100644 --- a/spec/ruby/core/argf/to_a_spec.rb +++ b/spec/ruby/core/argf/to_a_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/readlines' describe "ARGF.to_a" do - it_behaves_like :argf_readlines, :to_a + it "is an alias of ARGF.readlines" do + ARGF.method(:to_a).should == ARGF.method(:readlines) + end end diff --git a/spec/ruby/core/argf/to_i_spec.rb b/spec/ruby/core/argf/to_i_spec.rb index 2183de6cd49a71..e8df378f4ecfd0 100644 --- a/spec/ruby/core/argf/to_i_spec.rb +++ b/spec/ruby/core/argf/to_i_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/fileno' describe "ARGF.to_i" do - it_behaves_like :argf_fileno, :to_i + it "is an alias of ARGF.fileno" do + ARGF.method(:to_i).should == ARGF.method(:fileno) + end end diff --git a/spec/ruby/core/array/append_spec.rb b/spec/ruby/core/array/append_spec.rb index de0e56b5133bcb..5480d9f65ebc60 100644 --- a/spec/ruby/core/array/append_spec.rb +++ b/spec/ruby/core/array/append_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/push' describe "Array#<<" do it "pushes the object onto the end of the array" do @@ -36,5 +35,7 @@ end describe "Array#append" do - it_behaves_like :array_push, :append + it "is an alias of Array#push" do + Array.instance_method(:append).should == Array.instance_method(:push) + end end diff --git a/spec/ruby/core/array/collect_spec.rb b/spec/ruby/core/array/collect_spec.rb index 0ad4c283b1562c..43a539f805668a 100644 --- a/spec/ruby/core/array/collect_spec.rb +++ b/spec/ruby/core/array/collect_spec.rb @@ -1,11 +1,143 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/collect' +require_relative '../enumerable/shared/enumeratorized' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#collect" do - it_behaves_like :array_collect, :collect + it "returns a copy of array with each element replaced by the value returned by block" do + a = ['a', 'b', 'c', 'd'] + b = a.collect { |i| i + '!' } + b.should == ["a!", "b!", "c!", "d!"] + b.should_not.equal? a + end + + it "does not return subclass instance" do + ArraySpecs::MyArray[1, 2, 3].collect { |x| x + 1 }.should.instance_of?(Array) + end + + it "does not change self" do + a = ['a', 'b', 'c', 'd'] + a.collect { |i| i + '!' } + a.should == ['a', 'b', 'c', 'd'] + end + + it "returns the evaluated value of block if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.collect {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + end + + it "returns an Enumerator when no block given" do + a = [1, 2, 3] + a.collect.should.instance_of?(Enumerator) + end + + it "raises an ArgumentError when no block and with arguments" do + a = [1, 2, 3] + -> { + a.collect(:foo) + }.should.raise(ArgumentError) + end + + before :each do + @object = [1, 2, 3, 4] + end + it_behaves_like :enumeratorized_with_origin_size, :collect + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect end describe "Array#collect!" do - it_behaves_like :array_collect_b, :collect! + it "replaces each element with the value returned by block" do + a = [7, 9, 3, 5] + a.collect! { |i| i - 1 }.should.equal?(a) + a.should == [6, 8, 2, 4] + end + + it "returns self" do + a = [1, 2, 3, 4, 5] + b = a.collect! {|i| i+1 } + a.should.equal? b + end + + it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.collect! {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + a.should == ['a!', 'b!', 'c', 'd'] + end + + it "returns an Enumerator when no block given, and the enumerator can modify the original array" do + a = [1, 2, 3] + enum = a.collect! + enum.should.instance_of?(Enumerator) + enum.each{|i| "#{i}!" } + a.should == ["1!", "2!", "3!"] + end + + describe "when frozen" do + it "raises a FrozenError" do + -> { ArraySpecs.frozen_array.collect! {} }.should.raise(FrozenError) + end + + it "raises a FrozenError when empty" do + -> { ArraySpecs.empty_frozen_array.collect! {} }.should.raise(FrozenError) + end + + it "raises a FrozenError when calling #each on the returned Enumerator" do + enumerator = ArraySpecs.frozen_array.collect! + -> { enumerator.each {|x| x } }.should.raise(FrozenError) + end + + it "raises a FrozenError when calling #each on the returned Enumerator when empty" do + enumerator = ArraySpecs.empty_frozen_array.collect! + -> { enumerator.each {|x| x } }.should.raise(FrozenError) + end + end + + it "does not truncate the array is the block raises an exception" do + a = [1, 2, 3] + begin + a.collect! { raise StandardError, 'Oops' } + rescue + end + + a.should == [1, 2, 3] + end + + it "only changes elements before error is raised, keeping the element which raised an error." do + a = [1, 2, 3, 4] + begin + a.collect! do |e| + case e + when 1 then -1 + when 2 then -2 + when 3 then raise StandardError, 'Oops' + else 0 + end + end + rescue StandardError + end + + a.should == [-1, -2, 3, 4] + end + + before :each do + @object = [1, 2, 3, 4] + end + it_behaves_like :enumeratorized_with_origin_size, :collect! + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect! end diff --git a/spec/ruby/core/array/element_reference_spec.rb b/spec/ruby/core/array/element_reference_spec.rb index eb41a9e199c041..d5f4b54961d23d 100644 --- a/spec/ruby/core/array/element_reference_spec.rb +++ b/spec/ruby/core/array/element_reference_spec.rb @@ -1,9 +1,862 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/slice' describe "Array#[]" do - it_behaves_like :array_slice, :[] + it "returns the element at index with [index]" do + [ "a", "b", "c", "d", "e" ][1].should == "b" + + a = [1, 2, 3, 4] + + a[0].should == 1 + a[1].should == 2 + a[2].should == 3 + a[3].should == 4 + a[4].should == nil + a[10].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the element at index from the end of the array with [-index]" do + [ "a", "b", "c", "d", "e" ][-2].should == "d" + + a = [1, 2, 3, 4] + + a[-1].should == 4 + a[-2].should == 3 + a[-3].should == 2 + a[-4].should == 1 + a[-5].should == nil + a[-10].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns count elements starting from index with [index, count]" do + [ "a", "b", "c", "d", "e" ][2, 3].should == ["c", "d", "e"] + + a = [1, 2, 3, 4] + + a[0, 0].should == [] + a[0, 1].should == [1] + a[0, 2].should == [1, 2] + a[0, 4].should == [1, 2, 3, 4] + a[0, 6].should == [1, 2, 3, 4] + a[0, -1].should == nil + a[0, -2].should == nil + a[0, -4].should == nil + + a[2, 0].should == [] + a[2, 1].should == [3] + a[2, 2].should == [3, 4] + a[2, 4].should == [3, 4] + a[2, -1].should == nil + + a[4, 0].should == [] + a[4, 2].should == [] + a[4, -1].should == nil + + a[5, 0].should == nil + a[5, 2].should == nil + a[5, -1].should == nil + + a[6, 0].should == nil + a[6, 2].should == nil + a[6, -1].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns count elements starting at index from the end of array with [-index, count]" do + [ "a", "b", "c", "d", "e" ][-2, 2].should == ["d", "e"] + + a = [1, 2, 3, 4] + + a[-1, 0].should == [] + a[-1, 1].should == [4] + a[-1, 2].should == [4] + a[-1, -1].should == nil + + a[-2, 0].should == [] + a[-2, 1].should == [3] + a[-2, 2].should == [3, 4] + a[-2, 4].should == [3, 4] + a[-2, -1].should == nil + + a[-4, 0].should == [] + a[-4, 1].should == [1] + a[-4, 2].should == [1, 2] + a[-4, 4].should == [1, 2, 3, 4] + a[-4, 6].should == [1, 2, 3, 4] + a[-4, -1].should == nil + + a[-5, 0].should == nil + a[-5, 1].should == nil + a[-5, 10].should == nil + a[-5, -1].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the first count elements with [0, count]" do + [ "a", "b", "c", "d", "e" ][0, 3].should == ["a", "b", "c"] + end + + it "returns the subarray which is independent to self with [index,count]" do + a = [1, 2, 3] + sub = a[1, 2] + sub.replace([:a, :b]) + a.should == [1, 2, 3] + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.stub!(:to_int).and_return(2) + + a = [1, 2, 3, 4] + a[obj].should == 3 + a[obj, 1].should == [3] + a[obj, obj].should == [3, 4] + a[0, obj].should == [1, 2] + end + + it "raises TypeError if to_int returns non-integer" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + a = [1, 2, 3, 4, 5] + + def from.to_int() 'cat' end + def to.to_int() -2 end + + -> { a[from..to] }.should.raise(TypeError) + + def from.to_int() 1 end + def to.to_int() 'cat' end + + -> { a[from..to] }.should.raise(TypeError) + end + + it "returns the elements specified by Range indexes with [m..n]" do + [ "a", "b", "c", "d", "e" ][1..3].should == ["b", "c", "d"] + [ "a", "b", "c", "d", "e" ][4..-1].should == ['e'] + [ "a", "b", "c", "d", "e" ][3..3].should == ['d'] + [ "a", "b", "c", "d", "e" ][3..-2].should == ['d'] + ['a'][0..-1].should == ['a'] + + a = [1, 2, 3, 4] + + a[0..-10].should == [] + a[0..0].should == [1] + a[0..1].should == [1, 2] + a[0..2].should == [1, 2, 3] + a[0..3].should == [1, 2, 3, 4] + a[0..4].should == [1, 2, 3, 4] + a[0..10].should == [1, 2, 3, 4] + + a[2..-10].should == [] + a[2..0].should == [] + a[2..2].should == [3] + a[2..3].should == [3, 4] + a[2..4].should == [3, 4] + + a[3..0].should == [] + a[3..3].should == [4] + a[3..4].should == [4] + + a[4..0].should == [] + a[4..4].should == [] + a[4..5].should == [] + + a[5..0].should == nil + a[5..5].should == nil + a[5..6].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns elements specified by Range indexes except the element at index n with [m...n]" do + [ "a", "b", "c", "d", "e" ][1...3].should == ["b", "c"] + + a = [1, 2, 3, 4] + + a[0...-10].should == [] + a[0...0].should == [] + a[0...1].should == [1] + a[0...2].should == [1, 2] + a[0...3].should == [1, 2, 3] + a[0...4].should == [1, 2, 3, 4] + a[0...10].should == [1, 2, 3, 4] + + a[2...-10].should == [] + a[2...0].should == [] + a[2...2].should == [] + a[2...3].should == [3] + a[2...4].should == [3, 4] + + a[3...0].should == [] + a[3...3].should == [] + a[3...4].should == [4] + + a[4...0].should == [] + a[4...4].should == [] + a[4...5].should == [] + + a[5...0].should == nil + a[5...5].should == nil + a[5...6].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns elements that exist if range start is in the array but range end is not with [m..n]" do + [ "a", "b", "c", "d", "e" ][4..7].should == ["e"] + end + + it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do + a = [1, 2, 3, 4] + + a[-1..-1].should == [4] + a[-1...-1].should == [] + a[-1..3].should == [4] + a[-1...3].should == [] + a[-1..4].should == [4] + a[-1...4].should == [4] + a[-1..10].should == [4] + a[-1...10].should == [4] + a[-1..0].should == [] + a[-1..-4].should == [] + a[-1...-4].should == [] + a[-1..-6].should == [] + a[-1...-6].should == [] + + a[-2..-2].should == [3] + a[-2...-2].should == [] + a[-2..-1].should == [3, 4] + a[-2...-1].should == [3] + a[-2..10].should == [3, 4] + a[-2...10].should == [3, 4] + + a[-4..-4].should == [1] + a[-4..-2].should == [1, 2, 3] + a[-4...-2].should == [1, 2] + a[-4..-1].should == [1, 2, 3, 4] + a[-4...-1].should == [1, 2, 3] + a[-4..3].should == [1, 2, 3, 4] + a[-4...3].should == [1, 2, 3] + a[-4..4].should == [1, 2, 3, 4] + a[-4...4].should == [1, 2, 3, 4] + a[-4...4].should == [1, 2, 3, 4] + a[-4..0].should == [1] + a[-4...0].should == [] + a[-4..1].should == [1, 2] + a[-4...1].should == [1] + + a[-5..-5].should == nil + a[-5...-5].should == nil + a[-5..-4].should == nil + a[-5..-1].should == nil + a[-5..10].should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the subarray which is independent to self with [m..n]" do + a = [1, 2, 3] + sub = a[1..2] + sub.replace([:a, :b]) + a.should == [1, 2, 3] + end + + it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + a = [1, 2, 3, 4] + + a[from..to].should == [2, 3] + a[from...to].should == [2] + a[1..0].should == [] + a[1...0].should == [] + + -> { a["a" .. "b"] }.should.raise(TypeError) + -> { a["a" ... "b"] }.should.raise(TypeError) + -> { a[from .. "b"] }.should.raise(TypeError) + -> { a[from ... "b"] }.should.raise(TypeError) + end + + it "returns the same elements as [m..n] and [m...n] with Range subclasses" do + a = [1, 2, 3, 4] + range_incl = ArraySpecs::MyRange.new(1, 2) + range_excl = ArraySpecs::MyRange.new(-3, -1, true) + + a[range_incl].should == [2, 3] + a[range_excl].should == [2, 3] + end + + it "returns nil for a requested index not in the array with [index]" do + [ "a", "b", "c", "d", "e" ][5].should == nil + end + + it "returns [] if the index is valid but length is zero with [index, length]" do + [ "a", "b", "c", "d", "e" ][0, 0].should == [] + [ "a", "b", "c", "d", "e" ][2, 0].should == [] + end + + it "returns nil if length is zero but index is invalid with [index, length]" do + [ "a", "b", "c", "d", "e" ][100, 0].should == nil + [ "a", "b", "c", "d", "e" ][-50, 0].should == nil + end + + # This is by design. It is in the official documentation. + it "returns [] if index == array.size with [index, length]" do + %w|a b c d e|[5, 2].should == [] + end + + it "returns nil if index > array.size with [index, length]" do + %w|a b c d e|[6, 2].should == nil + end + + it "returns nil if length is negative with [index, length]" do + %w|a b c d e|[3, -1].should == nil + %w|a b c d e|[2, -2].should == nil + %w|a b c d e|[1, -100].should == nil + end + + it "returns nil if no requested index is in the array with [m..n]" do + [ "a", "b", "c", "d", "e" ][6..10].should == nil + end + + it "returns nil if range start is not in the array with [m..n]" do + [ "a", "b", "c", "d", "e" ][-10..2].should == nil + [ "a", "b", "c", "d", "e" ][10..12].should == nil + end + + it "returns an empty array when m == n with [m...n]" do + [1, 2, 3, 4, 5][1...1].should == [] + end + + it "returns an empty array with [0...0]" do + [1, 2, 3, 4, 5][0...0].should == [] + end + + it "returns a subarray where m, n negatives and m < n with [m..n]" do + [ "a", "b", "c", "d", "e" ][-3..-2].should == ["c", "d"] + end + + it "returns an array containing the first element with [0..0]" do + [1, 2, 3, 4, 5][0..0].should == [1] + end + + it "returns the entire array with [0..-1]" do + [1, 2, 3, 4, 5][0..-1].should == [1, 2, 3, 4, 5] + end + + it "returns all but the last element with [0...-1]" do + [1, 2, 3, 4, 5][0...-1].should == [1, 2, 3, 4] + end + + it "returns [3] for [2..-1] out of [1, 2, 3]" do + [1,2,3][2..-1].should == [3] + end + + it "returns an empty array when m > n and m, n are positive with [m..n]" do + [1, 2, 3, 4, 5][3..2].should == [] + end + + it "returns an empty array when m > n and m, n are negative with [m..n]" do + [1, 2, 3, 4, 5][-2..-3].should == [] + end + + it "does not expand array when the indices are outside of the array bounds" do + a = [1, 2] + a[4].should == nil + a.should == [1, 2] + a[4, 0].should == nil + a.should == [1, 2] + a[6, 1].should == nil + a.should == [1, 2] + a[8...8].should == nil + a.should == [1, 2] + a[10..10].should == nil + a.should == [1, 2] + end + + describe "with a subclass of Array" do + before :each do + ScratchPad.clear + + @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] + end + + it "returns a Array instance with [n, m]" do + @array[0, 2].should.instance_of?(Array) + end + + it "returns a Array instance with [-n, m]" do + @array[-3, 2].should.instance_of?(Array) + end + + it "returns a Array instance with [n..m]" do + @array[1..3].should.instance_of?(Array) + end + + it "returns a Array instance with [n...m]" do + @array[1...3].should.instance_of?(Array) + end + + it "returns a Array instance with [-n..-m]" do + @array[-3..-1].should.instance_of?(Array) + end + + it "returns a Array instance with [-n...-m]" do + @array[-3...-1].should.instance_of?(Array) + end + + it "returns an empty array when m == n with [m...n]" do + @array[1...1].should == [] + ScratchPad.recorded.should == nil + end + + it "returns an empty array with [0...0]" do + @array[0...0].should == [] + ScratchPad.recorded.should == nil + end + + it "returns an empty array when m > n and m, n are positive with [m..n]" do + @array[3..2].should == [] + ScratchPad.recorded.should == nil + end + + it "returns an empty array when m > n and m, n are negative with [m..n]" do + @array[-2..-3].should == [] + ScratchPad.recorded.should == nil + end + + it "returns [] if index == array.size with [index, length]" do + @array[5, 2].should == [] + ScratchPad.recorded.should == nil + end + + it "returns [] if the index is valid but length is zero with [index, length]" do + @array[0, 0].should == [] + @array[2, 0].should == [] + ScratchPad.recorded.should == nil + end + + it "does not call #initialize on the subclass instance" do + @array[0, 3].should == [1, 2, 3] + ScratchPad.recorded.should == nil + end + end + + it "raises a RangeError when the start index is out of range of Fixnum" do + array = [1, 2, 3, 4, 5, 6] + obj = mock('large value') + obj.should_receive(:to_int).and_return(bignum_value) + -> { array[obj] }.should.raise(RangeError) + + obj = 8e19 + -> { array[obj] }.should.raise(RangeError) + + # boundary value when longs are 64 bits + -> { array[2.0**63] }.should.raise(RangeError) + + # just under the boundary value when longs are 64 bits + array[max_long.to_f.prev_float].should == nil + end + + it "raises a RangeError when the length is out of range of Fixnum" do + array = [1, 2, 3, 4, 5, 6] + obj = mock('large value') + obj.should_receive(:to_int).and_return(bignum_value) + -> { array[1, obj] }.should.raise(RangeError) + + obj = 8e19 + -> { array[1, obj] }.should.raise(RangeError) + end + + it "raises a type error if a range is passed with a length" do + ->{ [1, 2, 3][1..2, 1] }.should.raise(TypeError) + end + + it "raises a RangeError if passed a range with a bound that is too large" do + array = [1, 2, 3, 4, 5, 6] + -> { array[bignum_value..(bignum_value + 1)] }.should.raise(RangeError) + -> { array[0..bignum_value] }.should.raise(RangeError) + end + + it "can accept endless ranges" do + a = [0, 1, 2, 3, 4, 5] + a[eval("(2..)")].should == [2, 3, 4, 5] + a[eval("(2...)")].should == [2, 3, 4, 5] + a[eval("(-2..)")].should == [4, 5] + a[eval("(-2...)")].should == [4, 5] + a[eval("(9..)")].should == nil + a[eval("(9...)")].should == nil + a[eval("(-9..)")].should == nil + a[eval("(-9...)")].should == nil + end + + describe "can be sliced with Enumerator::ArithmeticSequence" do + before :each do + @array = [0, 1, 2, 3, 4, 5] + end + + it "has endless range and positive steps" do + @array[eval("(0..).step(1)")].should == [0, 1, 2, 3, 4, 5] + @array[eval("(0..).step(2)")].should == [0, 2, 4] + @array[eval("(0..).step(10)")].should == [0] + + @array[eval("(2..).step(1)")].should == [2, 3, 4, 5] + @array[eval("(2..).step(2)")].should == [2, 4] + @array[eval("(2..).step(10)")].should == [2] + + @array[eval("(-3..).step(1)")].should == [3, 4, 5] + @array[eval("(-3..).step(2)")].should == [3, 5] + @array[eval("(-3..).step(10)")].should == [3] + end + + it "has beginless range and positive steps" do + # end with zero index + @array[(..0).step(1)].should == [0] + @array[(...0).step(1)].should == [] + + @array[(..0).step(2)].should == [0] + @array[(...0).step(2)].should == [] + + @array[(..0).step(10)].should == [0] + @array[(...0).step(10)].should == [] + + # end with positive index + @array[(..3).step(1)].should == [0, 1, 2, 3] + @array[(...3).step(1)].should == [0, 1, 2] + + @array[(..3).step(2)].should == [0, 2] + @array[(...3).step(2)].should == [0, 2] + + @array[(..3).step(10)].should == [0] + @array[(...3).step(10)].should == [0] + + # end with negative index + @array[(..-2).step(1)].should == [0, 1, 2, 3, 4,] + @array[(...-2).step(1)].should == [0, 1, 2, 3] + + @array[(..-2).step(2)].should == [0, 2, 4] + @array[(...-2).step(2)].should == [0, 2] + + @array[(..-2).step(10)].should == [0] + @array[(...-2).step(10)].should == [0] + end + + it "has endless range and negative steps" do + @array[eval("(0..).step(-1)")].should == [0] + @array[eval("(0..).step(-2)")].should == [0] + @array[eval("(0..).step(-10)")].should == [0] + + @array[eval("(2..).step(-1)")].should == [2, 1, 0] + @array[eval("(2..).step(-2)")].should == [2, 0] + + @array[eval("(-3..).step(-1)")].should == [3, 2, 1, 0] + @array[eval("(-3..).step(-2)")].should == [3, 1] + end + + it "has closed range and positive steps" do + # start and end with 0 + @array[eval("(0..0).step(1)")].should == [0] + @array[eval("(0...0).step(1)")].should == [] + + @array[eval("(0..0).step(2)")].should == [0] + @array[eval("(0...0).step(2)")].should == [] + + @array[eval("(0..0).step(10)")].should == [0] + @array[eval("(0...0).step(10)")].should == [] + + # start and end with positive index + @array[eval("(1..3).step(1)")].should == [1, 2, 3] + @array[eval("(1...3).step(1)")].should == [1, 2] + + @array[eval("(1..3).step(2)")].should == [1, 3] + @array[eval("(1...3).step(2)")].should == [1] + + @array[eval("(1..3).step(10)")].should == [1] + @array[eval("(1...3).step(10)")].should == [1] + + # start with positive index, end with negative index + @array[eval("(1..-2).step(1)")].should == [1, 2, 3, 4] + @array[eval("(1...-2).step(1)")].should == [1, 2, 3] + + @array[eval("(1..-2).step(2)")].should == [1, 3] + @array[eval("(1...-2).step(2)")].should == [1, 3] + + @array[eval("(1..-2).step(10)")].should == [1] + @array[eval("(1...-2).step(10)")].should == [1] + + # start with negative index, end with positive index + @array[eval("(-4..4).step(1)")].should == [2, 3, 4] + @array[eval("(-4...4).step(1)")].should == [2, 3] + + @array[eval("(-4..4).step(2)")].should == [2, 4] + @array[eval("(-4...4).step(2)")].should == [2] + + @array[eval("(-4..4).step(10)")].should == [2] + @array[eval("(-4...4).step(10)")].should == [2] + + # start with negative index, end with negative index + @array[eval("(-4..-2).step(1)")].should == [2, 3, 4] + @array[eval("(-4...-2).step(1)")].should == [2, 3] + + @array[eval("(-4..-2).step(2)")].should == [2, 4] + @array[eval("(-4...-2).step(2)")].should == [2] + + @array[eval("(-4..-2).step(10)")].should == [2] + @array[eval("(-4...-2).step(10)")].should == [2] + end + + it "has closed range and negative steps" do + # start and end with 0 + @array[eval("(0..0).step(-1)")].should == [0] + @array[eval("(0...0).step(-1)")].should == [] + + @array[eval("(0..0).step(-2)")].should == [0] + @array[eval("(0...0).step(-2)")].should == [] + + @array[eval("(0..0).step(-10)")].should == [0] + @array[eval("(0...0).step(-10)")].should == [] + + # start and end with positive index + @array[eval("(1..3).step(-1)")].should == [] + @array[eval("(1...3).step(-1)")].should == [] + + @array[eval("(1..3).step(-2)")].should == [] + @array[eval("(1...3).step(-2)")].should == [] + + @array[eval("(1..3).step(-10)")].should == [] + @array[eval("(1...3).step(-10)")].should == [] + + # start with positive index, end with negative index + @array[eval("(1..-2).step(-1)")].should == [] + @array[eval("(1...-2).step(-1)")].should == [] + + @array[eval("(1..-2).step(-2)")].should == [] + @array[eval("(1...-2).step(-2)")].should == [] + + @array[eval("(1..-2).step(-10)")].should == [] + @array[eval("(1...-2).step(-10)")].should == [] + + # start with negative index, end with positive index + @array[eval("(-4..4).step(-1)")].should == [] + @array[eval("(-4...4).step(-1)")].should == [] + + @array[eval("(-4..4).step(-2)")].should == [] + @array[eval("(-4...4).step(-2)")].should == [] + + @array[eval("(-4..4).step(-10)")].should == [] + @array[eval("(-4...4).step(-10)")].should == [] + + # start with negative index, end with negative index + @array[eval("(-4..-2).step(-1)")].should == [] + @array[eval("(-4...-2).step(-1)")].should == [] + + @array[eval("(-4..-2).step(-2)")].should == [] + @array[eval("(-4...-2).step(-2)")].should == [] + + @array[eval("(-4..-2).step(-10)")].should == [] + @array[eval("(-4...-2).step(-10)")].should == [] + end + + it "has inverted closed range and positive steps" do + # start and end with positive index + @array[eval("(3..1).step(1)")].should == [] + @array[eval("(3...1).step(1)")].should == [] + + @array[eval("(3..1).step(2)")].should == [] + @array[eval("(3...1).step(2)")].should == [] + + @array[eval("(3..1).step(10)")].should == [] + @array[eval("(3...1).step(10)")].should == [] + + # start with negative index, end with positive index + @array[eval("(-2..1).step(1)")].should == [] + @array[eval("(-2...1).step(1)")].should == [] + + @array[eval("(-2..1).step(2)")].should == [] + @array[eval("(-2...1).step(2)")].should == [] + + @array[eval("(-2..1).step(10)")].should == [] + @array[eval("(-2...1).step(10)")].should == [] + + # start with positive index, end with negative index + @array[eval("(4..-4).step(1)")].should == [] + @array[eval("(4...-4).step(1)")].should == [] + + @array[eval("(4..-4).step(2)")].should == [] + @array[eval("(4...-4).step(2)")].should == [] + + @array[eval("(4..-4).step(10)")].should == [] + @array[eval("(4...-4).step(10)")].should == [] + + # start with negative index, end with negative index + @array[eval("(-2..-4).step(1)")].should == [] + @array[eval("(-2...-4).step(1)")].should == [] + + @array[eval("(-2..-4).step(2)")].should == [] + @array[eval("(-2...-4).step(2)")].should == [] + + @array[eval("(-2..-4).step(10)")].should == [] + @array[eval("(-2...-4).step(10)")].should == [] + end + + it "has range with bounds outside of array" do + # end is equal to array's length + @array[(0..6).step(1)].should == [0, 1, 2, 3, 4, 5] + -> { @array[(0..6).step(2)] }.should.raise(RangeError) + + # end is greater than length with positive steps + @array[(1..6).step(2)].should == [1, 3, 5] + @array[(2..7).step(2)].should == [2, 4] + -> { @array[(2..8).step(2)] }.should.raise(RangeError) + + # begin is greater than length with negative steps + @array[(6..1).step(-2)].should == [5, 3, 1] + @array[(7..2).step(-2)].should == [5, 3] + -> { @array[(8..2).step(-2)] }.should.raise(RangeError) + end + + it "has endless range with start outside of array's bounds" do + @array[eval("(6..).step(1)")].should == [] + @array[eval("(7..).step(1)")].should == nil + + @array[eval("(6..).step(2)")].should == [] + -> { @array[eval("(7..).step(2)")] }.should.raise(RangeError) + end + end + + it "can accept beginless ranges" do + a = [0, 1, 2, 3, 4, 5] + a[(..3)].should == [0, 1, 2, 3] + a[(...3)].should == [0, 1, 2] + a[(..-3)].should == [0, 1, 2, 3] + a[(...-3)].should == [0, 1, 2] + a[(..0)].should == [0] + a[(...0)].should == [] + a[(..9)].should == [0, 1, 2, 3, 4, 5] + a[(...9)].should == [0, 1, 2, 3, 4, 5] + a[(..-9)].should == [] + a[(...-9)].should == [] + end + + describe "can be sliced with Enumerator::ArithmeticSequence" do + it "with infinite/inverted ranges and negative steps" do + array = [0, 1, 2, 3, 4, 5] + array[(2..).step(-1)].should == [2, 1, 0] + array[(2..).step(-2)].should == [2, 0] + array[(2..).step(-3)].should == [2] + array[(2..).step(-4)].should == [2] + + array[(-3..).step(-1)].should == [3, 2, 1, 0] + array[(-3..).step(-2)].should == [3, 1] + array[(-3..).step(-3)].should == [3, 0] + array[(-3..).step(-4)].should == [3] + array[(-3..).step(-5)].should == [3] + + array[(..0).step(-1)].should == [5, 4, 3, 2, 1, 0] + array[(..0).step(-2)].should == [5, 3, 1] + array[(..0).step(-3)].should == [5, 2] + array[(..0).step(-4)].should == [5, 1] + array[(..0).step(-5)].should == [5, 0] + array[(..0).step(-6)].should == [5] + array[(..0).step(-7)].should == [5] + + array[(...0).step(-1)].should == [5, 4, 3, 2, 1] + array[(...0).step(-2)].should == [5, 3, 1] + array[(...0).step(-3)].should == [5, 2] + array[(...0).step(-4)].should == [5, 1] + array[(...0).step(-5)].should == [5] + array[(...0).step(-6)].should == [5] + + array[(...1).step(-1)].should == [5, 4, 3, 2] + array[(...1).step(-2)].should == [5, 3] + array[(...1).step(-3)].should == [5, 2] + array[(...1).step(-4)].should == [5] + array[(...1).step(-5)].should == [5] + + array[(..-5).step(-1)].should == [5, 4, 3, 2, 1] + array[(..-5).step(-2)].should == [5, 3, 1] + array[(..-5).step(-3)].should == [5, 2] + array[(..-5).step(-4)].should == [5, 1] + array[(..-5).step(-5)].should == [5] + array[(..-5).step(-6)].should == [5] + + array[(...-5).step(-1)].should == [5, 4, 3, 2] + array[(...-5).step(-2)].should == [5, 3] + array[(...-5).step(-3)].should == [5, 2] + array[(...-5).step(-4)].should == [5] + array[(...-5).step(-5)].should == [5] + + array[(4..1).step(-1)].should == [4, 3, 2, 1] + array[(4..1).step(-2)].should == [4, 2] + array[(4..1).step(-3)].should == [4, 1] + array[(4..1).step(-4)].should == [4] + array[(4..1).step(-5)].should == [4] + + array[(4...1).step(-1)].should == [4, 3, 2] + array[(4...1).step(-2)].should == [4, 2] + array[(4...1).step(-3)].should == [4] + array[(4...1).step(-4)].should == [4] + + array[(-2..1).step(-1)].should == [4, 3, 2, 1] + array[(-2..1).step(-2)].should == [4, 2] + array[(-2..1).step(-3)].should == [4, 1] + array[(-2..1).step(-4)].should == [4] + array[(-2..1).step(-5)].should == [4] + + array[(-2...1).step(-1)].should == [4, 3, 2] + array[(-2...1).step(-2)].should == [4, 2] + array[(-2...1).step(-3)].should == [4] + array[(-2...1).step(-4)].should == [4] + + array[(4..-5).step(-1)].should == [4, 3, 2, 1] + array[(4..-5).step(-2)].should == [4, 2] + array[(4..-5).step(-3)].should == [4, 1] + array[(4..-5).step(-4)].should == [4] + array[(4..-5).step(-5)].should == [4] + + array[(4...-5).step(-1)].should == [4, 3, 2] + array[(4...-5).step(-2)].should == [4, 2] + array[(4...-5).step(-3)].should == [4] + array[(4...-5).step(-4)].should == [4] + + array[(-2..-5).step(-1)].should == [4, 3, 2, 1] + array[(-2..-5).step(-2)].should == [4, 2] + array[(-2..-5).step(-3)].should == [4, 1] + array[(-2..-5).step(-4)].should == [4] + array[(-2..-5).step(-5)].should == [4] + + array[(-2...-5).step(-1)].should == [4, 3, 2] + array[(-2...-5).step(-2)].should == [4, 2] + array[(-2...-5).step(-3)].should == [4] + array[(-2...-5).step(-4)].should == [4] + end + end + + it "can accept nil...nil ranges" do + a = [0, 1, 2, 3, 4, 5] + a[eval("(nil...nil)")].should == a + a[(...nil)].should == a + a[eval("(nil..)")].should == a + end end describe "Array.[]" do diff --git a/spec/ruby/core/array/filter_spec.rb b/spec/ruby/core/array/filter_spec.rb index 7807c3886d4929..6ebda61069c421 100644 --- a/spec/ruby/core/array/filter_spec.rb +++ b/spec/ruby/core/array/filter_spec.rb @@ -1,14 +1,13 @@ require_relative '../../spec_helper' -require_relative 'shared/select' describe "Array#filter" do - it_behaves_like :array_select, :filter + it "is an alias of Array#select" do + Array.instance_method(:filter).should == Array.instance_method(:select) + end end describe "Array#filter!" do - it "returns nil if no changes were made in the array" do - [1, 2, 3].filter! { true }.should == nil + it "is an alias of Array#select!" do + Array.instance_method(:filter!).should == Array.instance_method(:select!) end - - it_behaves_like :keep_if, :filter! end diff --git a/spec/ruby/core/array/find_index_spec.rb b/spec/ruby/core/array/find_index_spec.rb index 759472024ad03a..17ff6c04a7515f 100644 --- a/spec/ruby/core/array/find_index_spec.rb +++ b/spec/ruby/core/array/find_index_spec.rb @@ -1,6 +1,42 @@ require_relative '../../spec_helper' -require_relative 'shared/index' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#find_index" do - it_behaves_like :array_index, :find_index + it "returns the index of the first element == to object" do + x = mock('3') + def x.==(obj) 3 == obj; end + + [2, x, 3, 1, 3, 1].find_index(3).should == 1 + [2, 3.0, 3, x, 1, 3, 1].find_index(x).should == 1 + end + + it "returns 0 if first element == to object" do + [2, 1, 3, 2, 5].find_index(2).should == 0 + end + + it "returns size-1 if only last element == to object" do + [2, 1, 3, 1, 5].find_index(5).should == 4 + end + + it "returns nil if no element == to object" do + [2, 1, 1, 1, 1].find_index(3).should == nil + end + + it "accepts a block instead of an argument" do + [4, 2, 1, 5, 1, 3].find_index {|x| x < 2}.should == 2 + end + + it "ignores the block if there is an argument" do + -> { + [4, 2, 1, 5, 1, 3].find_index(5) {|x| x < 2}.should == 3 + }.should complain(/given block not used/) + end + + describe "given no argument and no block" do + it "produces an Enumerator" do + [].find_index.should.instance_of?(Enumerator) + end + end + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :find_index end diff --git a/spec/ruby/core/array/index_spec.rb b/spec/ruby/core/array/index_spec.rb index 3acb7d0ef30471..b66cb6eb5340ca 100644 --- a/spec/ruby/core/array/index_spec.rb +++ b/spec/ruby/core/array/index_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/index' describe "Array#index" do - it_behaves_like :array_index, :index + it "is an alias of Array#find_index" do + Array.instance_method(:index).should == Array.instance_method(:find_index) + end end diff --git a/spec/ruby/core/array/inspect_spec.rb b/spec/ruby/core/array/inspect_spec.rb index 0832224f5ace9d..e5dca828895e0d 100644 --- a/spec/ruby/core/array/inspect_spec.rb +++ b/spec/ruby/core/array/inspect_spec.rb @@ -1,7 +1,108 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/inspect' describe "Array#inspect" do - it_behaves_like :array_inspect, :inspect + it "returns a string" do + [1, 2, 3].inspect.should.instance_of?(String) + end + + it "returns '[]' for an empty Array" do + [].inspect.should == "[]" + end + + it "calls inspect on its elements and joins the results with commas" do + items = Array.new(3) do |i| + obj = mock(i.to_s) + obj.should_receive(:inspect).and_return(i.to_s) + obj + end + items.inspect.should == "[0, 1, 2]" + end + + it "does not call #to_s on a String returned from #inspect" do + str = +"abc" + str.should_not_receive(:to_s) + + [str].inspect.should == '["abc"]' + end + + it "calls #to_s on the object returned from #inspect if the Object isn't a String" do + obj = mock("Array#inspect/to_s calls #to_s") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_return("abc") + + [obj].inspect.should == "[abc]" + end + + it "does not call #to_str on the object returned from #inspect when it is not a String" do + obj = mock("Array#inspect/to_s does not call #to_str") + obj.should_receive(:inspect).and_return(obj) + obj.should_not_receive(:to_str) + + [obj].inspect.should =~ /^\[#\]$/ + end + + it "does not call #to_str on the object returned from #to_s when it is not a String" do + obj = mock("Array#inspect/to_s does not call #to_str on #to_s result") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_return(obj) + obj.should_not_receive(:to_str) + + [obj].inspect.should =~ /^\[#\]$/ + end + + it "does not swallow exceptions raised by #to_s" do + obj = mock("Array#inspect/to_s does not swallow #to_s exceptions") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_raise(Exception) + + -> { [obj].inspect }.should.raise(Exception) + end + + it "represents a recursive element with '[...]'" do + ArraySpecs.recursive_array.inspect.should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]" + ArraySpecs.head_recursive_array.inspect.should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]" + ArraySpecs.empty_recursive_array.inspect.should == "[[...]]" + end + + describe "with encoding" do + before :each do + @default_external_encoding = Encoding.default_external + end + + after :each do + Encoding.default_external = @default_external_encoding + end + + it "returns a US-ASCII string for an empty Array" do + [].inspect.encoding.should == Encoding::US_ASCII + end + + it "use the default external encoding if it is ascii compatible" do + Encoding.default_external = Encoding.find('UTF-8') + + utf8 = "utf8".encode("UTF-8") + jp = "jp".encode("EUC-JP") + array = [jp, utf8] + + array.inspect.encoding.name.should == "UTF-8" + end + + it "use US-ASCII encoding if the default external encoding is not ascii compatible" do + Encoding.default_external = Encoding.find('UTF-32') + + utf8 = "utf8".encode("UTF-8") + jp = "jp".encode("EUC-JP") + array = [jp, utf8] + + array.inspect.encoding.name.should == "US-ASCII" + end + + it "does not raise if inspected result is not default external encoding" do + utf_16be = mock(+"utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) + + [utf_16be].inspect.should == '["utf_16be \u3042"]' + end + end end diff --git a/spec/ruby/core/array/join_spec.rb b/spec/ruby/core/array/join_spec.rb index 811db036a844c5..3b4946a99fd6e6 100644 --- a/spec/ruby/core/array/join_spec.rb +++ b/spec/ruby/core/array/join_spec.rb @@ -4,7 +4,6 @@ describe "Array#join" do it_behaves_like :array_join_with_string_separator, :join - it_behaves_like :array_join_with_default_separator, :join it "does not separate elements when the passed separator is nil" do [1, 2, 3].join(nil).should == '123' @@ -32,6 +31,103 @@ end end +describe "Array#join with default separator" do + before :each do + @separator = $, + end + + after :each do + $, = @separator + end + + it "returns an empty string if the Array is empty" do + [].join.should == '' + end + + it "returns a US-ASCII string for an empty Array" do + [].join.encoding.should == Encoding::US_ASCII + end + + it "returns a string formed by concatenating each String element separated by $," do + suppress_warning { + $, = " | " + ["1", "2", "3"].join.should == "1 | 2 | 3" + } + end + + it "attempts coercion via #to_str first" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return("foo") + [obj].join.should == "foo" + end + + it "attempts coercion via #to_ary second" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return(nil) + obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"]) + [obj].join.should == "foo" + end + + it "attempts coercion via #to_s third" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return(nil) + obj.should_receive(:to_ary).any_number_of_times.and_return(nil) + obj.should_receive(:to_s).any_number_of_times.and_return("foo") + [obj].join.should == "foo" + end + + it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do + obj = mock('o') + class << obj; undef :to_s; end + -> { [1, obj].join }.should.raise(NoMethodError) + end + + it "raises an ArgumentError when the Array is recursive" do + -> { ArraySpecs.recursive_array.join }.should.raise(ArgumentError) + -> { ArraySpecs.head_recursive_array.join }.should.raise(ArgumentError) + -> { ArraySpecs.empty_recursive_array.join }.should.raise(ArgumentError) + end + + it "uses the first encoding when other strings are compatible" do + ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings + ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings + ary3 = ArraySpecs.array_with_utf8_and_7bit_binary_strings + ary4 = ArraySpecs.array_with_usascii_and_7bit_binary_strings + + ary1.join.encoding.should == Encoding::UTF_8 + ary2.join.encoding.should == Encoding::US_ASCII + ary3.join.encoding.should == Encoding::UTF_8 + ary4.join.encoding.should == Encoding::US_ASCII + end + + it "uses the widest common encoding when other strings are incompatible" do + ary1 = ArraySpecs.array_with_utf8_and_usascii_strings + ary2 = ArraySpecs.array_with_usascii_and_utf8_strings + + ary1.join.encoding.should == Encoding::UTF_8 + ary2.join.encoding.should == Encoding::UTF_8 + end + + it "fails for arrays with incompatibly-encoded strings" do + ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings + + -> { ary_utf8_bad_binary.join }.should.raise(EncodingError) + end + + context "when $, is not nil" do + before do + suppress_warning do + $, = '*' + end + end + + it "warns" do + -> { [].join }.should complain(/warning: \$, is set to non-nil value/) + -> { [].join(nil) }.should complain(/warning: \$, is set to non-nil value/) + end + end +end + describe "Array#join with $," do before :each do @before_separator = $, diff --git a/spec/ruby/core/array/length_spec.rb b/spec/ruby/core/array/length_spec.rb index a90c00130088b4..74b2eb3a08cc9b 100644 --- a/spec/ruby/core/array/length_spec.rb +++ b/spec/ruby/core/array/length_spec.rb @@ -1,7 +1,14 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/length' describe "Array#length" do - it_behaves_like :array_length, :length + it "returns the number of elements" do + [].length.should == 0 + [1, 2, 3].length.should == 3 + end + + it "properly handles recursive arrays" do + ArraySpecs.empty_recursive_array.length.should == 1 + ArraySpecs.recursive_array.length.should == 8 + end end diff --git a/spec/ruby/core/array/map_spec.rb b/spec/ruby/core/array/map_spec.rb index 0c7f3afa8c029a..f5e88c86245767 100644 --- a/spec/ruby/core/array/map_spec.rb +++ b/spec/ruby/core/array/map_spec.rb @@ -1,11 +1,13 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/collect' describe "Array#map" do - it_behaves_like :array_collect, :map + it "is an alias of Array#collect" do + Array.instance_method(:map).should == Array.instance_method(:collect) + end end describe "Array#map!" do - it_behaves_like :array_collect_b, :map! + it "is an alias of Array#collect!" do + Array.instance_method(:map!).should == Array.instance_method(:collect!) + end end diff --git a/spec/ruby/core/array/prepend_spec.rb b/spec/ruby/core/array/prepend_spec.rb index 368b8dcfcd5fa4..2d0ce31c7141ca 100644 --- a/spec/ruby/core/array/prepend_spec.rb +++ b/spec/ruby/core/array/prepend_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/unshift' describe "Array#prepend" do - it_behaves_like :array_unshift, :prepend + it "is an alias of Array#unshift" do + Array.instance_method(:prepend).should == Array.instance_method(:unshift) + end end diff --git a/spec/ruby/core/array/push_spec.rb b/spec/ruby/core/array/push_spec.rb index 607cbc7b4dc4c4..6255a84371748d 100644 --- a/spec/ruby/core/array/push_spec.rb +++ b/spec/ruby/core/array/push_spec.rb @@ -1,7 +1,36 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/push' describe "Array#push" do - it_behaves_like :array_push, :push + it "appends the arguments to the array" do + a = [ "a", "b", "c" ] + a.push("d", "e", "f").should.equal?(a) + a.push.should == ["a", "b", "c", "d", "e", "f"] + a.push(5) + a.should == ["a", "b", "c", "d", "e", "f", 5] + + a = [0, 1] + a.push(2) + a.should == [0, 1, 2] + end + + it "isn't confused by previous shift" do + a = [ "a", "b", "c" ] + a.shift + a.push("foo") + a.should == ["b", "c", "foo"] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.push(:last).should == [empty, :last] + + array = ArraySpecs.recursive_array + array.push(:last).should == [1, 'two', 3.0, array, array, array, array, array, :last] + end + + it "raises a FrozenError on a frozen array" do + -> { ArraySpecs.frozen_array.push(1) }.should.raise(FrozenError) + -> { ArraySpecs.frozen_array.push }.should.raise(FrozenError) + end end diff --git a/spec/ruby/core/array/replace_spec.rb b/spec/ruby/core/array/replace_spec.rb index 2f53338f5e4b04..ee6a98a6461ec5 100644 --- a/spec/ruby/core/array/replace_spec.rb +++ b/spec/ruby/core/array/replace_spec.rb @@ -1,7 +1,63 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/replace' describe "Array#replace" do - it_behaves_like :array_replace, :replace + it "replaces the elements with elements from other array" do + a = [1, 2, 3, 4, 5] + b = ['a', 'b', 'c'] + a.replace(b).should.equal?(a) + a.should == b + a.should_not.equal?(b) + + a.replace([4] * 10) + a.should == [4] * 10 + + a.replace([]) + a.should == [] + end + + it "properly handles recursive arrays" do + orig = [1, 2, 3] + empty = ArraySpecs.empty_recursive_array + orig.replace(empty) + orig.should == empty + + array = ArraySpecs.recursive_array + orig.replace(array) + orig.should == array + end + + it "returns self" do + ary = [1, 2, 3] + other = [:a, :b, :c] + ary.replace(other).should.equal?(ary) + end + + it "does not make self dependent to the original array" do + ary = [1, 2, 3] + other = [:a, :b, :c] + ary.replace(other) + ary.should == [:a, :b, :c] + ary << :d + ary.should == [:a, :b, :c, :d] + other.should == [:a, :b, :c] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('to_ary') + obj.stub!(:to_ary).and_return([1, 2, 3]) + [].replace(obj).should == [1, 2, 3] + end + + it "does not call #to_ary on Array subclasses" do + obj = ArraySpecs::ToAryArray[5, 6, 7] + obj.should_not_receive(:to_ary) + [].replace(ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7] + end + + it "raises a FrozenError on a frozen array" do + -> { + ArraySpecs.frozen_array.replace(ArraySpecs.frozen_array) + }.should.raise(FrozenError) + end end diff --git a/spec/ruby/core/array/select_spec.rb b/spec/ruby/core/array/select_spec.rb index e8775ee5ac5223..57ec0b2540eace 100644 --- a/spec/ruby/core/array/select_spec.rb +++ b/spec/ruby/core/array/select_spec.rb @@ -1,8 +1,37 @@ require_relative '../../spec_helper' -require_relative 'shared/select' +require_relative '../enumerable/shared/enumeratorized' +require_relative 'fixtures/classes' +require_relative 'shared/enumeratorize' +require_relative 'shared/iterable_and_tolerating_size_increasing' +require_relative 'shared/keep_if' describe "Array#select" do - it_behaves_like :array_select, :select + it_behaves_like :enumeratorize, :select + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :select + + before :each do + @object = [1,2,3] + end + it_behaves_like :enumeratorized_with_origin_size, :select + + it "returns a new array of elements for which block is true" do + [1, 3, 4, 5, 6, 9].select { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].select { true }.should.instance_of?(Array) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.select { true }.should == empty + empty.select { false }.should == [] + + array = ArraySpecs.recursive_array + array.select { true }.should == [1, 'two', 3.0, array, array, array, array, array] + array.select { false }.should == [] + end end describe "Array#select!" do diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb deleted file mode 100644 index aec51c9dc9623f..00000000000000 --- a/spec/ruby/core/array/shared/collect.rb +++ /dev/null @@ -1,141 +0,0 @@ -require_relative '../../enumerable/shared/enumeratorized' -require_relative '../shared/iterable_and_tolerating_size_increasing' - -describe :array_collect, shared: true do - it "returns a copy of array with each element replaced by the value returned by block" do - a = ['a', 'b', 'c', 'd'] - b = a.send(@method) { |i| i + '!' } - b.should == ["a!", "b!", "c!", "d!"] - b.should_not.equal? a - end - - it "does not return subclass instance" do - ArraySpecs::MyArray[1, 2, 3].send(@method) { |x| x + 1 }.should.instance_of?(Array) - end - - it "does not change self" do - a = ['a', 'b', 'c', 'd'] - a.send(@method) { |i| i + '!' } - a.should == ['a', 'b', 'c', 'd'] - end - - it "returns the evaluated value of block if it broke in the block" do - a = ['a', 'b', 'c', 'd'] - b = a.send(@method) {|i| - if i == 'c' - break 0 - else - i + '!' - end - } - b.should == 0 - end - - it "returns an Enumerator when no block given" do - a = [1, 2, 3] - a.send(@method).should.instance_of?(Enumerator) - end - - it "raises an ArgumentError when no block and with arguments" do - a = [1, 2, 3] - -> { - a.send(@method, :foo) - }.should.raise(ArgumentError) - end - - before :all do - @object = [1, 2, 3, 4] - end - it_should_behave_like :enumeratorized_with_origin_size - - it_should_behave_like :array_iterable_and_tolerating_size_increasing -end - -describe :array_collect_b, shared: true do - it "replaces each element with the value returned by block" do - a = [7, 9, 3, 5] - a.send(@method) { |i| i - 1 }.should.equal?(a) - a.should == [6, 8, 2, 4] - end - - it "returns self" do - a = [1, 2, 3, 4, 5] - b = a.send(@method) {|i| i+1 } - a.should.equal? b - end - - it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do - a = ['a', 'b', 'c', 'd'] - b = a.send(@method) {|i| - if i == 'c' - break 0 - else - i + '!' - end - } - b.should == 0 - a.should == ['a!', 'b!', 'c', 'd'] - end - - it "returns an Enumerator when no block given, and the enumerator can modify the original array" do - a = [1, 2, 3] - enum = a.send(@method) - enum.should.instance_of?(Enumerator) - enum.each{|i| "#{i}!" } - a.should == ["1!", "2!", "3!"] - end - - describe "when frozen" do - it "raises a FrozenError" do - -> { ArraySpecs.frozen_array.send(@method) {} }.should.raise(FrozenError) - end - - it "raises a FrozenError when empty" do - -> { ArraySpecs.empty_frozen_array.send(@method) {} }.should.raise(FrozenError) - end - - it "raises a FrozenError when calling #each on the returned Enumerator" do - enumerator = ArraySpecs.frozen_array.send(@method) - -> { enumerator.each {|x| x } }.should.raise(FrozenError) - end - - it "raises a FrozenError when calling #each on the returned Enumerator when empty" do - enumerator = ArraySpecs.empty_frozen_array.send(@method) - -> { enumerator.each {|x| x } }.should.raise(FrozenError) - end - end - - it "does not truncate the array is the block raises an exception" do - a = [1, 2, 3] - begin - a.send(@method) { raise StandardError, 'Oops' } - rescue - end - - a.should == [1, 2, 3] - end - - it "only changes elements before error is raised, keeping the element which raised an error." do - a = [1, 2, 3, 4] - begin - a.send(@method) do |e| - case e - when 1 then -1 - when 2 then -2 - when 3 then raise StandardError, 'Oops' - else 0 - end - end - rescue StandardError - end - - a.should == [-1, -2, 3, 4] - end - - before :all do - @object = [1, 2, 3, 4] - end - it_should_behave_like :enumeratorized_with_origin_size - - it_should_behave_like :array_iterable_and_tolerating_size_increasing -end diff --git a/spec/ruby/core/array/shared/index.rb b/spec/ruby/core/array/shared/index.rb deleted file mode 100644 index cc6d6cfb5b6f83..00000000000000 --- a/spec/ruby/core/array/shared/index.rb +++ /dev/null @@ -1,41 +0,0 @@ -require_relative '../shared/iterable_and_tolerating_size_increasing' - -describe :array_index, shared: true do - it "returns the index of the first element == to object" do - x = mock('3') - def x.==(obj) 3 == obj; end - - [2, x, 3, 1, 3, 1].send(@method, 3).should == 1 - [2, 3.0, 3, x, 1, 3, 1].send(@method, x).should == 1 - end - - it "returns 0 if first element == to object" do - [2, 1, 3, 2, 5].send(@method, 2).should == 0 - end - - it "returns size-1 if only last element == to object" do - [2, 1, 3, 1, 5].send(@method, 5).should == 4 - end - - it "returns nil if no element == to object" do - [2, 1, 1, 1, 1].send(@method, 3).should == nil - end - - it "accepts a block instead of an argument" do - [4, 2, 1, 5, 1, 3].send(@method) {|x| x < 2}.should == 2 - end - - it "ignores the block if there is an argument" do - -> { - [4, 2, 1, 5, 1, 3].send(@method, 5) {|x| x < 2}.should == 3 - }.should complain(/given block not used/) - end - - describe "given no argument and no block" do - it "produces an Enumerator" do - [].send(@method).should.instance_of?(Enumerator) - end - end - - it_should_behave_like :array_iterable_and_tolerating_size_increasing -end diff --git a/spec/ruby/core/array/shared/inspect.rb b/spec/ruby/core/array/shared/inspect.rb deleted file mode 100644 index 7197cd7f264813..00000000000000 --- a/spec/ruby/core/array/shared/inspect.rb +++ /dev/null @@ -1,107 +0,0 @@ -require_relative '../fixtures/encoded_strings' - -describe :array_inspect, shared: true do - it "returns a string" do - [1, 2, 3].send(@method).should.instance_of?(String) - end - - it "returns '[]' for an empty Array" do - [].send(@method).should == "[]" - end - - it "calls inspect on its elements and joins the results with commas" do - items = Array.new(3) do |i| - obj = mock(i.to_s) - obj.should_receive(:inspect).and_return(i.to_s) - obj - end - items.send(@method).should == "[0, 1, 2]" - end - - it "does not call #to_s on a String returned from #inspect" do - str = +"abc" - str.should_not_receive(:to_s) - - [str].send(@method).should == '["abc"]' - end - - it "calls #to_s on the object returned from #inspect if the Object isn't a String" do - obj = mock("Array#inspect/to_s calls #to_s") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_return("abc") - - [obj].send(@method).should == "[abc]" - end - - it "does not call #to_str on the object returned from #inspect when it is not a String" do - obj = mock("Array#inspect/to_s does not call #to_str") - obj.should_receive(:inspect).and_return(obj) - obj.should_not_receive(:to_str) - - [obj].send(@method).should =~ /^\[#\]$/ - end - - it "does not call #to_str on the object returned from #to_s when it is not a String" do - obj = mock("Array#inspect/to_s does not call #to_str on #to_s result") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_return(obj) - obj.should_not_receive(:to_str) - - [obj].send(@method).should =~ /^\[#\]$/ - end - - it "does not swallow exceptions raised by #to_s" do - obj = mock("Array#inspect/to_s does not swallow #to_s exceptions") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_raise(Exception) - - -> { [obj].send(@method) }.should.raise(Exception) - end - - it "represents a recursive element with '[...]'" do - ArraySpecs.recursive_array.send(@method).should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]" - ArraySpecs.head_recursive_array.send(@method).should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]" - ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]" - end - - describe "with encoding" do - before :each do - @default_external_encoding = Encoding.default_external - end - - after :each do - Encoding.default_external = @default_external_encoding - end - - it "returns a US-ASCII string for an empty Array" do - [].send(@method).encoding.should == Encoding::US_ASCII - end - - it "use the default external encoding if it is ascii compatible" do - Encoding.default_external = Encoding.find('UTF-8') - - utf8 = "utf8".encode("UTF-8") - jp = "jp".encode("EUC-JP") - array = [jp, utf8] - - array.send(@method).encoding.name.should == "UTF-8" - end - - it "use US-ASCII encoding if the default external encoding is not ascii compatible" do - Encoding.default_external = Encoding.find('UTF-32') - - utf8 = "utf8".encode("UTF-8") - jp = "jp".encode("EUC-JP") - array = [jp, utf8] - - array.send(@method).encoding.name.should == "US-ASCII" - end - - it "does not raise if inspected result is not default external encoding" do - utf_16be = mock(+"utf_16be") - utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) - - [utf_16be].send(@method).should == '["utf_16be \u3042"]' - end - end -end diff --git a/spec/ruby/core/array/shared/join.rb b/spec/ruby/core/array/shared/join.rb index 2be60a4dbcd226..93d5329ee39f0d 100644 --- a/spec/ruby/core/array/shared/join.rb +++ b/spec/ruby/core/array/shared/join.rb @@ -1,103 +1,6 @@ require_relative '../fixtures/classes' require_relative '../fixtures/encoded_strings' -describe :array_join_with_default_separator, shared: true do - before :each do - @separator = $, - end - - after :each do - $, = @separator - end - - it "returns an empty string if the Array is empty" do - [].send(@method).should == '' - end - - it "returns a US-ASCII string for an empty Array" do - [].send(@method).encoding.should == Encoding::US_ASCII - end - - it "returns a string formed by concatenating each String element separated by $," do - suppress_warning { - $, = " | " - ["1", "2", "3"].send(@method).should == "1 | 2 | 3" - } - end - - it "attempts coercion via #to_str first" do - obj = mock('foo') - obj.should_receive(:to_str).any_number_of_times.and_return("foo") - [obj].send(@method).should == "foo" - end - - it "attempts coercion via #to_ary second" do - obj = mock('foo') - obj.should_receive(:to_str).any_number_of_times.and_return(nil) - obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"]) - [obj].send(@method).should == "foo" - end - - it "attempts coercion via #to_s third" do - obj = mock('foo') - obj.should_receive(:to_str).any_number_of_times.and_return(nil) - obj.should_receive(:to_ary).any_number_of_times.and_return(nil) - obj.should_receive(:to_s).any_number_of_times.and_return("foo") - [obj].send(@method).should == "foo" - end - - it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do - obj = mock('o') - class << obj; undef :to_s; end - -> { [1, obj].send(@method) }.should.raise(NoMethodError) - end - - it "raises an ArgumentError when the Array is recursive" do - -> { ArraySpecs.recursive_array.send(@method) }.should.raise(ArgumentError) - -> { ArraySpecs.head_recursive_array.send(@method) }.should.raise(ArgumentError) - -> { ArraySpecs.empty_recursive_array.send(@method) }.should.raise(ArgumentError) - end - - it "uses the first encoding when other strings are compatible" do - ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings - ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings - ary3 = ArraySpecs.array_with_utf8_and_7bit_binary_strings - ary4 = ArraySpecs.array_with_usascii_and_7bit_binary_strings - - ary1.send(@method).encoding.should == Encoding::UTF_8 - ary2.send(@method).encoding.should == Encoding::US_ASCII - ary3.send(@method).encoding.should == Encoding::UTF_8 - ary4.send(@method).encoding.should == Encoding::US_ASCII - end - - it "uses the widest common encoding when other strings are incompatible" do - ary1 = ArraySpecs.array_with_utf8_and_usascii_strings - ary2 = ArraySpecs.array_with_usascii_and_utf8_strings - - ary1.send(@method).encoding.should == Encoding::UTF_8 - ary2.send(@method).encoding.should == Encoding::UTF_8 - end - - it "fails for arrays with incompatibly-encoded strings" do - ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings - - -> { ary_utf8_bad_binary.send(@method) }.should.raise(EncodingError) - end - - context "when $, is not nil" do - before do - suppress_warning do - $, = '*' - end - end - - it "warns" do - -> { [].join }.should complain(/warning: \$, is set to non-nil value/) - -> { [].join(nil) }.should complain(/warning: \$, is set to non-nil value/) - end - end -end - describe :array_join_with_string_separator, shared: true do it "returns a string formed by concatenating each element.to_str separated by separator" do obj = mock('foo') diff --git a/spec/ruby/core/array/shared/length.rb b/spec/ruby/core/array/shared/length.rb deleted file mode 100644 index f84966d0baad6f..00000000000000 --- a/spec/ruby/core/array/shared/length.rb +++ /dev/null @@ -1,11 +0,0 @@ -describe :array_length, shared: true do - it "returns the number of elements" do - [].send(@method).should == 0 - [1, 2, 3].send(@method).should == 3 - end - - it "properly handles recursive arrays" do - ArraySpecs.empty_recursive_array.send(@method).should == 1 - ArraySpecs.recursive_array.send(@method).should == 8 - end -end diff --git a/spec/ruby/core/array/shared/push.rb b/spec/ruby/core/array/shared/push.rb deleted file mode 100644 index ec406e506e19b3..00000000000000 --- a/spec/ruby/core/array/shared/push.rb +++ /dev/null @@ -1,33 +0,0 @@ -describe :array_push, shared: true do - it "appends the arguments to the array" do - a = [ "a", "b", "c" ] - a.send(@method, "d", "e", "f").should.equal?(a) - a.send(@method).should == ["a", "b", "c", "d", "e", "f"] - a.send(@method, 5) - a.should == ["a", "b", "c", "d", "e", "f", 5] - - a = [0, 1] - a.send(@method, 2) - a.should == [0, 1, 2] - end - - it "isn't confused by previous shift" do - a = [ "a", "b", "c" ] - a.shift - a.send(@method, "foo") - a.should == ["b", "c", "foo"] - end - - it "properly handles recursive arrays" do - empty = ArraySpecs.empty_recursive_array - empty.send(@method, :last).should == [empty, :last] - - array = ArraySpecs.recursive_array - array.send(@method, :last).should == [1, 'two', 3.0, array, array, array, array, array, :last] - end - - it "raises a FrozenError on a frozen array" do - -> { ArraySpecs.frozen_array.send(@method, 1) }.should.raise(FrozenError) - -> { ArraySpecs.frozen_array.send(@method) }.should.raise(FrozenError) - end -end diff --git a/spec/ruby/core/array/shared/replace.rb b/spec/ruby/core/array/shared/replace.rb deleted file mode 100644 index 06bfd00795c9b4..00000000000000 --- a/spec/ruby/core/array/shared/replace.rb +++ /dev/null @@ -1,60 +0,0 @@ -describe :array_replace, shared: true do - it "replaces the elements with elements from other array" do - a = [1, 2, 3, 4, 5] - b = ['a', 'b', 'c'] - a.send(@method, b).should.equal?(a) - a.should == b - a.should_not.equal?(b) - - a.send(@method, [4] * 10) - a.should == [4] * 10 - - a.send(@method, []) - a.should == [] - end - - it "properly handles recursive arrays" do - orig = [1, 2, 3] - empty = ArraySpecs.empty_recursive_array - orig.send(@method, empty) - orig.should == empty - - array = ArraySpecs.recursive_array - orig.send(@method, array) - orig.should == array - end - - it "returns self" do - ary = [1, 2, 3] - other = [:a, :b, :c] - ary.send(@method, other).should.equal?(ary) - end - - it "does not make self dependent to the original array" do - ary = [1, 2, 3] - other = [:a, :b, :c] - ary.send(@method, other) - ary.should == [:a, :b, :c] - ary << :d - ary.should == [:a, :b, :c, :d] - other.should == [:a, :b, :c] - end - - it "tries to convert the passed argument to an Array using #to_ary" do - obj = mock('to_ary') - obj.stub!(:to_ary).and_return([1, 2, 3]) - [].send(@method, obj).should == [1, 2, 3] - end - - it "does not call #to_ary on Array subclasses" do - obj = ArraySpecs::ToAryArray[5, 6, 7] - obj.should_not_receive(:to_ary) - [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7] - end - - it "raises a FrozenError on a frozen array" do - -> { - ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array) - }.should.raise(FrozenError) - end -end diff --git a/spec/ruby/core/array/shared/select.rb b/spec/ruby/core/array/shared/select.rb deleted file mode 100644 index cb4f9acbb799b4..00000000000000 --- a/spec/ruby/core/array/shared/select.rb +++ /dev/null @@ -1,35 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/enumeratorize' -require_relative '../shared/keep_if' -require_relative '../shared/iterable_and_tolerating_size_increasing' -require_relative '../../enumerable/shared/enumeratorized' - -describe :array_select, shared: true do - it_should_behave_like :enumeratorize - - it_should_behave_like :array_iterable_and_tolerating_size_increasing - - before :each do - @object = [1,2,3] - end - it_should_behave_like :enumeratorized_with_origin_size - - it "returns a new array of elements for which block is true" do - [1, 3, 4, 5, 6, 9].send(@method) { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6] - end - - it "does not return subclass instance on Array subclasses" do - ArraySpecs::MyArray[1, 2, 3].send(@method) { true }.should.instance_of?(Array) - end - - it "properly handles recursive arrays" do - empty = ArraySpecs.empty_recursive_array - empty.send(@method) { true }.should == empty - empty.send(@method) { false }.should == [] - - array = ArraySpecs.recursive_array - array.send(@method) { true }.should == [1, 'two', 3.0, array, array, array, array, array] - array.send(@method) { false }.should == [] - end -end diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb deleted file mode 100644 index b838d86118c7ee..00000000000000 --- a/spec/ruby/core/array/shared/slice.rb +++ /dev/null @@ -1,857 +0,0 @@ -describe :array_slice, shared: true do - it "returns the element at index with [index]" do - [ "a", "b", "c", "d", "e" ].send(@method, 1).should == "b" - - a = [1, 2, 3, 4] - - a.send(@method, 0).should == 1 - a.send(@method, 1).should == 2 - a.send(@method, 2).should == 3 - a.send(@method, 3).should == 4 - a.send(@method, 4).should == nil - a.send(@method, 10).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns the element at index from the end of the array with [-index]" do - [ "a", "b", "c", "d", "e" ].send(@method, -2).should == "d" - - a = [1, 2, 3, 4] - - a.send(@method, -1).should == 4 - a.send(@method, -2).should == 3 - a.send(@method, -3).should == 2 - a.send(@method, -4).should == 1 - a.send(@method, -5).should == nil - a.send(@method, -10).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns count elements starting from index with [index, count]" do - [ "a", "b", "c", "d", "e" ].send(@method, 2, 3).should == ["c", "d", "e"] - - a = [1, 2, 3, 4] - - a.send(@method, 0, 0).should == [] - a.send(@method, 0, 1).should == [1] - a.send(@method, 0, 2).should == [1, 2] - a.send(@method, 0, 4).should == [1, 2, 3, 4] - a.send(@method, 0, 6).should == [1, 2, 3, 4] - a.send(@method, 0, -1).should == nil - a.send(@method, 0, -2).should == nil - a.send(@method, 0, -4).should == nil - - a.send(@method, 2, 0).should == [] - a.send(@method, 2, 1).should == [3] - a.send(@method, 2, 2).should == [3, 4] - a.send(@method, 2, 4).should == [3, 4] - a.send(@method, 2, -1).should == nil - - a.send(@method, 4, 0).should == [] - a.send(@method, 4, 2).should == [] - a.send(@method, 4, -1).should == nil - - a.send(@method, 5, 0).should == nil - a.send(@method, 5, 2).should == nil - a.send(@method, 5, -1).should == nil - - a.send(@method, 6, 0).should == nil - a.send(@method, 6, 2).should == nil - a.send(@method, 6, -1).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns count elements starting at index from the end of array with [-index, count]" do - [ "a", "b", "c", "d", "e" ].send(@method, -2, 2).should == ["d", "e"] - - a = [1, 2, 3, 4] - - a.send(@method, -1, 0).should == [] - a.send(@method, -1, 1).should == [4] - a.send(@method, -1, 2).should == [4] - a.send(@method, -1, -1).should == nil - - a.send(@method, -2, 0).should == [] - a.send(@method, -2, 1).should == [3] - a.send(@method, -2, 2).should == [3, 4] - a.send(@method, -2, 4).should == [3, 4] - a.send(@method, -2, -1).should == nil - - a.send(@method, -4, 0).should == [] - a.send(@method, -4, 1).should == [1] - a.send(@method, -4, 2).should == [1, 2] - a.send(@method, -4, 4).should == [1, 2, 3, 4] - a.send(@method, -4, 6).should == [1, 2, 3, 4] - a.send(@method, -4, -1).should == nil - - a.send(@method, -5, 0).should == nil - a.send(@method, -5, 1).should == nil - a.send(@method, -5, 10).should == nil - a.send(@method, -5, -1).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns the first count elements with [0, count]" do - [ "a", "b", "c", "d", "e" ].send(@method, 0, 3).should == ["a", "b", "c"] - end - - it "returns the subarray which is independent to self with [index,count]" do - a = [1, 2, 3] - sub = a.send(@method, 1,2) - sub.replace([:a, :b]) - a.should == [1, 2, 3] - end - - it "tries to convert the passed argument to an Integer using #to_int" do - obj = mock('to_int') - obj.stub!(:to_int).and_return(2) - - a = [1, 2, 3, 4] - a.send(@method, obj).should == 3 - a.send(@method, obj, 1).should == [3] - a.send(@method, obj, obj).should == [3, 4] - a.send(@method, 0, obj).should == [1, 2] - end - - it "raises TypeError if to_int returns non-integer" do - from = mock('from') - to = mock('to') - - # So we can construct a range out of them... - def from.<=>(o) 0 end - def to.<=>(o) 0 end - - a = [1, 2, 3, 4, 5] - - def from.to_int() 'cat' end - def to.to_int() -2 end - - -> { a.send(@method, from..to) }.should.raise(TypeError) - - def from.to_int() 1 end - def to.to_int() 'cat' end - - -> { a.send(@method, from..to) }.should.raise(TypeError) - end - - it "returns the elements specified by Range indexes with [m..n]" do - [ "a", "b", "c", "d", "e" ].send(@method, 1..3).should == ["b", "c", "d"] - [ "a", "b", "c", "d", "e" ].send(@method, 4..-1).should == ['e'] - [ "a", "b", "c", "d", "e" ].send(@method, 3..3).should == ['d'] - [ "a", "b", "c", "d", "e" ].send(@method, 3..-2).should == ['d'] - ['a'].send(@method, 0..-1).should == ['a'] - - a = [1, 2, 3, 4] - - a.send(@method, 0..-10).should == [] - a.send(@method, 0..0).should == [1] - a.send(@method, 0..1).should == [1, 2] - a.send(@method, 0..2).should == [1, 2, 3] - a.send(@method, 0..3).should == [1, 2, 3, 4] - a.send(@method, 0..4).should == [1, 2, 3, 4] - a.send(@method, 0..10).should == [1, 2, 3, 4] - - a.send(@method, 2..-10).should == [] - a.send(@method, 2..0).should == [] - a.send(@method, 2..2).should == [3] - a.send(@method, 2..3).should == [3, 4] - a.send(@method, 2..4).should == [3, 4] - - a.send(@method, 3..0).should == [] - a.send(@method, 3..3).should == [4] - a.send(@method, 3..4).should == [4] - - a.send(@method, 4..0).should == [] - a.send(@method, 4..4).should == [] - a.send(@method, 4..5).should == [] - - a.send(@method, 5..0).should == nil - a.send(@method, 5..5).should == nil - a.send(@method, 5..6).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns elements specified by Range indexes except the element at index n with [m...n]" do - [ "a", "b", "c", "d", "e" ].send(@method, 1...3).should == ["b", "c"] - - a = [1, 2, 3, 4] - - a.send(@method, 0...-10).should == [] - a.send(@method, 0...0).should == [] - a.send(@method, 0...1).should == [1] - a.send(@method, 0...2).should == [1, 2] - a.send(@method, 0...3).should == [1, 2, 3] - a.send(@method, 0...4).should == [1, 2, 3, 4] - a.send(@method, 0...10).should == [1, 2, 3, 4] - - a.send(@method, 2...-10).should == [] - a.send(@method, 2...0).should == [] - a.send(@method, 2...2).should == [] - a.send(@method, 2...3).should == [3] - a.send(@method, 2...4).should == [3, 4] - - a.send(@method, 3...0).should == [] - a.send(@method, 3...3).should == [] - a.send(@method, 3...4).should == [4] - - a.send(@method, 4...0).should == [] - a.send(@method, 4...4).should == [] - a.send(@method, 4...5).should == [] - - a.send(@method, 5...0).should == nil - a.send(@method, 5...5).should == nil - a.send(@method, 5...6).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns elements that exist if range start is in the array but range end is not with [m..n]" do - [ "a", "b", "c", "d", "e" ].send(@method, 4..7).should == ["e"] - end - - it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do - a = [1, 2, 3, 4] - - a.send(@method, -1..-1).should == [4] - a.send(@method, -1...-1).should == [] - a.send(@method, -1..3).should == [4] - a.send(@method, -1...3).should == [] - a.send(@method, -1..4).should == [4] - a.send(@method, -1...4).should == [4] - a.send(@method, -1..10).should == [4] - a.send(@method, -1...10).should == [4] - a.send(@method, -1..0).should == [] - a.send(@method, -1..-4).should == [] - a.send(@method, -1...-4).should == [] - a.send(@method, -1..-6).should == [] - a.send(@method, -1...-6).should == [] - - a.send(@method, -2..-2).should == [3] - a.send(@method, -2...-2).should == [] - a.send(@method, -2..-1).should == [3, 4] - a.send(@method, -2...-1).should == [3] - a.send(@method, -2..10).should == [3, 4] - a.send(@method, -2...10).should == [3, 4] - - a.send(@method, -4..-4).should == [1] - a.send(@method, -4..-2).should == [1, 2, 3] - a.send(@method, -4...-2).should == [1, 2] - a.send(@method, -4..-1).should == [1, 2, 3, 4] - a.send(@method, -4...-1).should == [1, 2, 3] - a.send(@method, -4..3).should == [1, 2, 3, 4] - a.send(@method, -4...3).should == [1, 2, 3] - a.send(@method, -4..4).should == [1, 2, 3, 4] - a.send(@method, -4...4).should == [1, 2, 3, 4] - a.send(@method, -4...4).should == [1, 2, 3, 4] - a.send(@method, -4..0).should == [1] - a.send(@method, -4...0).should == [] - a.send(@method, -4..1).should == [1, 2] - a.send(@method, -4...1).should == [1] - - a.send(@method, -5..-5).should == nil - a.send(@method, -5...-5).should == nil - a.send(@method, -5..-4).should == nil - a.send(@method, -5..-1).should == nil - a.send(@method, -5..10).should == nil - - a.should == [1, 2, 3, 4] - end - - it "returns the subarray which is independent to self with [m..n]" do - a = [1, 2, 3] - sub = a.send(@method, 1..2) - sub.replace([:a, :b]) - a.should == [1, 2, 3] - end - - it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do - from = mock('from') - to = mock('to') - - # So we can construct a range out of them... - def from.<=>(o) 0 end - def to.<=>(o) 0 end - - def from.to_int() 1 end - def to.to_int() -2 end - - a = [1, 2, 3, 4] - - a.send(@method, from..to).should == [2, 3] - a.send(@method, from...to).should == [2] - a.send(@method, 1..0).should == [] - a.send(@method, 1...0).should == [] - - -> { a.send(@method, "a" .. "b") }.should.raise(TypeError) - -> { a.send(@method, "a" ... "b") }.should.raise(TypeError) - -> { a.send(@method, from .. "b") }.should.raise(TypeError) - -> { a.send(@method, from ... "b") }.should.raise(TypeError) - end - - it "returns the same elements as [m..n] and [m...n] with Range subclasses" do - a = [1, 2, 3, 4] - range_incl = ArraySpecs::MyRange.new(1, 2) - range_excl = ArraySpecs::MyRange.new(-3, -1, true) - - a.send(@method, range_incl).should == [2, 3] - a.send(@method, range_excl).should == [2, 3] - end - - it "returns nil for a requested index not in the array with [index]" do - [ "a", "b", "c", "d", "e" ].send(@method, 5).should == nil - end - - it "returns [] if the index is valid but length is zero with [index, length]" do - [ "a", "b", "c", "d", "e" ].send(@method, 0, 0).should == [] - [ "a", "b", "c", "d", "e" ].send(@method, 2, 0).should == [] - end - - it "returns nil if length is zero but index is invalid with [index, length]" do - [ "a", "b", "c", "d", "e" ].send(@method, 100, 0).should == nil - [ "a", "b", "c", "d", "e" ].send(@method, -50, 0).should == nil - end - - # This is by design. It is in the official documentation. - it "returns [] if index == array.size with [index, length]" do - %w|a b c d e|.send(@method, 5, 2).should == [] - end - - it "returns nil if index > array.size with [index, length]" do - %w|a b c d e|.send(@method, 6, 2).should == nil - end - - it "returns nil if length is negative with [index, length]" do - %w|a b c d e|.send(@method, 3, -1).should == nil - %w|a b c d e|.send(@method, 2, -2).should == nil - %w|a b c d e|.send(@method, 1, -100).should == nil - end - - it "returns nil if no requested index is in the array with [m..n]" do - [ "a", "b", "c", "d", "e" ].send(@method, 6..10).should == nil - end - - it "returns nil if range start is not in the array with [m..n]" do - [ "a", "b", "c", "d", "e" ].send(@method, -10..2).should == nil - [ "a", "b", "c", "d", "e" ].send(@method, 10..12).should == nil - end - - it "returns an empty array when m == n with [m...n]" do - [1, 2, 3, 4, 5].send(@method, 1...1).should == [] - end - - it "returns an empty array with [0...0]" do - [1, 2, 3, 4, 5].send(@method, 0...0).should == [] - end - - it "returns a subarray where m, n negatives and m < n with [m..n]" do - [ "a", "b", "c", "d", "e" ].send(@method, -3..-2).should == ["c", "d"] - end - - it "returns an array containing the first element with [0..0]" do - [1, 2, 3, 4, 5].send(@method, 0..0).should == [1] - end - - it "returns the entire array with [0..-1]" do - [1, 2, 3, 4, 5].send(@method, 0..-1).should == [1, 2, 3, 4, 5] - end - - it "returns all but the last element with [0...-1]" do - [1, 2, 3, 4, 5].send(@method, 0...-1).should == [1, 2, 3, 4] - end - - it "returns [3] for [2..-1] out of [1, 2, 3]" do - [1,2,3].send(@method, 2..-1).should == [3] - end - - it "returns an empty array when m > n and m, n are positive with [m..n]" do - [1, 2, 3, 4, 5].send(@method, 3..2).should == [] - end - - it "returns an empty array when m > n and m, n are negative with [m..n]" do - [1, 2, 3, 4, 5].send(@method, -2..-3).should == [] - end - - it "does not expand array when the indices are outside of the array bounds" do - a = [1, 2] - a.send(@method, 4).should == nil - a.should == [1, 2] - a.send(@method, 4, 0).should == nil - a.should == [1, 2] - a.send(@method, 6, 1).should == nil - a.should == [1, 2] - a.send(@method, 8...8).should == nil - a.should == [1, 2] - a.send(@method, 10..10).should == nil - a.should == [1, 2] - end - - describe "with a subclass of Array" do - before :each do - ScratchPad.clear - - @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] - end - - it "returns a Array instance with [n, m]" do - @array.send(@method, 0, 2).should.instance_of?(Array) - end - - it "returns a Array instance with [-n, m]" do - @array.send(@method, -3, 2).should.instance_of?(Array) - end - - it "returns a Array instance with [n..m]" do - @array.send(@method, 1..3).should.instance_of?(Array) - end - - it "returns a Array instance with [n...m]" do - @array.send(@method, 1...3).should.instance_of?(Array) - end - - it "returns a Array instance with [-n..-m]" do - @array.send(@method, -3..-1).should.instance_of?(Array) - end - - it "returns a Array instance with [-n...-m]" do - @array.send(@method, -3...-1).should.instance_of?(Array) - end - - it "returns an empty array when m == n with [m...n]" do - @array.send(@method, 1...1).should == [] - ScratchPad.recorded.should == nil - end - - it "returns an empty array with [0...0]" do - @array.send(@method, 0...0).should == [] - ScratchPad.recorded.should == nil - end - - it "returns an empty array when m > n and m, n are positive with [m..n]" do - @array.send(@method, 3..2).should == [] - ScratchPad.recorded.should == nil - end - - it "returns an empty array when m > n and m, n are negative with [m..n]" do - @array.send(@method, -2..-3).should == [] - ScratchPad.recorded.should == nil - end - - it "returns [] if index == array.size with [index, length]" do - @array.send(@method, 5, 2).should == [] - ScratchPad.recorded.should == nil - end - - it "returns [] if the index is valid but length is zero with [index, length]" do - @array.send(@method, 0, 0).should == [] - @array.send(@method, 2, 0).should == [] - ScratchPad.recorded.should == nil - end - - it "does not call #initialize on the subclass instance" do - @array.send(@method, 0, 3).should == [1, 2, 3] - ScratchPad.recorded.should == nil - end - end - - it "raises a RangeError when the start index is out of range of Fixnum" do - array = [1, 2, 3, 4, 5, 6] - obj = mock('large value') - obj.should_receive(:to_int).and_return(bignum_value) - -> { array.send(@method, obj) }.should.raise(RangeError) - - obj = 8e19 - -> { array.send(@method, obj) }.should.raise(RangeError) - - # boundary value when longs are 64 bits - -> { array.send(@method, 2.0**63) }.should.raise(RangeError) - - # just under the boundary value when longs are 64 bits - array.send(@method, max_long.to_f.prev_float).should == nil - end - - it "raises a RangeError when the length is out of range of Fixnum" do - array = [1, 2, 3, 4, 5, 6] - obj = mock('large value') - obj.should_receive(:to_int).and_return(bignum_value) - -> { array.send(@method, 1, obj) }.should.raise(RangeError) - - obj = 8e19 - -> { array.send(@method, 1, obj) }.should.raise(RangeError) - end - - it "raises a type error if a range is passed with a length" do - ->{ [1, 2, 3].send(@method, 1..2, 1) }.should.raise(TypeError) - end - - it "raises a RangeError if passed a range with a bound that is too large" do - array = [1, 2, 3, 4, 5, 6] - -> { array.send(@method, bignum_value..(bignum_value + 1)) }.should.raise(RangeError) - -> { array.send(@method, 0..bignum_value) }.should.raise(RangeError) - end - - it "can accept endless ranges" do - a = [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(2..)")).should == [2, 3, 4, 5] - a.send(@method, eval("(2...)")).should == [2, 3, 4, 5] - a.send(@method, eval("(-2..)")).should == [4, 5] - a.send(@method, eval("(-2...)")).should == [4, 5] - a.send(@method, eval("(9..)")).should == nil - a.send(@method, eval("(9...)")).should == nil - a.send(@method, eval("(-9..)")).should == nil - a.send(@method, eval("(-9...)")).should == nil - end - - describe "can be sliced with Enumerator::ArithmeticSequence" do - before :each do - @array = [0, 1, 2, 3, 4, 5] - end - - it "has endless range and positive steps" do - @array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5] - @array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4] - @array.send(@method, eval("(0..).step(10)")).should == [0] - - @array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5] - @array.send(@method, eval("(2..).step(2)")).should == [2, 4] - @array.send(@method, eval("(2..).step(10)")).should == [2] - - @array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5] - @array.send(@method, eval("(-3..).step(2)")).should == [3, 5] - @array.send(@method, eval("(-3..).step(10)")).should == [3] - end - - it "has beginless range and positive steps" do - # end with zero index - @array.send(@method, (..0).step(1)).should == [0] - @array.send(@method, (...0).step(1)).should == [] - - @array.send(@method, (..0).step(2)).should == [0] - @array.send(@method, (...0).step(2)).should == [] - - @array.send(@method, (..0).step(10)).should == [0] - @array.send(@method, (...0).step(10)).should == [] - - # end with positive index - @array.send(@method, (..3).step(1)).should == [0, 1, 2, 3] - @array.send(@method, (...3).step(1)).should == [0, 1, 2] - - @array.send(@method, (..3).step(2)).should == [0, 2] - @array.send(@method, (...3).step(2)).should == [0, 2] - - @array.send(@method, (..3).step(10)).should == [0] - @array.send(@method, (...3).step(10)).should == [0] - - # end with negative index - @array.send(@method, (..-2).step(1)).should == [0, 1, 2, 3, 4,] - @array.send(@method, (...-2).step(1)).should == [0, 1, 2, 3] - - @array.send(@method, (..-2).step(2)).should == [0, 2, 4] - @array.send(@method, (...-2).step(2)).should == [0, 2] - - @array.send(@method, (..-2).step(10)).should == [0] - @array.send(@method, (...-2).step(10)).should == [0] - end - - it "has endless range and negative steps" do - @array.send(@method, eval("(0..).step(-1)")).should == [0] - @array.send(@method, eval("(0..).step(-2)")).should == [0] - @array.send(@method, eval("(0..).step(-10)")).should == [0] - - @array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0] - @array.send(@method, eval("(2..).step(-2)")).should == [2, 0] - - @array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0] - @array.send(@method, eval("(-3..).step(-2)")).should == [3, 1] - end - - it "has closed range and positive steps" do - # start and end with 0 - @array.send(@method, eval("(0..0).step(1)")).should == [0] - @array.send(@method, eval("(0...0).step(1)")).should == [] - - @array.send(@method, eval("(0..0).step(2)")).should == [0] - @array.send(@method, eval("(0...0).step(2)")).should == [] - - @array.send(@method, eval("(0..0).step(10)")).should == [0] - @array.send(@method, eval("(0...0).step(10)")).should == [] - - # start and end with positive index - @array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3] - @array.send(@method, eval("(1...3).step(1)")).should == [1, 2] - - @array.send(@method, eval("(1..3).step(2)")).should == [1, 3] - @array.send(@method, eval("(1...3).step(2)")).should == [1] - - @array.send(@method, eval("(1..3).step(10)")).should == [1] - @array.send(@method, eval("(1...3).step(10)")).should == [1] - - # start with positive index, end with negative index - @array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4] - @array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3] - - @array.send(@method, eval("(1..-2).step(2)")).should == [1, 3] - @array.send(@method, eval("(1...-2).step(2)")).should == [1, 3] - - @array.send(@method, eval("(1..-2).step(10)")).should == [1] - @array.send(@method, eval("(1...-2).step(10)")).should == [1] - - # start with negative index, end with positive index - @array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4] - @array.send(@method, eval("(-4...4).step(1)")).should == [2, 3] - - @array.send(@method, eval("(-4..4).step(2)")).should == [2, 4] - @array.send(@method, eval("(-4...4).step(2)")).should == [2] - - @array.send(@method, eval("(-4..4).step(10)")).should == [2] - @array.send(@method, eval("(-4...4).step(10)")).should == [2] - - # start with negative index, end with negative index - @array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4] - @array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3] - - @array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4] - @array.send(@method, eval("(-4...-2).step(2)")).should == [2] - - @array.send(@method, eval("(-4..-2).step(10)")).should == [2] - @array.send(@method, eval("(-4...-2).step(10)")).should == [2] - end - - it "has closed range and negative steps" do - # start and end with 0 - @array.send(@method, eval("(0..0).step(-1)")).should == [0] - @array.send(@method, eval("(0...0).step(-1)")).should == [] - - @array.send(@method, eval("(0..0).step(-2)")).should == [0] - @array.send(@method, eval("(0...0).step(-2)")).should == [] - - @array.send(@method, eval("(0..0).step(-10)")).should == [0] - @array.send(@method, eval("(0...0).step(-10)")).should == [] - - # start and end with positive index - @array.send(@method, eval("(1..3).step(-1)")).should == [] - @array.send(@method, eval("(1...3).step(-1)")).should == [] - - @array.send(@method, eval("(1..3).step(-2)")).should == [] - @array.send(@method, eval("(1...3).step(-2)")).should == [] - - @array.send(@method, eval("(1..3).step(-10)")).should == [] - @array.send(@method, eval("(1...3).step(-10)")).should == [] - - # start with positive index, end with negative index - @array.send(@method, eval("(1..-2).step(-1)")).should == [] - @array.send(@method, eval("(1...-2).step(-1)")).should == [] - - @array.send(@method, eval("(1..-2).step(-2)")).should == [] - @array.send(@method, eval("(1...-2).step(-2)")).should == [] - - @array.send(@method, eval("(1..-2).step(-10)")).should == [] - @array.send(@method, eval("(1...-2).step(-10)")).should == [] - - # start with negative index, end with positive index - @array.send(@method, eval("(-4..4).step(-1)")).should == [] - @array.send(@method, eval("(-4...4).step(-1)")).should == [] - - @array.send(@method, eval("(-4..4).step(-2)")).should == [] - @array.send(@method, eval("(-4...4).step(-2)")).should == [] - - @array.send(@method, eval("(-4..4).step(-10)")).should == [] - @array.send(@method, eval("(-4...4).step(-10)")).should == [] - - # start with negative index, end with negative index - @array.send(@method, eval("(-4..-2).step(-1)")).should == [] - @array.send(@method, eval("(-4...-2).step(-1)")).should == [] - - @array.send(@method, eval("(-4..-2).step(-2)")).should == [] - @array.send(@method, eval("(-4...-2).step(-2)")).should == [] - - @array.send(@method, eval("(-4..-2).step(-10)")).should == [] - @array.send(@method, eval("(-4...-2).step(-10)")).should == [] - end - - it "has inverted closed range and positive steps" do - # start and end with positive index - @array.send(@method, eval("(3..1).step(1)")).should == [] - @array.send(@method, eval("(3...1).step(1)")).should == [] - - @array.send(@method, eval("(3..1).step(2)")).should == [] - @array.send(@method, eval("(3...1).step(2)")).should == [] - - @array.send(@method, eval("(3..1).step(10)")).should == [] - @array.send(@method, eval("(3...1).step(10)")).should == [] - - # start with negative index, end with positive index - @array.send(@method, eval("(-2..1).step(1)")).should == [] - @array.send(@method, eval("(-2...1).step(1)")).should == [] - - @array.send(@method, eval("(-2..1).step(2)")).should == [] - @array.send(@method, eval("(-2...1).step(2)")).should == [] - - @array.send(@method, eval("(-2..1).step(10)")).should == [] - @array.send(@method, eval("(-2...1).step(10)")).should == [] - - # start with positive index, end with negative index - @array.send(@method, eval("(4..-4).step(1)")).should == [] - @array.send(@method, eval("(4...-4).step(1)")).should == [] - - @array.send(@method, eval("(4..-4).step(2)")).should == [] - @array.send(@method, eval("(4...-4).step(2)")).should == [] - - @array.send(@method, eval("(4..-4).step(10)")).should == [] - @array.send(@method, eval("(4...-4).step(10)")).should == [] - - # start with negative index, end with negative index - @array.send(@method, eval("(-2..-4).step(1)")).should == [] - @array.send(@method, eval("(-2...-4).step(1)")).should == [] - - @array.send(@method, eval("(-2..-4).step(2)")).should == [] - @array.send(@method, eval("(-2...-4).step(2)")).should == [] - - @array.send(@method, eval("(-2..-4).step(10)")).should == [] - @array.send(@method, eval("(-2...-4).step(10)")).should == [] - end - - it "has range with bounds outside of array" do - # end is equal to array's length - @array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5] - -> { @array.send(@method, (0..6).step(2)) }.should.raise(RangeError) - - # end is greater than length with positive steps - @array.send(@method, (1..6).step(2)).should == [1, 3, 5] - @array.send(@method, (2..7).step(2)).should == [2, 4] - -> { @array.send(@method, (2..8).step(2)) }.should.raise(RangeError) - - # begin is greater than length with negative steps - @array.send(@method, (6..1).step(-2)).should == [5, 3, 1] - @array.send(@method, (7..2).step(-2)).should == [5, 3] - -> { @array.send(@method, (8..2).step(-2)) }.should.raise(RangeError) - end - - it "has endless range with start outside of array's bounds" do - @array.send(@method, eval("(6..).step(1)")).should == [] - @array.send(@method, eval("(7..).step(1)")).should == nil - - @array.send(@method, eval("(6..).step(2)")).should == [] - -> { @array.send(@method, eval("(7..).step(2)")) }.should.raise(RangeError) - end - end - - it "can accept beginless ranges" do - a = [0, 1, 2, 3, 4, 5] - a.send(@method, (..3)).should == [0, 1, 2, 3] - a.send(@method, (...3)).should == [0, 1, 2] - a.send(@method, (..-3)).should == [0, 1, 2, 3] - a.send(@method, (...-3)).should == [0, 1, 2] - a.send(@method, (..0)).should == [0] - a.send(@method, (...0)).should == [] - a.send(@method, (..9)).should == [0, 1, 2, 3, 4, 5] - a.send(@method, (...9)).should == [0, 1, 2, 3, 4, 5] - a.send(@method, (..-9)).should == [] - a.send(@method, (...-9)).should == [] - end - - describe "can be sliced with Enumerator::ArithmeticSequence" do - it "with infinite/inverted ranges and negative steps" do - @array = [0, 1, 2, 3, 4, 5] - @array.send(@method, (2..).step(-1)).should == [2, 1, 0] - @array.send(@method, (2..).step(-2)).should == [2, 0] - @array.send(@method, (2..).step(-3)).should == [2] - @array.send(@method, (2..).step(-4)).should == [2] - - @array.send(@method, (-3..).step(-1)).should == [3, 2, 1, 0] - @array.send(@method, (-3..).step(-2)).should == [3, 1] - @array.send(@method, (-3..).step(-3)).should == [3, 0] - @array.send(@method, (-3..).step(-4)).should == [3] - @array.send(@method, (-3..).step(-5)).should == [3] - - @array.send(@method, (..0).step(-1)).should == [5, 4, 3, 2, 1, 0] - @array.send(@method, (..0).step(-2)).should == [5, 3, 1] - @array.send(@method, (..0).step(-3)).should == [5, 2] - @array.send(@method, (..0).step(-4)).should == [5, 1] - @array.send(@method, (..0).step(-5)).should == [5, 0] - @array.send(@method, (..0).step(-6)).should == [5] - @array.send(@method, (..0).step(-7)).should == [5] - - @array.send(@method, (...0).step(-1)).should == [5, 4, 3, 2, 1] - @array.send(@method, (...0).step(-2)).should == [5, 3, 1] - @array.send(@method, (...0).step(-3)).should == [5, 2] - @array.send(@method, (...0).step(-4)).should == [5, 1] - @array.send(@method, (...0).step(-5)).should == [5] - @array.send(@method, (...0).step(-6)).should == [5] - - @array.send(@method, (...1).step(-1)).should == [5, 4, 3, 2] - @array.send(@method, (...1).step(-2)).should == [5, 3] - @array.send(@method, (...1).step(-3)).should == [5, 2] - @array.send(@method, (...1).step(-4)).should == [5] - @array.send(@method, (...1).step(-5)).should == [5] - - @array.send(@method, (..-5).step(-1)).should == [5, 4, 3, 2, 1] - @array.send(@method, (..-5).step(-2)).should == [5, 3, 1] - @array.send(@method, (..-5).step(-3)).should == [5, 2] - @array.send(@method, (..-5).step(-4)).should == [5, 1] - @array.send(@method, (..-5).step(-5)).should == [5] - @array.send(@method, (..-5).step(-6)).should == [5] - - @array.send(@method, (...-5).step(-1)).should == [5, 4, 3, 2] - @array.send(@method, (...-5).step(-2)).should == [5, 3] - @array.send(@method, (...-5).step(-3)).should == [5, 2] - @array.send(@method, (...-5).step(-4)).should == [5] - @array.send(@method, (...-5).step(-5)).should == [5] - - @array.send(@method, (4..1).step(-1)).should == [4, 3, 2, 1] - @array.send(@method, (4..1).step(-2)).should == [4, 2] - @array.send(@method, (4..1).step(-3)).should == [4, 1] - @array.send(@method, (4..1).step(-4)).should == [4] - @array.send(@method, (4..1).step(-5)).should == [4] - - @array.send(@method, (4...1).step(-1)).should == [4, 3, 2] - @array.send(@method, (4...1).step(-2)).should == [4, 2] - @array.send(@method, (4...1).step(-3)).should == [4] - @array.send(@method, (4...1).step(-4)).should == [4] - - @array.send(@method, (-2..1).step(-1)).should == [4, 3, 2, 1] - @array.send(@method, (-2..1).step(-2)).should == [4, 2] - @array.send(@method, (-2..1).step(-3)).should == [4, 1] - @array.send(@method, (-2..1).step(-4)).should == [4] - @array.send(@method, (-2..1).step(-5)).should == [4] - - @array.send(@method, (-2...1).step(-1)).should == [4, 3, 2] - @array.send(@method, (-2...1).step(-2)).should == [4, 2] - @array.send(@method, (-2...1).step(-3)).should == [4] - @array.send(@method, (-2...1).step(-4)).should == [4] - - @array.send(@method, (4..-5).step(-1)).should == [4, 3, 2, 1] - @array.send(@method, (4..-5).step(-2)).should == [4, 2] - @array.send(@method, (4..-5).step(-3)).should == [4, 1] - @array.send(@method, (4..-5).step(-4)).should == [4] - @array.send(@method, (4..-5).step(-5)).should == [4] - - @array.send(@method, (4...-5).step(-1)).should == [4, 3, 2] - @array.send(@method, (4...-5).step(-2)).should == [4, 2] - @array.send(@method, (4...-5).step(-3)).should == [4] - @array.send(@method, (4...-5).step(-4)).should == [4] - - @array.send(@method, (-2..-5).step(-1)).should == [4, 3, 2, 1] - @array.send(@method, (-2..-5).step(-2)).should == [4, 2] - @array.send(@method, (-2..-5).step(-3)).should == [4, 1] - @array.send(@method, (-2..-5).step(-4)).should == [4] - @array.send(@method, (-2..-5).step(-5)).should == [4] - - @array.send(@method, (-2...-5).step(-1)).should == [4, 3, 2] - @array.send(@method, (-2...-5).step(-2)).should == [4, 2] - @array.send(@method, (-2...-5).step(-3)).should == [4] - @array.send(@method, (-2...-5).step(-4)).should == [4] - end - end - - it "can accept nil...nil ranges" do - a = [0, 1, 2, 3, 4, 5] - a.send(@method, eval("(nil...nil)")).should == a - a.send(@method, (...nil)).should == a - a.send(@method, eval("(nil..)")).should == a - end -end diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb deleted file mode 100644 index b636347cd36d33..00000000000000 --- a/spec/ruby/core/array/shared/unshift.rb +++ /dev/null @@ -1,64 +0,0 @@ -describe :array_unshift, shared: true do - it "prepends object to the original array" do - a = [1, 2, 3] - a.send(@method, "a").should.equal?(a) - a.should == ['a', 1, 2, 3] - a.send(@method).should.equal?(a) - a.should == ['a', 1, 2, 3] - a.send(@method, 5, 4, 3) - a.should == [5, 4, 3, 'a', 1, 2, 3] - - # shift all but one element - a = [1, 2] - a.shift - a.send(@method, 3, 4) - a.should == [3, 4, 2] - - # now shift all elements - a.shift - a.shift - a.shift - a.send(@method, 3, 4) - a.should == [3, 4] - end - - it "returns self" do - a = [1, 2, 3] - a.send(@method, "a").should.equal?(a) - end - - it "quietly ignores unshifting nothing" do - [].send(@method).should == [] - end - - it "properly handles recursive arrays" do - empty = ArraySpecs.empty_recursive_array - empty.send(@method, :new).should == [:new, empty] - - array = ArraySpecs.recursive_array - array.send(@method, :new) - array[0..5].should == [:new, 1, 'two', 3.0, array, array] - end - - it "raises a FrozenError on a frozen array when the array is modified" do - -> { ArraySpecs.frozen_array.send(@method, 1) }.should.raise(FrozenError) - end - - # see [ruby-core:23666] - it "raises a FrozenError on a frozen array when the array would not be modified" do - -> { ArraySpecs.frozen_array.send(@method) }.should.raise(FrozenError) - end - - # https://github.com/truffleruby/truffleruby/issues/2772 - it "doesn't rely on Array#[]= so it can be overridden" do - subclass = Class.new(Array) do - def []=(*) - raise "[]= is called" - end - end - - array = subclass.new - array.send(@method, 1) - array.should == [1] - end -end diff --git a/spec/ruby/core/array/size_spec.rb b/spec/ruby/core/array/size_spec.rb index d68f956a832f35..83e89690124143 100644 --- a/spec/ruby/core/array/size_spec.rb +++ b/spec/ruby/core/array/size_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' describe "Array#size" do - it_behaves_like :array_length, :size + it "is an alias of Array#length" do + Array.instance_method(:size).should == Array.instance_method(:length) + end end diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb index eb7e47d9476ea3..230d1dc5d16361 100644 --- a/spec/ruby/core/array/slice_spec.rb +++ b/spec/ruby/core/array/slice_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/slice' describe "Array#slice!" do it "removes and return the element at index" do @@ -214,5 +213,7 @@ def to.to_int() -2 end end describe "Array#slice" do - it_behaves_like :array_slice, :slice + it "is an alias of Array#[]" do + Array.instance_method(:slice).should == Array.instance_method(:[]) + end end diff --git a/spec/ruby/core/array/to_s_spec.rb b/spec/ruby/core/array/to_s_spec.rb index e8476702ec0e1d..483e14f9025871 100644 --- a/spec/ruby/core/array/to_s_spec.rb +++ b/spec/ruby/core/array/to_s_spec.rb @@ -1,8 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/join' -require_relative 'shared/inspect' describe "Array#to_s" do - it_behaves_like :array_inspect, :to_s + it "is an alias of Array#inspect" do + Array.instance_method(:to_s).should == Array.instance_method(:inspect) + end end diff --git a/spec/ruby/core/array/unshift_spec.rb b/spec/ruby/core/array/unshift_spec.rb index b8b675e5f8617b..c190db4d02d876 100644 --- a/spec/ruby/core/array/unshift_spec.rb +++ b/spec/ruby/core/array/unshift_spec.rb @@ -1,7 +1,67 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/unshift' describe "Array#unshift" do - it_behaves_like :array_unshift, :unshift + it "prepends object to the original array" do + a = [1, 2, 3] + a.unshift("a").should.equal?(a) + a.should == ['a', 1, 2, 3] + a.unshift.should.equal?(a) + a.should == ['a', 1, 2, 3] + a.unshift(5, 4, 3) + a.should == [5, 4, 3, 'a', 1, 2, 3] + + # shift all but one element + a = [1, 2] + a.shift + a.unshift(3, 4) + a.should == [3, 4, 2] + + # now shift all elements + a.shift + a.shift + a.shift + a.unshift(3, 4) + a.should == [3, 4] + end + + it "returns self" do + a = [1, 2, 3] + a.unshift("a").should.equal?(a) + end + + it "quietly ignores unshifting nothing" do + [].unshift.should == [] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.unshift(:new).should == [:new, empty] + + array = ArraySpecs.recursive_array + array.unshift(:new) + array[0..5].should == [:new, 1, 'two', 3.0, array, array] + end + + it "raises a FrozenError on a frozen array when the array is modified" do + -> { ArraySpecs.frozen_array.unshift(1) }.should.raise(FrozenError) + end + + # see [ruby-core:23666] + it "raises a FrozenError on a frozen array when the array would not be modified" do + -> { ArraySpecs.frozen_array.unshift }.should.raise(FrozenError) + end + + # https://github.com/truffleruby/truffleruby/issues/2772 + it "doesn't rely on Array#[]= so it can be overridden" do + subclass = Class.new(Array) do + def []=(*) + raise "[]= is called" + end + end + + array = subclass.new + array.unshift(1) + array.should == [1] + end end diff --git a/spec/ruby/core/complex/abs_spec.rb b/spec/ruby/core/complex/abs_spec.rb index 43912c517f1314..ed5aebb11d411b 100644 --- a/spec/ruby/core/complex/abs_spec.rb +++ b/spec/ruby/core/complex/abs_spec.rb @@ -1,6 +1,12 @@ require_relative '../../spec_helper' -require_relative 'shared/abs' describe "Complex#abs" do - it_behaves_like :complex_abs, :abs + it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do + Complex(0, 0).abs.should == 0 + Complex(3, 4).abs.should == 5 # well-known integer case + Complex(-3, 4).abs.should == 5 + Complex(1, -1).abs.should be_close(Math.sqrt(2), TOLERANCE) + Complex(6.5, 0).abs.should be_close(6.5, TOLERANCE) + Complex(0, -7.2).abs.should be_close(7.2, TOLERANCE) + end end diff --git a/spec/ruby/core/complex/angle_spec.rb b/spec/ruby/core/complex/angle_spec.rb index 4aa176956fa5f4..7551214d2bdf74 100644 --- a/spec/ruby/core/complex/angle_spec.rb +++ b/spec/ruby/core/complex/angle_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Complex#angle" do - it_behaves_like :complex_arg, :angle + it "is an alias of Complex#arg" do + Complex.instance_method(:angle).should == Complex.instance_method(:arg) + end end diff --git a/spec/ruby/core/complex/arg_spec.rb b/spec/ruby/core/complex/arg_spec.rb index 009f19429fffdc..dd64102d77ab4e 100644 --- a/spec/ruby/core/complex/arg_spec.rb +++ b/spec/ruby/core/complex/arg_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Complex#arg" do - it_behaves_like :complex_arg, :arg + it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do + two_pi = 2 * Math::PI + (Complex(1, 0).arg % two_pi).should be_close(0, TOLERANCE) + (Complex(0, 2).arg % two_pi).should be_close(Math::PI * 0.5, TOLERANCE) + (Complex(-100, 0).arg % two_pi).should be_close(Math::PI, TOLERANCE) + (Complex(0, -75.3).arg % two_pi).should be_close(Math::PI * 1.5, TOLERANCE) + end end diff --git a/spec/ruby/core/complex/conj_spec.rb b/spec/ruby/core/complex/conj_spec.rb index 5e3bc1acb8c47a..063c85faec31c7 100644 --- a/spec/ruby/core/complex/conj_spec.rb +++ b/spec/ruby/core/complex/conj_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/conjugate' describe "Complex#conj" do - it_behaves_like :complex_conjugate, :conj + it "is an alias of Complex#conjugate" do + Complex.instance_method(:conj).should == Complex.instance_method(:conjugate) + end end diff --git a/spec/ruby/core/complex/conjugate_spec.rb b/spec/ruby/core/complex/conjugate_spec.rb index f658bab4da7d80..256fe4b3be1420 100644 --- a/spec/ruby/core/complex/conjugate_spec.rb +++ b/spec/ruby/core/complex/conjugate_spec.rb @@ -1,6 +1,10 @@ require_relative '../../spec_helper' -require_relative 'shared/conjugate' describe "Complex#conjugate" do - it_behaves_like :complex_conjugate, :conjugate + it "returns the complex conjugate: conj a + bi = a - bi" do + Complex(3, 5).conjugate.should == Complex(3, -5) + Complex(3, -5).conjugate.should == Complex(3, 5) + Complex(-3.0, 5.2).conjugate.should be_close(Complex(-3.0, -5.2), TOLERANCE) + Complex(3.0, -5.2).conjugate.should be_close(Complex(3.0, 5.2), TOLERANCE) + end end diff --git a/spec/ruby/core/complex/divide_spec.rb b/spec/ruby/core/complex/divide_spec.rb index bebf862312515b..d5b4aa38854ad2 100644 --- a/spec/ruby/core/complex/divide_spec.rb +++ b/spec/ruby/core/complex/divide_spec.rb @@ -1,6 +1,84 @@ require_relative '../../spec_helper' -require_relative 'shared/divide' describe "Complex#/" do - it_behaves_like :complex_divide, :/ + describe "with Complex" do + it "divides according to the usual rule for complex numbers" do + a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10)) + b = Complex(1, 2) + (a / b).should == Complex(10, 20) + + c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2)) + d = Complex(1.5, 2.1) + # remember the floating-point arithmetic + (c / d).should be_close(Complex(100.2, -30.3), TOLERANCE) + end + end + + describe "with Fixnum" do + it "divides both parts of the Complex number" do + (Complex(20, 40) / 2).should == Complex(10, 20) + (Complex(30, 30) / 10).should == Complex(3, 3) + end + + it "raises a ZeroDivisionError when given zero" do + -> { Complex(20, 40) / 0 }.should.raise(ZeroDivisionError) + end + + it "produces Rational parts" do + (Complex(5, 9) / 2).should.eql?(Complex(Rational(5,2), Rational(9,2))) + end + end + + describe "with Bignum" do + it "divides both parts of the Complex number" do + (Complex(20, 40) / 2).should == Complex(10, 20) + (Complex(15, 16) / 2.0).should be_close(Complex(7.5, 8), TOLERANCE) + end + end + + describe "with Float" do + it "divides both parts of the Complex number" do + (Complex(3, 9) / 1.5).should == Complex(2, 6) + (Complex(15, 16) / 2.0).should be_close(Complex(7.5, 8), TOLERANCE) + end + + it "returns Complex(Infinity, Infinity) when given zero" do + (Complex(20, 40) / 0.0).real.infinite?.should == 1 + (Complex(20, 40) / 0.0).imag.infinite?.should == 1 + (Complex(-20, 40) / 0.0).real.infinite?.should == -1 + (Complex(-20, 40) / 0.0).imag.infinite?.should == 1 + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([4, 2]) + (value / obj).should == 2 + end + end + + describe "with a Numeric which responds to #real? with true" do + it "returns Complex(real.quo(other), imag.quo(other))" do + other = mock_numeric('other') + real = mock_numeric('real') + imag = mock_numeric('imag') + other.should_receive(:real?).and_return(true) + real.should_receive(:quo).with(other).and_return(1) + imag.should_receive(:quo).with(other).and_return(2) + (Complex(real, imag) / other).should == Complex(1, 2) + end + end + + describe "with a Numeric which responds to #real? with false" do + it "coerces the passed argument to Complex and divides the resulting elements" do + complex = Complex(3, 0) + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(false) + other.should_receive(:coerce).with(complex).and_return([5, 2]) + (complex / other).should.eql?(Rational(5, 2)) + end + end end diff --git a/spec/ruby/core/complex/imag_spec.rb b/spec/ruby/core/complex/imag_spec.rb index 2bafd1ab545d01..225f168e78ab0e 100644 --- a/spec/ruby/core/complex/imag_spec.rb +++ b/spec/ruby/core/complex/imag_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/image' describe "Complex#imag" do - it_behaves_like :complex_image, :imag + it "is an alias of Complex#imaginary" do + Complex.instance_method(:imag).should == Complex.instance_method(:imaginary) + end end diff --git a/spec/ruby/core/complex/imaginary_spec.rb b/spec/ruby/core/complex/imaginary_spec.rb index a8a1bfea90fd7a..ac9284e934e639 100644 --- a/spec/ruby/core/complex/imaginary_spec.rb +++ b/spec/ruby/core/complex/imaginary_spec.rb @@ -1,6 +1,10 @@ require_relative '../../spec_helper' -require_relative 'shared/image' describe "Complex#imaginary" do - it_behaves_like :complex_image, :imaginary + it "returns the imaginary part of self" do + Complex(1, 0).imaginary.should == 0 + Complex(2, 1).imaginary.should == 1 + Complex(6.7, 8.9).imaginary.should == 8.9 + Complex(1, bignum_value).imaginary.should == bignum_value + end end diff --git a/spec/ruby/core/complex/magnitude_spec.rb b/spec/ruby/core/complex/magnitude_spec.rb index 86f3b29868fb3c..6341b4eec8f193 100644 --- a/spec/ruby/core/complex/magnitude_spec.rb +++ b/spec/ruby/core/complex/magnitude_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/abs' describe "Complex#magnitude" do - it_behaves_like :complex_abs, :magnitude + it "is an alias of Complex#abs" do + Complex.instance_method(:magnitude).should == Complex.instance_method(:abs) + end end diff --git a/spec/ruby/core/complex/phase_spec.rb b/spec/ruby/core/complex/phase_spec.rb index 89574bf533bf19..2ab90989e12f35 100644 --- a/spec/ruby/core/complex/phase_spec.rb +++ b/spec/ruby/core/complex/phase_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Complex#phase" do - it_behaves_like :complex_arg, :phase + it "is an alias of Complex#arg" do + Complex.instance_method(:phase).should == Complex.instance_method(:arg) + end end diff --git a/spec/ruby/core/complex/quo_spec.rb b/spec/ruby/core/complex/quo_spec.rb index ee6fd65c79e7a9..be0a44d5321633 100644 --- a/spec/ruby/core/complex/quo_spec.rb +++ b/spec/ruby/core/complex/quo_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/divide' describe "Complex#quo" do - it_behaves_like :complex_divide, :quo + it "is an alias of Complex#/" do + Complex.instance_method(:quo).should == Complex.instance_method(:/) + end end diff --git a/spec/ruby/core/complex/rect_spec.rb b/spec/ruby/core/complex/rect_spec.rb index 9e95f3efc264a3..72f2bf9930bf67 100644 --- a/spec/ruby/core/complex/rect_spec.rb +++ b/spec/ruby/core/complex/rect_spec.rb @@ -1,10 +1,13 @@ require_relative '../../spec_helper' -require_relative 'shared/rect' describe "Complex#rect" do - it_behaves_like :complex_rect, :rect + it "is an alias of Complex#rectangular" do + Complex.instance_method(:rect).should == Complex.instance_method(:rectangular) + end end describe "Complex.rect" do - it_behaves_like :complex_rect_class, :rect + it "is an alias of Complex#rectangular" do + Complex.method(:rect).should == Complex.method(:rectangular) + end end diff --git a/spec/ruby/core/complex/rectangular_spec.rb b/spec/ruby/core/complex/rectangular_spec.rb index d4b8ad9782a6e7..7789bf925eddfe 100644 --- a/spec/ruby/core/complex/rectangular_spec.rb +++ b/spec/ruby/core/complex/rectangular_spec.rb @@ -1,10 +1,114 @@ require_relative '../../spec_helper' -require_relative 'shared/rect' describe "Complex#rectangular" do - it_behaves_like :complex_rect, :rectangular + before :each do + @numbers = [ + Complex(1), + Complex(0, 20), + Complex(0, 0), + Complex(0.0), + Complex(9999999**99), + Complex(-20), + Complex.polar(76, 10) + ] + end + + it "returns an Array" do + @numbers.each do |number| + number.rectangular.should.instance_of?(Array) + end + end + + it "returns a two-element Array" do + @numbers.each do |number| + number.rectangular.size.should == 2 + end + end + + it "returns the real part of self as the first element" do + @numbers.each do |number| + number.rectangular.first.should == number.real + end + end + + it "returns the imaginary part of self as the last element" do + @numbers.each do |number| + number.rectangular.last.should == number.imaginary + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + -> { number.rectangular(number) }.should.raise(ArgumentError) + end + end end describe "Complex.rectangular" do - it_behaves_like :complex_rect_class, :rectangular + describe "passed a Numeric n which responds to #real? with true" do + it "returns a Complex with real part n and imaginary part 0" do + n = mock_numeric('n') + n.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex.rectangular(n) + result.real.should == n + result.imag.should == 0 + end + end + + describe "passed a Numeric which responds to #real? with false" do + it "raises TypeError" do + n = mock_numeric('n') + n.should_receive(:real?).any_number_of_times.and_return(false) + -> { Complex.rectangular(n) }.should.raise(TypeError) + end + end + + describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do + [[false, false], [false, true], [true, false]].each do |r1, r2| + it "raises TypeError" do + n1 = mock_numeric('n1') + n2 = mock_numeric('n2') + n1.should_receive(:real?).any_number_of_times.and_return(r1) + n2.should_receive(:real?).any_number_of_times.and_return(r2) + -> { Complex.rectangular(n1, n2) }.should.raise(TypeError) + end + end + end + + describe "passed Numerics n1 and n2 and both respond to #real? with true" do + it "returns a Complex with real part n1 and imaginary part n2" do + n1 = mock_numeric('n1') + n2 = mock_numeric('n2') + n1.should_receive(:real?).any_number_of_times.and_return(true) + n2.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex.rectangular(n1, n2) + result.real.should == n1 + result.imag.should == n2 + end + end + + describe "when passed a Complex" do + it "raises a TypeError when the imaginary part is not zero" do + -> { + Complex.rectangular(1.0+1i, 2) + }.should.raise(TypeError) + + -> { + Complex.rectangular(1.0, 2i) + }.should.raise(TypeError) + end + + it "ignores the imaginary part if it is zero" do + result = Complex.rectangular(1.0+0i, 2+0.0i) + result.real.should == 1.0 + result.imag.should == 2 + end + end + + describe "passed a non-Numeric" do + it "raises TypeError" do + -> { Complex.rectangular(:sym) }.should.raise(TypeError) + -> { Complex.rectangular(0, :sym) }.should.raise(TypeError) + end + end end diff --git a/spec/ruby/core/complex/shared/abs.rb b/spec/ruby/core/complex/shared/abs.rb deleted file mode 100644 index 22994793415bca..00000000000000 --- a/spec/ruby/core/complex/shared/abs.rb +++ /dev/null @@ -1,10 +0,0 @@ -describe :complex_abs, shared: true do - it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do - Complex(0, 0).send(@method).should == 0 - Complex(3, 4).send(@method).should == 5 # well-known integer case - Complex(-3, 4).send(@method).should == 5 - Complex(1, -1).send(@method).should be_close(Math.sqrt(2), TOLERANCE) - Complex(6.5, 0).send(@method).should be_close(6.5, TOLERANCE) - Complex(0, -7.2).send(@method).should be_close(7.2, TOLERANCE) - end -end diff --git a/spec/ruby/core/complex/shared/arg.rb b/spec/ruby/core/complex/shared/arg.rb deleted file mode 100644 index c81f1974339dbe..00000000000000 --- a/spec/ruby/core/complex/shared/arg.rb +++ /dev/null @@ -1,9 +0,0 @@ -describe :complex_arg, shared: true do - it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do - two_pi = 2 * Math::PI - (Complex(1, 0).send(@method) % two_pi).should be_close(0, TOLERANCE) - (Complex(0, 2).send(@method) % two_pi).should be_close(Math::PI * 0.5, TOLERANCE) - (Complex(-100, 0).send(@method) % two_pi).should be_close(Math::PI, TOLERANCE) - (Complex(0, -75.3).send(@method) % two_pi).should be_close(Math::PI * 1.5, TOLERANCE) - end -end diff --git a/spec/ruby/core/complex/shared/conjugate.rb b/spec/ruby/core/complex/shared/conjugate.rb deleted file mode 100644 index d1ae47bcb678b6..00000000000000 --- a/spec/ruby/core/complex/shared/conjugate.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :complex_conjugate, shared: true do - it "returns the complex conjugate: conj a + bi = a - bi" do - Complex(3, 5).send(@method).should == Complex(3, -5) - Complex(3, -5).send(@method).should == Complex(3, 5) - Complex(-3.0, 5.2).send(@method).should be_close(Complex(-3.0, -5.2), TOLERANCE) - Complex(3.0, -5.2).send(@method).should be_close(Complex(3.0, 5.2), TOLERANCE) - end -end diff --git a/spec/ruby/core/complex/shared/divide.rb b/spec/ruby/core/complex/shared/divide.rb deleted file mode 100644 index ef79ecdf751fa0..00000000000000 --- a/spec/ruby/core/complex/shared/divide.rb +++ /dev/null @@ -1,82 +0,0 @@ -describe :complex_divide, shared: true do - describe "with Complex" do - it "divides according to the usual rule for complex numbers" do - a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10)) - b = Complex(1, 2) - a.send(@method, b).should == Complex(10, 20) - - c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2)) - d = Complex(1.5, 2.1) - # remember the floating-point arithmetic - c.send(@method, d).should be_close(Complex(100.2, -30.3), TOLERANCE) - end - end - - describe "with Fixnum" do - it "divides both parts of the Complex number" do - Complex(20, 40).send(@method, 2).should == Complex(10, 20) - Complex(30, 30).send(@method, 10).should == Complex(3, 3) - end - - it "raises a ZeroDivisionError when given zero" do - -> { Complex(20, 40).send(@method, 0) }.should.raise(ZeroDivisionError) - end - - it "produces Rational parts" do - Complex(5, 9).send(@method, 2).should.eql?(Complex(Rational(5,2), Rational(9,2))) - end - end - - describe "with Bignum" do - it "divides both parts of the Complex number" do - Complex(20, 40).send(@method, 2).should == Complex(10, 20) - Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE) - end - end - - describe "with Float" do - it "divides both parts of the Complex number" do - Complex(3, 9).send(@method, 1.5).should == Complex(2, 6) - Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE) - end - - it "returns Complex(Infinity, Infinity) when given zero" do - Complex(20, 40).send(@method, 0.0).real.infinite?.should == 1 - Complex(20, 40).send(@method, 0.0).imag.infinite?.should == 1 - Complex(-20, 40).send(@method, 0.0).real.infinite?.should == -1 - Complex(-20, 40).send(@method, 0.0).imag.infinite?.should == 1 - end - end - - describe "with Object" do - it "tries to coerce self into other" do - value = Complex(3, 9) - - obj = mock("Object") - obj.should_receive(:coerce).with(value).and_return([4, 2]) - value.send(@method, obj).should == 2 - end - end - - describe "with a Numeric which responds to #real? with true" do - it "returns Complex(real.quo(other), imag.quo(other))" do - other = mock_numeric('other') - real = mock_numeric('real') - imag = mock_numeric('imag') - other.should_receive(:real?).and_return(true) - real.should_receive(:quo).with(other).and_return(1) - imag.should_receive(:quo).with(other).and_return(2) - Complex(real, imag).send(@method, other).should == Complex(1, 2) - end - end - - describe "with a Numeric which responds to #real? with false" do - it "coerces the passed argument to Complex and divides the resulting elements" do - complex = Complex(3, 0) - other = mock_numeric('other') - other.should_receive(:real?).any_number_of_times.and_return(false) - other.should_receive(:coerce).with(complex).and_return([5, 2]) - complex.send(@method, other).should.eql?(Rational(5, 2)) - end - end -end diff --git a/spec/ruby/core/complex/shared/image.rb b/spec/ruby/core/complex/shared/image.rb deleted file mode 100644 index f839dbcaf909fb..00000000000000 --- a/spec/ruby/core/complex/shared/image.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :complex_image, shared: true do - it "returns the imaginary part of self" do - Complex(1, 0).send(@method).should == 0 - Complex(2, 1).send(@method).should == 1 - Complex(6.7, 8.9).send(@method).should == 8.9 - Complex(1, bignum_value).send(@method).should == bignum_value - end -end diff --git a/spec/ruby/core/complex/shared/rect.rb b/spec/ruby/core/complex/shared/rect.rb deleted file mode 100644 index 858234961b648b..00000000000000 --- a/spec/ruby/core/complex/shared/rect.rb +++ /dev/null @@ -1,94 +0,0 @@ -describe :complex_rect, shared: true do - before :each do - @numbers = [ - Complex(1), - Complex(0, 20), - Complex(0, 0), - Complex(0.0), - Complex(9999999**99), - Complex(-20), - Complex.polar(76, 10) - ] - end - - it "returns an Array" do - @numbers.each do |number| - number.send(@method).should.instance_of?(Array) - end - end - - it "returns a two-element Array" do - @numbers.each do |number| - number.send(@method).size.should == 2 - end - end - - it "returns the real part of self as the first element" do - @numbers.each do |number| - number.send(@method).first.should == number.real - end - end - - it "returns the imaginary part of self as the last element" do - @numbers.each do |number| - number.send(@method).last.should == number.imaginary - end - end - - it "raises an ArgumentError if given any arguments" do - @numbers.each do |number| - -> { number.send(@method, number) }.should.raise(ArgumentError) - end - end -end - -describe :complex_rect_class, shared: true do - describe "passed a Numeric n which responds to #real? with true" do - it "returns a Complex with real part n and imaginary part 0" do - n = mock_numeric('n') - n.should_receive(:real?).any_number_of_times.and_return(true) - result = Complex.send(@method, n) - result.real.should == n - result.imag.should == 0 - end - end - - describe "passed a Numeric which responds to #real? with false" do - it "raises TypeError" do - n = mock_numeric('n') - n.should_receive(:real?).any_number_of_times.and_return(false) - -> { Complex.send(@method, n) }.should.raise(TypeError) - end - end - - describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do - [[false, false], [false, true], [true, false]].each do |r1, r2| - it "raises TypeError" do - n1 = mock_numeric('n1') - n2 = mock_numeric('n2') - n1.should_receive(:real?).any_number_of_times.and_return(r1) - n2.should_receive(:real?).any_number_of_times.and_return(r2) - -> { Complex.send(@method, n1, n2) }.should.raise(TypeError) - end - end - end - - describe "passed Numerics n1 and n2 and both respond to #real? with true" do - it "returns a Complex with real part n1 and imaginary part n2" do - n1 = mock_numeric('n1') - n2 = mock_numeric('n2') - n1.should_receive(:real?).any_number_of_times.and_return(true) - n2.should_receive(:real?).any_number_of_times.and_return(true) - result = Complex.send(@method, n1, n2) - result.real.should == n1 - result.imag.should == n2 - end - end - - describe "passed a non-Numeric" do - it "raises TypeError" do - -> { Complex.send(@method, :sym) }.should.raise(TypeError) - -> { Complex.send(@method, 0, :sym) }.should.raise(TypeError) - end - end -end diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb index 53f2334546a25c..7e81f966ea0c0f 100644 --- a/spec/ruby/core/data/deconstruct_keys_spec.rb +++ b/spec/ruby/core/data/deconstruct_keys_spec.rb @@ -56,7 +56,7 @@ d.deconstruct_keys(nil).should == {x: 1, y: 2} end - ruby_bug "Bug #21844", ""..."4.1" do + ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844 it "tries to convert a key with #to_str if index is not a String nor a Symbol, but responds to #to_str" do klass = Data.define(:x, :y) d = klass.new(1, 2) diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb index 63d49e9c84da56..0320ca880c7317 100644 --- a/spec/ruby/core/data/initialize_spec.rb +++ b/spec/ruby/core/data/initialize_spec.rb @@ -47,6 +47,12 @@ data.unit.should == "km" end + it "accepts the last entry when a keyword is given as both String and Symbol" do + data = DataSpecs::Single.new("value" => -1, value: 42) + + data.value.should == 42 + end + it "accepts positional arguments with empty keyword arguments" do data = DataSpecs::Single.new(42, **{}) @@ -74,6 +80,16 @@ } end + ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844 + it "raises ArgumentError if at least one argument is missing and other is provided as both String and Symbol" do + -> { + DataSpecs::Measure.new(unit: "km", "unit" => "km") + }.should.raise(ArgumentError) { |e| + e.message.should.include?("missing keyword: :amount") + } + end + end + it "raises ArgumentError if unknown keyword is given" do -> { DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric") @@ -82,6 +98,38 @@ } end + ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844 + it "raises ArgumentError if unknown keyword is given which is convertable to String" do + key = mock("to_str") + key.should_receive(:to_str).and_return("system") + + -> { + DataSpecs::Measure.new(amount: 42, unit: "km", key => "metric") + }.should.raise(ArgumentError) { |e| + e.message.should.include?('unknown keyword: "system"') + } + end + + it "raises TypeError when the keyword is not convertable to String" do + -> { + DataSpecs::Measure.new(1 => 2) + }.should.raise(TypeError) { |e| + e.message.should == "1 is not a symbol nor a string" + } + end + + it "raises TypeError if the conversion with #to_str does not return a String" do + klass = Data.define(:x, :y) + + key = mock("to_str") + key.should_receive(:to_str).and_return(0) + + -> { + klass.new(key => 2) + }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) + end + end + it "supports super from a subclass" do ms = DataSpecs::MeasureSubclass.new(amount: 1, unit: "km") diff --git a/spec/ruby/core/data/inspect_spec.rb b/spec/ruby/core/data/inspect_spec.rb index 38642910a04563..6c97a719e3c51d 100644 --- a/spec/ruby/core/data/inspect_spec.rb +++ b/spec/ruby/core/data/inspect_spec.rb @@ -1,6 +1,63 @@ require_relative '../../spec_helper' -require_relative 'shared/inspect' +require_relative 'fixtures/classes' describe "Data#inspect" do - it_behaves_like :data_inspect, :inspect + it "returns a string representation showing members and values" do + a = DataSpecs::Measure.new(42, "km") + a.inspect.should == '#' + end + + it "returns a string representation without the class name for anonymous structs" do + Data.define(:a).new("").inspect.should == '#' + end + + it "returns a string representation without the class name for structs nested in anonymous classes" do + c = Class.new + c.class_eval <<~DOC + Foo = Data.define(:a) + DOC + + c::Foo.new("").inspect.should == '#' + end + + it "returns a string representation without the class name for structs nested in anonymous modules" do + m = Module.new + m.class_eval <<~DOC + Foo = Data.define(:a) + DOC + + m::Foo.new("").inspect.should == '#' + end + + it "does not call #name method" do + struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") + struct.inspect.should == '#' + end + + it "does not call #name method when struct is anonymous" do + klass = Class.new(DataSpecs::Measure) do + def self.name + "A" + end + end + struct = klass.new(42, "km") + struct.inspect.should == '#' + end + + context "recursive structure" do + it "returns string representation with recursive attribute replaced with ..." do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + a.inspect.should == "#>" + end + + it "returns string representation with recursive attribute replaced with ... when an anonymous class" do + klass = Class.new(DataSpecs::Measure) + a = klass.allocate + a.send(:initialize, amount: 42, unit: a) + + a.inspect.should =~ /#:\.\.\.>>/ + end + end end diff --git a/spec/ruby/core/data/shared/inspect.rb b/spec/ruby/core/data/shared/inspect.rb deleted file mode 100644 index 6cd5664da7757e..00000000000000 --- a/spec/ruby/core/data/shared/inspect.rb +++ /dev/null @@ -1,62 +0,0 @@ -require_relative '../fixtures/classes' - -describe :data_inspect, shared: true do - it "returns a string representation showing members and values" do - a = DataSpecs::Measure.new(42, "km") - a.send(@method).should == '#' - end - - it "returns a string representation without the class name for anonymous structs" do - Data.define(:a).new("").send(@method).should == '#' - end - - it "returns a string representation without the class name for structs nested in anonymous classes" do - c = Class.new - c.class_eval <<~DOC - Foo = Data.define(:a) - DOC - - c::Foo.new("").send(@method).should == '#' - end - - it "returns a string representation without the class name for structs nested in anonymous modules" do - m = Module.new - m.class_eval <<~DOC - Foo = Data.define(:a) - DOC - - m::Foo.new("").send(@method).should == '#' - end - - it "does not call #name method" do - struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") - struct.send(@method).should == '#' - end - - it "does not call #name method when struct is anonymous" do - klass = Class.new(DataSpecs::Measure) do - def self.name - "A" - end - end - struct = klass.new(42, "km") - struct.send(@method).should == '#' - end - - context "recursive structure" do - it "returns string representation with recursive attribute replaced with ..." do - a = DataSpecs::Measure.allocate - a.send(:initialize, amount: 42, unit: a) - - a.send(@method).should == "#>" - end - - it "returns string representation with recursive attribute replaced with ... when an anonymous class" do - klass = Class.new(DataSpecs::Measure) - a = klass.allocate - a.send(:initialize, amount: 42, unit: a) - - a.send(@method).should =~ /#:\.\.\.>>/ - end - end -end diff --git a/spec/ruby/core/data/to_s_spec.rb b/spec/ruby/core/data/to_s_spec.rb index 2b4a670e8e301b..a552e4659b8833 100644 --- a/spec/ruby/core/data/to_s_spec.rb +++ b/spec/ruby/core/data/to_s_spec.rb @@ -1,6 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/inspect' +require_relative 'fixtures/classes' describe "Data#to_s" do - it_behaves_like :data_inspect, :to_s + it "is an alias of Data#inspect" do + a = DataSpecs::Measure.new(42, "km") + a.method(:to_s).should == a.method(:inspect) + end end diff --git a/spec/ruby/core/dir/exist_spec.rb b/spec/ruby/core/dir/exist_spec.rb index 0b8e521894d5e1..05ad67dd030e90 100644 --- a/spec/ruby/core/dir/exist_spec.rb +++ b/spec/ruby/core/dir/exist_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/exist' describe "Dir.exist?" do before :all do @@ -11,7 +10,61 @@ DirSpecs.delete_mock_dirs end - it_behaves_like :dir_exist, :exist? + it "returns true if the given directory exists" do + Dir.exist?(__dir__).should == true + end + + it "returns true for '.'" do + Dir.exist?('.').should == true + end + + it "returns true for '..'" do + Dir.exist?('..').should == true + end + + it "understands non-ASCII paths" do + subdir = File.join(tmp("\u{9876}\u{665}")) + Dir.exist?(subdir).should == false + Dir.mkdir(subdir) + Dir.exist?(subdir).should == true + Dir.rmdir(subdir) + end + + it "understands relative paths" do + Dir.exist?(__dir__ + '/../').should == true + end + + it "returns false if the given directory doesn't exist" do + Dir.exist?('y26dg27n2nwjs8a/').should == false + end + + it "doesn't require the name to have a trailing slash" do + dir = __dir__ + dir.sub!(/\/$/,'') + Dir.exist?(dir).should == true + end + + it "doesn't expand paths" do + skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) + Dir.exist?(File.expand_path('~')).should == true + Dir.exist?('~').should == false + end + + it "returns false if the argument exists but is a file" do + File.should.exist?(__FILE__) + Dir.exist?(__FILE__).should == false + end + + it "doesn't set $! when file doesn't exist" do + Dir.exist?("/path/to/non/existent/dir") + $!.should == nil + end + + it "calls #to_path on non String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(__dir__) + Dir.exist?(p) + end end describe "Dir.exists?" do diff --git a/spec/ruby/core/dir/getwd_spec.rb b/spec/ruby/core/dir/getwd_spec.rb index 132634347c4ea0..138481821f3dee 100644 --- a/spec/ruby/core/dir/getwd_spec.rb +++ b/spec/ruby/core/dir/getwd_spec.rb @@ -1,15 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/common' -require_relative 'shared/pwd' describe "Dir.getwd" do - before :all do - DirSpecs.create_mock_dirs + it "is an alias of Dir.pwd" do + Dir.method(:getwd).should == Dir.method(:pwd) end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_pwd, :getwd end diff --git a/spec/ruby/core/dir/open_spec.rb b/spec/ruby/core/dir/open_spec.rb index 27f362320b194b..be01638fbc5fa1 100644 --- a/spec/ruby/core/dir/open_spec.rb +++ b/spec/ruby/core/dir/open_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/open' describe "Dir.open" do before :all do @@ -11,5 +10,75 @@ DirSpecs.delete_mock_dirs end - it_behaves_like :dir_open, :open + it "returns a Dir instance representing the specified directory" do + dir = Dir.open(DirSpecs.mock_dir) + dir.should.is_a?(Dir) + dir.close + end + + it "raises a SystemCallError if the directory does not exist" do + -> do + Dir.open(DirSpecs.nonexistent) + end.should.raise(SystemCallError) + end + + it "may take a block which is yielded to with the Dir instance" do + Dir.open(DirSpecs.mock_dir) {|dir| dir.should.is_a?(Dir)} + end + + it "returns the value of the block if a block is given" do + Dir.open(DirSpecs.mock_dir) {|dir| :value }.should == :value + end + + it "closes the Dir instance when the block exits if given a block" do + closed_dir = Dir.open(DirSpecs.mock_dir) { |dir| dir } + -> { closed_dir.read }.should.raise(IOError) + end + + it "closes the Dir instance when the block exits the block even due to an exception" do + closed_dir = nil + + -> do + Dir.open(DirSpecs.mock_dir) do |dir| + closed_dir = dir + raise "dir specs" + end + end.should.raise(RuntimeError, "dir specs") + + -> { closed_dir.read }.should.raise(IOError) + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.open(p) { true } + end + + it "accepts an options Hash" do + dir = Dir.open(DirSpecs.mock_dir, encoding: "utf-8") {|d| d } + dir.should.is_a?(Dir) + end + + it "calls #to_hash to convert the options object" do + options = mock("dir_open") + options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 }) + + dir = Dir.open(DirSpecs.mock_dir, **options) {|d| d } + dir.should.is_a?(Dir) + end + + it "ignores the :encoding option if it is nil" do + dir = Dir.open(DirSpecs.mock_dir, encoding: nil) {|d| d } + dir.should.is_a?(Dir) + end + + platform_is_not :windows do + it 'sets the close-on-exec flag for the directory file descriptor' do + Dir.open(DirSpecs.mock_dir) do |dir| + io = IO.for_fd(dir.fileno) + io.autoclose = false + io.should.close_on_exec? + end + end + end end diff --git a/spec/ruby/core/dir/path_spec.rb b/spec/ruby/core/dir/path_spec.rb index b1c24c406b76cd..02ddd2cd4257cd 100644 --- a/spec/ruby/core/dir/path_spec.rb +++ b/spec/ruby/core/dir/path_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/path' describe "Dir#path" do before :all do @@ -11,5 +10,28 @@ DirSpecs.delete_mock_dirs end - it_behaves_like :dir_path, :path + it "returns the path that was supplied to .new or .open" do + dir = Dir.open DirSpecs.mock_dir + begin + dir.path.should == DirSpecs.mock_dir + ensure + dir.close rescue nil + end + end + + it "returns the path even when called on a closed Dir instance" do + dir = Dir.open DirSpecs.mock_dir + dir.close + dir.path.should == DirSpecs.mock_dir + end + + it "returns a String with the same encoding as the argument to .open" do + path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 + dir = Dir.open path + begin + dir.path.encoding.should.equal?(Encoding::IBM866) + ensure + dir.close + end + end end diff --git a/spec/ruby/core/dir/pos_spec.rb b/spec/ruby/core/dir/pos_spec.rb index b382bff81fa81b..f8ca06d7780124 100644 --- a/spec/ruby/core/dir/pos_spec.rb +++ b/spec/ruby/core/dir/pos_spec.rb @@ -1,30 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/closed' require_relative 'shared/pos' describe "Dir#pos" do - before :all do - DirSpecs.create_mock_dirs - end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_pos, :pos -end - -describe "Dir#pos" do - before :all do - DirSpecs.create_mock_dirs + it "is an alias of Dir#tell" do + Dir.instance_method(:pos).should == Dir.instance_method(:tell) end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_closed, :pos end describe "Dir#pos=" do diff --git a/spec/ruby/core/dir/pwd_spec.rb b/spec/ruby/core/dir/pwd_spec.rb index ad01286c9012b9..70208b03d673a1 100644 --- a/spec/ruby/core/dir/pwd_spec.rb +++ b/spec/ruby/core/dir/pwd_spec.rb @@ -1,7 +1,6 @@ # -*- encoding: utf-8 -*- require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/pwd' describe "Dir.pwd" do before :all do @@ -12,7 +11,49 @@ DirSpecs.delete_mock_dirs end - it_behaves_like :dir_pwd, :pwd + before :each do + @fs_encoding = Encoding.find('filesystem') + end + + it "returns the current working directory" do + pwd = Dir.pwd + + File.directory?(pwd).should == true + + # On ubuntu gutsy, for example, /bin/pwd does not + # understand -P. With just `pwd -P`, /bin/pwd is run. + + # The following uses inode rather than file names to account for + # case insensitive file systems like default OS/X file systems + platform_is_not :windows do + File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino + end + platform_is :windows do + File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino + end + end + + it "returns an absolute path" do + pwd = Dir.pwd + pwd.should == File.expand_path(pwd) + end + + it "returns an absolute path even when chdir to a relative path" do + Dir.chdir(".") do + pwd = Dir.pwd + File.directory?(pwd).should == true + pwd.should == File.expand_path(pwd) + end + end + + it "returns a String with the filesystem encoding" do + enc = Dir.pwd.encoding + if @fs_encoding == Encoding::US_ASCII + [Encoding::US_ASCII, Encoding::BINARY].should.include?(enc) + else + enc.should.equal?(@fs_encoding) + end + end end describe "Dir.pwd" do diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb deleted file mode 100644 index 4ceaccea66fe6b..00000000000000 --- a/spec/ruby/core/dir/shared/exist.rb +++ /dev/null @@ -1,57 +0,0 @@ -describe :dir_exist, shared: true do - it "returns true if the given directory exists" do - Dir.send(@method, __dir__).should == true - end - - it "returns true for '.'" do - Dir.send(@method, '.').should == true - end - - it "returns true for '..'" do - Dir.send(@method, '..').should == true - end - - it "understands non-ASCII paths" do - subdir = File.join(tmp("\u{9876}\u{665}")) - Dir.send(@method, subdir).should == false - Dir.mkdir(subdir) - Dir.send(@method, subdir).should == true - Dir.rmdir(subdir) - end - - it "understands relative paths" do - Dir.send(@method, __dir__ + '/../').should == true - end - - it "returns false if the given directory doesn't exist" do - Dir.send(@method, 'y26dg27n2nwjs8a/').should == false - end - - it "doesn't require the name to have a trailing slash" do - dir = __dir__ - dir.sub!(/\/$/,'') - Dir.send(@method, dir).should == true - end - - it "doesn't expand paths" do - skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) - Dir.send(@method, File.expand_path('~')).should == true - Dir.send(@method, '~').should == false - end - - it "returns false if the argument exists but is a file" do - File.should.exist?(__FILE__) - Dir.send(@method, __FILE__).should == false - end - - it "doesn't set $! when file doesn't exist" do - Dir.send(@method, "/path/to/non/existent/dir") - $!.should == nil - end - - it "calls #to_path on non String arguments" do - p = mock('path') - p.should_receive(:to_path).and_return(__dir__) - Dir.send(@method, p) - end -end diff --git a/spec/ruby/core/dir/shared/open.rb b/spec/ruby/core/dir/shared/open.rb deleted file mode 100644 index 9ac3a40694375d..00000000000000 --- a/spec/ruby/core/dir/shared/open.rb +++ /dev/null @@ -1,73 +0,0 @@ -describe :dir_open, shared: true do - it "returns a Dir instance representing the specified directory" do - dir = Dir.send(@method, DirSpecs.mock_dir) - dir.should.is_a?(Dir) - dir.close - end - - it "raises a SystemCallError if the directory does not exist" do - -> do - Dir.send @method, DirSpecs.nonexistent - end.should.raise(SystemCallError) - end - - it "may take a block which is yielded to with the Dir instance" do - Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should.is_a?(Dir)} - end - - it "returns the value of the block if a block is given" do - Dir.send(@method, DirSpecs.mock_dir) {|dir| :value }.should == :value - end - - it "closes the Dir instance when the block exits if given a block" do - closed_dir = Dir.send(@method, DirSpecs.mock_dir) { |dir| dir } - -> { closed_dir.read }.should.raise(IOError) - end - - it "closes the Dir instance when the block exits the block even due to an exception" do - closed_dir = nil - - -> do - Dir.send(@method, DirSpecs.mock_dir) do |dir| - closed_dir = dir - raise "dir specs" - end - end.should.raise(RuntimeError, "dir specs") - - -> { closed_dir.read }.should.raise(IOError) - end - - it "calls #to_path on non-String arguments" do - p = mock('path') - p.should_receive(:to_path).and_return(DirSpecs.mock_dir) - Dir.send(@method, p) { true } - end - - it "accepts an options Hash" do - dir = Dir.send(@method, DirSpecs.mock_dir, encoding: "utf-8") {|d| d } - dir.should.is_a?(Dir) - end - - it "calls #to_hash to convert the options object" do - options = mock("dir_open") - options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 }) - - dir = Dir.send(@method, DirSpecs.mock_dir, **options) {|d| d } - dir.should.is_a?(Dir) - end - - it "ignores the :encoding option if it is nil" do - dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d } - dir.should.is_a?(Dir) - end - - platform_is_not :windows do - it 'sets the close-on-exec flag for the directory file descriptor' do - Dir.send(@method, DirSpecs.mock_dir) do |dir| - io = IO.for_fd(dir.fileno) - io.autoclose = false - io.should.close_on_exec? - end - end - end -end diff --git a/spec/ruby/core/dir/shared/path.rb b/spec/ruby/core/dir/shared/path.rb deleted file mode 100644 index 7647c04421127b..00000000000000 --- a/spec/ruby/core/dir/shared/path.rb +++ /dev/null @@ -1,30 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/common' -require_relative 'closed' - -describe :dir_path, shared: true do - it "returns the path that was supplied to .new or .open" do - dir = Dir.open DirSpecs.mock_dir - begin - dir.send(@method).should == DirSpecs.mock_dir - ensure - dir.close rescue nil - end - end - - it "returns the path even when called on a closed Dir instance" do - dir = Dir.open DirSpecs.mock_dir - dir.close - dir.send(@method).should == DirSpecs.mock_dir - end - - it "returns a String with the same encoding as the argument to .open" do - path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 - dir = Dir.open path - begin - dir.send(@method).encoding.should.equal?(Encoding::IBM866) - ensure - dir.close - end - end -end diff --git a/spec/ruby/core/dir/shared/pos.rb b/spec/ruby/core/dir/shared/pos.rb index 11712cc75df463..741de8918ced50 100644 --- a/spec/ruby/core/dir/shared/pos.rb +++ b/spec/ruby/core/dir/shared/pos.rb @@ -1,30 +1,3 @@ -describe :dir_pos, shared: true do - before :each do - @dir = Dir.open DirSpecs.mock_dir - end - - after :each do - @dir.close rescue nil - end - - it "returns an Integer representing the current position in the directory" do - @dir.send(@method).should.is_a?(Integer) - @dir.send(@method).should.is_a?(Integer) - @dir.send(@method).should.is_a?(Integer) - end - - it "returns a different Integer if moved from previous position" do - a = @dir.send(@method) - @dir.read - b = @dir.send(@method) - - a.should.is_a?(Integer) - b.should.is_a?(Integer) - - a.should_not == b - end -end - describe :dir_pos_set, shared: true do before :each do @dir = Dir.open DirSpecs.mock_dir diff --git a/spec/ruby/core/dir/shared/pwd.rb b/spec/ruby/core/dir/shared/pwd.rb deleted file mode 100644 index ed47fe382af309..00000000000000 --- a/spec/ruby/core/dir/shared/pwd.rb +++ /dev/null @@ -1,45 +0,0 @@ -describe :dir_pwd, shared: true do - before :each do - @fs_encoding = Encoding.find('filesystem') - end - - it "returns the current working directory" do - pwd = Dir.send(@method) - - File.directory?(pwd).should == true - - # On ubuntu gutsy, for example, /bin/pwd does not - # understand -P. With just `pwd -P`, /bin/pwd is run. - - # The following uses inode rather than file names to account for - # case insensitive file systems like default OS/X file systems - platform_is_not :windows do - File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino - end - platform_is :windows do - File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino - end - end - - it "returns an absolute path" do - pwd = Dir.send(@method) - pwd.should == File.expand_path(pwd) - end - - it "returns an absolute path even when chdir to a relative path" do - Dir.chdir(".") do - pwd = Dir.send(@method) - File.directory?(pwd).should == true - pwd.should == File.expand_path(pwd) - end - end - - it "returns a String with the filesystem encoding" do - enc = Dir.send(@method).encoding - if @fs_encoding == Encoding::US_ASCII - [Encoding::US_ASCII, Encoding::BINARY].should.include?(enc) - else - enc.should.equal?(@fs_encoding) - end - end -end diff --git a/spec/ruby/core/dir/tell_spec.rb b/spec/ruby/core/dir/tell_spec.rb index af86dc1598d999..dcbb40438f2b40 100644 --- a/spec/ruby/core/dir/tell_spec.rb +++ b/spec/ruby/core/dir/tell_spec.rb @@ -12,7 +12,30 @@ DirSpecs.delete_mock_dirs end - it_behaves_like :dir_pos, :tell - it_behaves_like :dir_closed, :tell + + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close rescue nil + end + + it "returns an Integer representing the current position in the directory" do + @dir.tell.should.is_a?(Integer) + @dir.tell.should.is_a?(Integer) + @dir.tell.should.is_a?(Integer) + end + + it "returns a different Integer if moved from previous position" do + a = @dir.tell + @dir.read + b = @dir.tell + + a.should.is_a?(Integer) + b.should.is_a?(Integer) + + a.should_not == b + end end diff --git a/spec/ruby/core/dir/to_path_spec.rb b/spec/ruby/core/dir/to_path_spec.rb index 77404a3dc85911..2ed533e757df68 100644 --- a/spec/ruby/core/dir/to_path_spec.rb +++ b/spec/ruby/core/dir/to_path_spec.rb @@ -1,15 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/common' -require_relative 'shared/path' describe "Dir#to_path" do - before :all do - DirSpecs.create_mock_dirs + it "is an alias of Dir#path" do + Dir.instance_method(:to_path).should == Dir.instance_method(:path) end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_path, :to_path end diff --git a/spec/ruby/core/encoding/name_spec.rb b/spec/ruby/core/encoding/name_spec.rb index dce9347978e4e0..1d625c937966f2 100644 --- a/spec/ruby/core/encoding/name_spec.rb +++ b/spec/ruby/core/encoding/name_spec.rb @@ -1,6 +1,15 @@ require_relative "../../spec_helper" -require_relative 'shared/name' describe "Encoding#name" do - it_behaves_like :encoding_name, :name + it "returns a String" do + Encoding.list.each do |e| + e.name.should.instance_of?(String) + end + end + + it "uniquely identifies an encoding" do + Encoding.list.each do |e| + e.should == Encoding.find(e.name) + end + end end diff --git a/spec/ruby/core/encoding/shared/name.rb b/spec/ruby/core/encoding/shared/name.rb deleted file mode 100644 index 4d4b860a1f652f..00000000000000 --- a/spec/ruby/core/encoding/shared/name.rb +++ /dev/null @@ -1,15 +0,0 @@ -require_relative '../../../spec_helper' - -describe :encoding_name, shared: true do - it "returns a String" do - Encoding.list.each do |e| - e.send(@method).should.instance_of?(String) - end - end - - it "uniquely identifies an encoding" do - Encoding.list.each do |e| - e.should == Encoding.find(e.send(@method)) - end - end -end diff --git a/spec/ruby/core/encoding/to_s_spec.rb b/spec/ruby/core/encoding/to_s_spec.rb index bab394a888888f..ee5e3b4abeeb73 100644 --- a/spec/ruby/core/encoding/to_s_spec.rb +++ b/spec/ruby/core/encoding/to_s_spec.rb @@ -1,6 +1,7 @@ require_relative "../../spec_helper" -require_relative 'shared/name' describe "Encoding#to_s" do - it_behaves_like :encoding_name, :to_s + it "is an alias of Encoding#name" do + Encoding.instance_method(:to_s).should == Encoding.instance_method(:name) + end end diff --git a/spec/ruby/core/enumerable/shared/value_packing.rb b/spec/ruby/core/enumerable/shared/value_packing.rb new file mode 100644 index 00000000000000..ff77f45cdfe156 --- /dev/null +++ b/spec/ruby/core/enumerable/shared/value_packing.rb @@ -0,0 +1,26 @@ +# This is the behavior of rb_enum_values_pack() in CRuby +describe :enumerable_value_packing, shared: true do + # @take must be set to a Proc that returns the take-result whose #each + # yields packed values (e.g. -> e { e.take(1) } or -> e { e.lazy.take(1) }). + + it "yields a single nil for a zero-argument source yield" do + e = Enumerator.new { |y| y.yield } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [nil] + end + + it "yields the value for a single-argument source yield" do + e = Enumerator.new { |y| y.yield :v } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [:v] + end + + it "yields a packed Array for a multi-argument source yield" do + e = Enumerator.new { |y| y.yield 1, 2 } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [[1, 2]] + end +end diff --git a/spec/ruby/core/enumerable/take_spec.rb b/spec/ruby/core/enumerable/take_spec.rb index 8cc746f88d9edd..ca439b750d5715 100644 --- a/spec/ruby/core/enumerable/take_spec.rb +++ b/spec/ruby/core/enumerable/take_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/take' +require_relative 'shared/value_packing' describe "Enumerable#take" do it "requires an argument" do @@ -10,4 +11,11 @@ describe "when passed an argument" do it_behaves_like :enumerable_take, :take end + + describe "value packing of source yields" do + before :each do + @take = -> e { e.take(1) } + end + it_behaves_like :enumerable_value_packing, nil + end end diff --git a/spec/ruby/core/enumerator/generator/each_spec.rb b/spec/ruby/core/enumerator/generator/each_spec.rb deleted file mode 100644 index 41a494298b8817..00000000000000 --- a/spec/ruby/core/enumerator/generator/each_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Enumerator::Generator#each" do - before :each do - @generator = Enumerator::Generator.new do |y, *args| - y << 3 << 2 << 1 - y << args unless args.empty? - :block_returned - end - end - - it "is an enumerable" do - @generator.should.is_a?(Enumerable) - end - - it "supports enumeration with a block" do - r = [] - @generator.each { |v| r << v } - - r.should == [3, 2, 1] - end - - it "raises a LocalJumpError if no block given" do - -> { @generator.each }.should.raise(LocalJumpError) - end - - it "returns the block returned value" do - @generator.each {}.should.equal?(:block_returned) - end - - it "requires multiple arguments" do - Enumerator::Generator.instance_method(:each).arity.should < 0 - end - - it "appends given arguments to receiver.each" do - yields = [] - @generator.each(:each0, :each1) { |yielded| yields << yielded } - yields.should == [3, 2, 1, [:each0, :each1]] - end -end diff --git a/spec/ruby/core/enumerator/generator/initialize_spec.rb b/spec/ruby/core/enumerator/generator/initialize_spec.rb deleted file mode 100644 index 0f77d591d90fc2..00000000000000 --- a/spec/ruby/core/enumerator/generator/initialize_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../spec_helper' - -describe "Enumerator::Generator#initialize" do - before :each do - @class = Enumerator::Generator - @uninitialized = @class.allocate - end - - it "is a private method" do - @class.private_instance_methods(false).should.include?(:initialize) - end - - it "returns self when given a block" do - @uninitialized.send(:initialize) {}.should.equal?(@uninitialized) - end - - describe "on frozen instance" do - it "raises a FrozenError" do - -> { - @uninitialized.freeze.send(:initialize) {} - }.should.raise(FrozenError) - end - end -end diff --git a/spec/ruby/core/enumerator/lazy/take_spec.rb b/spec/ruby/core/enumerator/lazy/take_spec.rb index f92360f543e7ef..2dd5b939e2b4b0 100644 --- a/spec/ruby/core/enumerator/lazy/take_spec.rb +++ b/spec/ruby/core/enumerator/lazy/take_spec.rb @@ -2,8 +2,16 @@ require_relative '../../../spec_helper' require_relative 'fixtures/classes' +require_relative '../../enumerable/shared/value_packing' describe "Enumerator::Lazy#take" do + describe "value packing of source yields (matches Enumerable#take)" do + before :each do + @take = -> e { e.lazy.take(1) } + end + it_behaves_like :enumerable_value_packing, nil + end + before :each do @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb index 539328a6c9dc87..eb6c13759e4043 100644 --- a/spec/ruby/core/enumerator/new_spec.rb +++ b/spec/ruby/core/enumerator/new_spec.rb @@ -42,35 +42,74 @@ enum.to_a.should == ["a\n", "b\n", "c"] end - describe 'yielded values' do - it 'handles yield arguments properly' do + describe '#yield' do + it 'accepts a single argument' do Enumerator.new { |y| y.yield(1) }.to_a.should == [1] Enumerator.new { |y| y.yield(1) }.first.should == 1 + end - Enumerator.new { |y| y.yield([1]) }.to_a.should == [[1]] - Enumerator.new { |y| y.yield([1]) }.first.should == [1] - + it 'accepts multiple arguments' do Enumerator.new { |y| y.yield(1, 2) }.to_a.should == [[1, 2]] Enumerator.new { |y| y.yield(1, 2) }.first.should == [1, 2] + end + + it "doesn't double-wrap arrays" do + Enumerator.new { |y| y.yield([1]) }.to_a.should == [[1]] + Enumerator.new { |y| y.yield([1]) }.first.should == [1] Enumerator.new { |y| y.yield([1, 2]) }.to_a.should == [[1, 2]] Enumerator.new { |y| y.yield([1, 2]) }.first.should == [1, 2] end - it 'handles << arguments properly' do + it 'returns nil' do + ScratchPad.record [] + Enumerator.new do |y| + ScratchPad << y.yield(1) + end.to_a + + ScratchPad.recorded.should == [nil] + end + + it 'accepts keyword arguments and treats them as a positional hash' do + Enumerator.new { |y| y.yield(foo: 42) }.to_a.should == [{ foo: 42 }] + Enumerator.new { |y| y.yield(foo: 42) }.first.should == { foo: 42 } + + Enumerator.new { |y| y.yield(123, foo: 42) }.to_a.should == [[123, { foo: 42 }]] + Enumerator.new { |y| y.yield(123, foo: 42) }.first.should == [123, { foo: 42 }] + end + end + + describe '#<<' do + it 'accepts a single argument' do Enumerator.new { |y| y.<<(1) }.to_a.should == [1] Enumerator.new { |y| y.<<(1) }.first.should == 1 + end + it "doesn't double-wrap arrays" do Enumerator.new { |y| y.<<([1]) }.to_a.should == [[1]] Enumerator.new { |y| y.<<([1]) }.first.should == [1] - # << doesn't accept multiple arguments - # Enumerator.new { |y| y.<<(1, 2) }.to_a.should == [[1, 2]] - # Enumerator.new { |y| y.<<(1, 2) }.first.should == [1, 2] - Enumerator.new { |y| y.<<([1, 2]) }.to_a.should == [[1, 2]] Enumerator.new { |y| y.<<([1, 2]) }.first.should == [1, 2] end + + it 'accepts keyword arguments and treats them as a positional hash' do + Enumerator.new { |y| y.<<(foo: 42) }.to_a.should == [{ foo: 42 }] + Enumerator.new { |y| y.<<(foo: 42) }.first.should == { foo: 42 } + end + + it 'can be chained' do + enum = Enumerator.new do |y| + y << 1 << 2 + end + enum.to_a.should == [1, 2] + end + + it 'raises ArgumentError when given more than one argument' do + -> { + Enumerator.new { |y| y.<<(1, 2) }.to_a + }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 1)") + end end end end diff --git a/spec/ruby/core/enumerator/produce_spec.rb b/spec/ruby/core/enumerator/produce_spec.rb index c69fb493033e2a..eb1b09294e8f5a 100644 --- a/spec/ruby/core/enumerator/produce_spec.rb +++ b/spec/ruby/core/enumerator/produce_spec.rb @@ -3,6 +3,8 @@ describe "Enumerator.produce" do it "creates an infinite enumerator" do enum = Enumerator.produce(0) { |prev| prev + 1 } + + enum.size.should == Float::INFINITY enum.take(5).should == [0, 1, 2, 3, 4] end @@ -31,4 +33,46 @@ lines.should == ["a\n", "b\n", "c\n", "d"] end end + + it "raises ArgumentError when no block is given" do + -> { Enumerator.produce }.should.raise(ArgumentError, "no block given") + end + + ruby_version_is ""..."4.0" do + it "accepts keyword arguments as the initial value" do + enum = Enumerator.produce(a: 1, b: 1) {} + enum.take(1).should == [{a: 1, b: 1}] + end + end + + ruby_version_is "4.0" do + it "raises ArgumentError for unknown keyword arguments" do + -> { Enumerator.produce(a: 1, b: 1) {} }.should.raise(ArgumentError, /unknown keywords/) + end + end + + ruby_version_is "4.0" do + context "with size keyword argument" do + it "sets the size of the enumerator" do + enum = Enumerator.produce(0, size: 10) { |n| n + 1 } + + enum.size.should == 10 + enum.take(5).should == [0, 1, 2, 3, 4] + end + + it "accepts a callable" do + enum = Enumerator.produce(0, size: -> { 5 * 5 }) { |n| n + 1 } + + enum.size.should == 25 + enum.take(5).should == [0, 1, 2, 3, 4] + end + + it "accepts nil" do + enum = Enumerator.produce(0, size: nil) { |n| n + 1 } + + enum.size.should == nil + enum.take(5).should == [0, 1, 2, 3, 4] + end + end + end end diff --git a/spec/ruby/core/enumerator/product/each_spec.rb b/spec/ruby/core/enumerator/product/each_spec.rb index 164998404d418f..a5dced4db17323 100644 --- a/spec/ruby/core/enumerator/product/each_spec.rb +++ b/spec/ruby/core/enumerator/product/each_spec.rb @@ -68,4 +68,18 @@ def object2.each_entry enum.each { |x, y, z| acc << z } acc.should == [nil, nil, nil, nil] end + + it "yields no element when any enumerable is empty" do + enum = Enumerator::Product.new([], [1]) + + acc = [] + enum.each { |x| acc << x } + acc.should == [] + + enum = Enumerator::Product.new([1], []) + + acc = [] + enum.each { |x| acc << x } + acc.should == [] + end end diff --git a/spec/ruby/core/enumerator/product/size_spec.rb b/spec/ruby/core/enumerator/product/size_spec.rb index 96632d6eeecb1a..0ba427af9a1cd6 100644 --- a/spec/ruby/core/enumerator/product/size_spec.rb +++ b/spec/ruby/core/enumerator/product/size_spec.rb @@ -51,4 +51,14 @@ def enum.size; 1.0; end product = Enumerator::Product.new(1..2, enum) product.size.should == nil end + + ruby_version_is "3.4" do + it "returns zero when any enumerable reports zero" do + enum = Enumerator::Product.new(1...1, ["A", "B"]) + enum.size.should == 0 + + enum = Enumerator::Product.new(["A", "B"], 1...1) + enum.size.should == 0 + end + end end diff --git a/spec/ruby/core/enumerator/yielder/append_spec.rb b/spec/ruby/core/enumerator/yielder/append_spec.rb deleted file mode 100644 index 2e1f5203d9447e..00000000000000 --- a/spec/ruby/core/enumerator/yielder/append_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Enumerator::Yielder#<<" do - # TODO: There's some common behavior between yield and <<; move to a shared spec - it "yields the value to the block" do - ary = [] - y = Enumerator::Yielder.new {|x| ary << x} - y << 1 - - ary.should == [1] - end - - it "doesn't double-wrap Arrays" do - yields = [] - y = Enumerator::Yielder.new {|args| yields << args } - y << [1] - yields.should == [[1]] - end - - it "returns self" do - y = Enumerator::Yielder.new {|x| x + 1} - (y << 1).should.equal?(y) - end - - context "when multiple arguments passed" do - it "raises an ArgumentError" do - ary = [] - y = Enumerator::Yielder.new { |*x| ary << x } - - -> { - y.<<(1, 2) - }.should.raise(ArgumentError, /wrong number of arguments/) - end - end -end diff --git a/spec/ruby/core/enumerator/yielder/initialize_spec.rb b/spec/ruby/core/enumerator/yielder/initialize_spec.rb deleted file mode 100644 index 925f561ec43db7..00000000000000 --- a/spec/ruby/core/enumerator/yielder/initialize_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../spec_helper' - -describe "Enumerator::Yielder#initialize" do - before :each do - @class = Enumerator::Yielder - @uninitialized = @class.allocate - end - - it "is a private method" do - @class.private_instance_methods(false).should.include?(:initialize) - end - - it "returns self when given a block" do - @uninitialized.send(:initialize) {}.should.equal?(@uninitialized) - end -end diff --git a/spec/ruby/core/enumerator/yielder/to_proc_spec.rb b/spec/ruby/core/enumerator/yielder/to_proc_spec.rb deleted file mode 100644 index 1d3681ab503277..00000000000000 --- a/spec/ruby/core/enumerator/yielder/to_proc_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Enumerator::Yielder#to_proc" do - it "returns a Proc object that takes an argument and yields it to the block" do - ScratchPad.record [] - y = Enumerator::Yielder.new { |*args| ScratchPad << args; "foobar" } - - callable = y.to_proc - callable.class.should == Proc - - result = callable.call(1, 2) - ScratchPad.recorded.should == [[1, 2]] - - result.should == "foobar" - end -end diff --git a/spec/ruby/core/enumerator/yielder/yield_spec.rb b/spec/ruby/core/enumerator/yielder/yield_spec.rb deleted file mode 100644 index acfdf114b68d9e..00000000000000 --- a/spec/ruby/core/enumerator/yielder/yield_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require_relative '../../../spec_helper' - -describe "Enumerator::Yielder#yield" do - it "yields the value to the block" do - ary = [] - y = Enumerator::Yielder.new {|x| ary << x} - y.yield 1 - - ary.should == [1] - end - - it "yields with passed arguments" do - yields = [] - y = Enumerator::Yielder.new {|*args| yields << args } - y.yield 1, 2 - yields.should == [[1, 2]] - end - - it "returns the result of the block for the given value" do - y = Enumerator::Yielder.new {|x| x + 1} - y.yield(1).should == 2 - end - - context "when multiple arguments passed" do - it "yields the arguments list to the block" do - ary = [] - y = Enumerator::Yielder.new { |*x| ary << x } - y.yield(1, 2) - - ary.should == [[1, 2]] - end - end -end diff --git a/spec/ruby/core/hash/each_pair_spec.rb b/spec/ruby/core/hash/each_pair_spec.rb index eb6656681d238d..01810c130c71d4 100644 --- a/spec/ruby/core/hash/each_pair_spec.rb +++ b/spec/ruby/core/hash/each_pair_spec.rb @@ -1,11 +1,111 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' require_relative 'shared/iteration' -require_relative 'shared/each' require_relative '../enumerable/shared/enumeratorized' describe "Hash#each_pair" do - it_behaves_like :hash_each, :each_pair it_behaves_like :hash_iteration_no_block, :each_pair it_behaves_like :enumeratorized_with_origin_size, :each_pair, { 1 => 2, 3 => 4, 5 => 6 } + + # This is inconsistent with below, MRI checks the block arity in rb_hash_each_pair() + it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do + all_args = [] + { 1 => 2, 3 => 4 }.each_pair { |*args| all_args << args } + all_args.sort.should == [[[1, 2]], [[3, 4]]] + end + + it "yields the key and value of each pair to a block expecting |key, value|" do + r = {} + h = { a: 1, b: 2, c: 3, d: 5 } + h.each_pair { |k,v| r[k.to_s] = v.to_s }.should.equal?(h) + r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" } + end + + it "yields the key only to a block expecting |key,|" do + ary = [] + h = { "a" => 1, "b" => 2, "c" => 3 } + h.each_pair { |k,| ary << k } + ary.sort.should == ["a", "b", "c"] + end + + it "always yields an Array of 2 elements, even when given a callable of arity 2" do + obj = Object.new + def obj.foo(key, value) + end + + -> { + { "a" => 1 }.each_pair(&obj.method(:foo)) + }.should.raise(ArgumentError) + + -> { + { "a" => 1 }.each_pair(&-> key, value { }) + }.should.raise(ArgumentError) + end + + it "yields an Array of 2 elements when given a callable of arity 1" do + obj = Object.new + def obj.foo(key_value) + ScratchPad << key_value + end + + ScratchPad.record([]) + { "a" => 1 }.each_pair(&obj.method(:foo)) + ScratchPad.recorded.should == [["a", 1]] + end + + it "raises an error for a Hash when an arity enforcing callable of arity >2 is passed in" do + obj = Object.new + def obj.foo(key, value, extra) + end + + -> { + { "a" => 1 }.each_pair(&obj.method(:foo)) + }.should.raise(ArgumentError) + end + + it "uses the same order as keys() and values()" do + h = { a: 1, b: 2, c: 3, d: 5 } + keys = [] + values = [] + + h.each_pair do |k, v| + keys << k + values << v + end + + keys.should == h.keys + values.should == h.values + end + + # Confirming the argument-splatting works from child class for both k, v and [k, v] + it "properly expands (or not) child class's 'each'-yielded args" do + cls1 = Class.new(Hash) do + attr_accessor :k_v + def each + super do |k, v| + @k_v = [k, v] + yield k, v + end + end + end + + cls2 = Class.new(Hash) do + attr_accessor :k_v + def each + super do |k, v| + @k_v = [k, v] + yield([k, v]) + end + end + end + + obj1 = cls1.new + obj1['a'] = 'b' + obj1.map {|k, v| [k, v]}.should == [['a', 'b']] + obj1.k_v.should == ['a', 'b'] + + obj2 = cls2.new + obj2['a'] = 'b' + obj2.map {|k, v| [k, v]}.should == [['a', 'b']] + obj2.k_v.should == ['a', 'b'] + end end diff --git a/spec/ruby/core/hash/each_spec.rb b/spec/ruby/core/hash/each_spec.rb index f0de0bdee56107..1644b63216abef 100644 --- a/spec/ruby/core/hash/each_spec.rb +++ b/spec/ruby/core/hash/each_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/iteration' -require_relative 'shared/each' -require_relative '../enumerable/shared/enumeratorized' describe "Hash#each" do - it_behaves_like :hash_each, :each - it_behaves_like :hash_iteration_no_block, :each - it_behaves_like :enumeratorized_with_origin_size, :each, { 1 => 2, 3 => 4, 5 => 6 } + it "is an alias of Hash#each_pair" do + Hash.instance_method(:each).should == Hash.instance_method(:each_pair) + end end diff --git a/spec/ruby/core/hash/element_set_spec.rb b/spec/ruby/core/hash/element_set_spec.rb index 67c5a04d733767..bb805477cadbf0 100644 --- a/spec/ruby/core/hash/element_set_spec.rb +++ b/spec/ruby/core/hash/element_set_spec.rb @@ -1,7 +1,121 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/store' describe "Hash#[]=" do - it_behaves_like :hash_store, :[]= + it "associates the key with the value" do + h = { a: 1 } + h[:b] = 2 + h.should == { b:2, a:1 } + end + + it "returns the assigned value" do + h = {} + h.send(:[]=, 1, 2).should == 2 + end + + it "duplicates string keys using dup semantics" do + # dup doesn't copy singleton methods + key = +"foo" + def key.reverse() "bar" end + h = {} + h[key] = 0 + h.keys[0].reverse.should == "oof" + end + + it "stores unequal keys that hash to the same value" do + h = {} + k1 = ["x"] + k2 = ["y"] + # So they end up in the same bucket + k1.should_receive(:hash).and_return(0) + k2.should_receive(:hash).and_return(0) + + h[k1] = 1 + h[k2] = 2 + h.size.should == 2 + end + + it "accepts keys with private #hash method" do + key = HashSpecs::KeyWithPrivateHash.new + h = {} + h[key] = "foo" + h[key].should == "foo" + end + + it " accepts keys with an Integer hash" do + o = mock(hash: 1 << 100) + h = {} + h[o] = 1 + h[o].should == 1 + end + + it "duplicates and freezes string keys" do + key = +"foo" + h = {} + h[key] = 0 + key << "bar" + + h.should == { "foo" => 0 } + h.keys[0].should.frozen? + end + + it "doesn't duplicate and freeze already frozen string keys" do + key = "foo".freeze + h = {} + h[key] = 0 + h.keys[0].should.equal?(key) + end + + it "keeps the existing key in the hash if there is a matching one" do + h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } + key1 = HashSpecs::ByValueKey.new(13) + key2 = HashSpecs::ByValueKey.new(13) + h[key1] = 41 + key_in_hash = h.keys.last + key_in_hash.should.equal?(key1) + h[key2] = 42 + last_key = h.keys.last + last_key.should.equal?(key_in_hash) + last_key.should_not.equal?(key2) + end + + it "keeps the existing String key in the hash if there is a matching one" do + h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } + key1 = "foo".dup + key2 = "foo".dup + key1.should_not.equal?(key2) + h[key1] = 41 + frozen_key = h.keys.last + frozen_key.should_not.equal?(key1) + h[key2] = 42 + h.keys.last.should.equal?(frozen_key) + h.keys.last.should_not.equal?(key2) + end + + it "raises a FrozenError if called on a frozen instance" do + -> { HashSpecs.frozen_hash[1] = 2 }.should.raise(FrozenError) + end + + it "does not raise an exception if changing the value of an existing key during iteration" do + hash = {1 => 2, 3 => 4, 5 => 6} + hash.each { hash[1] = :foo } + hash.should == {1 => :foo, 3 => 4, 5 => 6} + end + + it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do + code = <<-EOC + load '#{fixture __FILE__, "name.rb"}' + hash = {} + [true, false, 1, 2.0, "hello", :ok].each do |value| + hash[value] = 42 + raise "incorrect value" unless hash[value] == 42 + hash[value] = 43 + raise "incorrect value" unless hash[value] == 43 + end + puts "OK" + puts hash.size + EOC + result = ruby_exe(code, args: "2>&1") + result.should == "OK\n6\n" + end end diff --git a/spec/ruby/core/hash/filter_spec.rb b/spec/ruby/core/hash/filter_spec.rb index 7dabe44984271b..625a7feb901cc2 100644 --- a/spec/ruby/core/hash/filter_spec.rb +++ b/spec/ruby/core/hash/filter_spec.rb @@ -1,10 +1,13 @@ require_relative '../../spec_helper' -require_relative 'shared/select' describe "Hash#filter" do - it_behaves_like :hash_select, :filter + it "is an alias of Hash#select" do + Hash.instance_method(:filter).should == Hash.instance_method(:select) + end end describe "Hash#filter!" do - it_behaves_like :hash_select!, :filter! + it "is an alias of Hash#select!" do + Hash.instance_method(:filter!).should == Hash.instance_method(:select!) + end end diff --git a/spec/ruby/core/hash/has_key_spec.rb b/spec/ruby/core/hash/has_key_spec.rb index 4af53579e57b82..9a6244c6f68d2f 100644 --- a/spec/ruby/core/hash/has_key_spec.rb +++ b/spec/ruby/core/hash/has_key_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/key' describe "Hash#has_key?" do - it_behaves_like :hash_key_p, :has_key? + it "is an alias of Hash#include?" do + Hash.instance_method(:has_key?).should == Hash.instance_method(:include?) + end end diff --git a/spec/ruby/core/hash/has_value_spec.rb b/spec/ruby/core/hash/has_value_spec.rb index 39f1627fd3e160..d40e52ebfd78f0 100644 --- a/spec/ruby/core/hash/has_value_spec.rb +++ b/spec/ruby/core/hash/has_value_spec.rb @@ -1,7 +1,16 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/value' describe "Hash#has_value?" do - it_behaves_like :hash_value_p, :has_value? + it "returns true if the value exists in the hash" do + { a: :b }.has_value?(:a).should == false + { 1 => 2 }.has_value?(2).should == true + h = Hash.new(5) + h.has_value?(5).should == false + h = Hash.new { 5 } + h.has_value?(5).should == false + end + + it "uses == semantics for comparing values" do + { 5 => 2.0 }.has_value?(2).should == true + end end diff --git a/spec/ruby/core/hash/include_spec.rb b/spec/ruby/core/hash/include_spec.rb index f3959dc5891233..1c32e9fb239369 100644 --- a/spec/ruby/core/hash/include_spec.rb +++ b/spec/ruby/core/hash/include_spec.rb @@ -1,7 +1,40 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/key' describe "Hash#include?" do - it_behaves_like :hash_key_p, :include? + it "returns true if argument is a key" do + h = { a: 1, b: 2, c: 3, 4 => 0 } + h.include?(:a).should == true + h.include?(:b).should == true + h.include?(2).should == false + h.include?(4).should == true + + not_supported_on :opal do + h.include?('b').should == false + h.include?(4.0).should == false + end + end + + it "returns true if the key's matching value was nil" do + { xyz: nil }.include?(:xyz).should == true + end + + it "returns true if the key's matching value was false" do + { xyz: false }.include?(:xyz).should == true + end + + it "returns true if the key is nil" do + { nil => 'b' }.include?(nil).should == true + { nil => nil }.include?(nil).should == true + end + + it "compares keys with the same #hash value via #eql?" do + x = mock('x') + x.stub!(:hash).and_return(42) + + y = mock('y') + y.stub!(:hash).and_return(42) + y.should_receive(:eql?).and_return(false) + + { x => nil }.include?(y).should == false + end end diff --git a/spec/ruby/core/hash/inspect_spec.rb b/spec/ruby/core/hash/inspect_spec.rb index f41ebb70a6fd8c..359b13360f375c 100644 --- a/spec/ruby/core/hash/inspect_spec.rb +++ b/spec/ruby/core/hash/inspect_spec.rb @@ -1,7 +1,123 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_s' describe "Hash#inspect" do - it_behaves_like :hash_to_s, :inspect + it "returns a string representation with same order as each()" do + h = { a: [1, 2], b: -2, d: -6, nil => nil } + expected = ruby_version_is("3.4") ? "{a: [1, 2], b: -2, d: -6, nil => nil}" : "{:a=>[1, 2], :b=>-2, :d=>-6, nil=>nil}" + h.inspect.should == expected + end + + it "calls #inspect on keys and values" do + key = mock('key') + val = mock('val') + key.should_receive(:inspect).and_return('key') + val.should_receive(:inspect).and_return('val') + expected = ruby_version_is("3.4") ? "{key => val}" : "{key=>val}" + { key => val }.inspect.should == expected + end + + it "does not call #to_s on a String returned from #inspect" do + str = +"abc" + str.should_not_receive(:to_s) + expected = ruby_version_is("3.4") ? '{a: "abc"}' : '{:a=>"abc"}' + { a: str }.inspect.should == expected + end + + it "calls #to_s on the object returned from #inspect if the Object isn't a String" do + obj = mock("Hash#inspect/to_s calls #to_s") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_return("abc") + expected = ruby_version_is("3.4") ? "{a: abc}" : "{:a=>abc}" + { a: obj }.inspect.should == expected + end + + it "does not call #to_str on the object returned from #inspect when it is not a String" do + obj = mock("Hash#inspect/to_s does not call #to_str") + obj.should_receive(:inspect).and_return(obj) + obj.should_not_receive(:to_str) + expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ + { a: obj }.inspect.should =~ expected_pattern + end + + it "does not call #to_str on the object returned from #to_s when it is not a String" do + obj = mock("Hash#inspect/to_s does not call #to_str on #to_s result") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_return(obj) + obj.should_not_receive(:to_str) + expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ + { a: obj }.inspect.should =~ expected_pattern + end + + it "does not swallow exceptions raised by #to_s" do + obj = mock("Hash#inspect/to_s does not swallow #to_s exceptions") + obj.should_receive(:inspect).and_return(obj) + obj.should_receive(:to_s).and_raise(Exception) + + -> { { a: obj }.inspect }.should.raise(Exception) + end + + it "handles hashes with recursive values" do + x = {} + x[0] = x + expected = ruby_version_is("3.4") ? '{0 => {...}}' : '{0=>{...}}' + x.inspect.should == expected + + x = {} + y = {} + x[0] = y + y[1] = x + expected_x = ruby_version_is("3.4") ? '{0 => {1 => {...}}}' : '{0=>{1=>{...}}}' + expected_y = ruby_version_is("3.4") ? '{1 => {0 => {...}}}' : '{1=>{0=>{...}}}' + x.inspect.should == expected_x + y.inspect.should == expected_y + end + + it "does not raise if inspected result is not default external encoding" do + utf_16be = mock("utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) + expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}' + {a: utf_16be}.inspect.should == expected + end + + it "works for keys and values whose #inspect return a frozen String" do + expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}" + { true => false }.inspect.should == expected + end + + ruby_version_is "3.4" do + it "adds quotes to symbol keys that are not valid symbol literals" do + { "needs-quotes": 1 }.inspect.should == '{"needs-quotes": 1}' + end + + it "can be evaled" do + no_quote = '{a: 1, a!: 1, a?: 1}' + eval(no_quote).inspect.should == no_quote + [ + '{"": 1}', + '{"0": 1, "!": 1, "%": 1, "&": 1, "*": 1, "+": 1, "-": 1, "/": 1, "<": 1, ">": 1, "^": 1, "`": 1, "|": 1, "~": 1}', + '{"@a": 1, "$a": 1, "+@": 1, "a=": 1, "[]": 1}', + '{"a\"b": 1, "@@a": 1, "<=>": 1, "===": 1, "[]=": 1}', + ].each do |quote| + eval(quote).inspect.should == quote + end + end + + it "can be evaled when Encoding.default_external is changed" do + external = Encoding.default_external + + Encoding.default_external = Encoding::ASCII + utf8_ascii_hash = '{"\\u3042": 1}' + eval(utf8_ascii_hash).inspect.should == utf8_ascii_hash + + Encoding.default_external = Encoding::UTF_8 + utf8_hash = "{\u3042: 1}" + eval(utf8_hash).inspect.should == utf8_hash + + Encoding.default_external = Encoding::Windows_31J + sjis_hash = "{\x87]: 1}".dup.force_encoding('sjis') + eval(sjis_hash).inspect.should == sjis_hash + ensure + Encoding.default_external = external + end + end end diff --git a/spec/ruby/core/hash/key_spec.rb b/spec/ruby/core/hash/key_spec.rb index 73eecbc98e02f9..c2d7049aed8f3d 100644 --- a/spec/ruby/core/hash/key_spec.rb +++ b/spec/ruby/core/hash/key_spec.rb @@ -1,12 +1,32 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/key' -require_relative 'shared/index' describe "Hash#key?" do - it_behaves_like :hash_key_p, :key? + it "is an alias of Hash#include?" do + Hash.instance_method(:key?).should == Hash.instance_method(:include?) + end end describe "Hash#key" do - it_behaves_like :hash_index, :key + it "returns the corresponding key for value" do + { 2 => 'a', 1 => 'b' }.key('b').should == 1 + end + + it "returns nil if the value is not found" do + { a: -1, b: 3.14, c: 2.718 }.key(1).should == nil + end + + it "doesn't return default value if the value is not found" do + Hash.new(5).key(5).should == nil + end + + it "compares values using ==" do + { 1 => 0 }.key(0.0).should == 1 + { 1 => 0.0 }.key(0).should == 1 + + needle = mock('needle') + inhash = mock('inhash') + inhash.should_receive(:==).with(needle).and_return(true) + + { 1 => inhash }.key(needle).should == 1 + end end diff --git a/spec/ruby/core/hash/length_spec.rb b/spec/ruby/core/hash/length_spec.rb index d0af0945df053c..325973306f1d4f 100644 --- a/spec/ruby/core/hash/length_spec.rb +++ b/spec/ruby/core/hash/length_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' describe "Hash#length" do - it_behaves_like :hash_length, :length + it "is an alias of Hash#size" do + Hash.instance_method(:size).should == Hash.instance_method(:length) + end end diff --git a/spec/ruby/core/hash/member_spec.rb b/spec/ruby/core/hash/member_spec.rb index 37c04145596419..e7309c3f7d1600 100644 --- a/spec/ruby/core/hash/member_spec.rb +++ b/spec/ruby/core/hash/member_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/key' describe "Hash#member?" do - it_behaves_like :hash_key_p, :member? + it "is an alias of Hash#include?" do + Hash.instance_method(:member?).should == Hash.instance_method(:include?) + end end diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb index 5fb278ad471809..9e566fcee92b11 100644 --- a/spec/ruby/core/hash/merge_spec.rb +++ b/spec/ruby/core/hash/merge_spec.rb @@ -1,7 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/iteration' -require_relative 'shared/update' describe "Hash#merge" do it "returns a new hash by combining self with the contents of other" do @@ -119,5 +117,7 @@ end describe "Hash#merge!" do - it_behaves_like :hash_update, :merge! + it "is an alias of Hash#update" do + Hash.instance_method(:merge!).should == Hash.instance_method(:update) + end end diff --git a/spec/ruby/core/hash/select_spec.rb b/spec/ruby/core/hash/select_spec.rb index 38b0180b0e6e78..60b2ce67c16f99 100644 --- a/spec/ruby/core/hash/select_spec.rb +++ b/spec/ruby/core/hash/select_spec.rb @@ -1,10 +1,112 @@ require_relative '../../spec_helper' -require_relative 'shared/select' +require_relative '../enumerable/shared/enumeratorized' +require_relative 'fixtures/classes' +require_relative 'shared/iteration' describe "Hash#select" do - it_behaves_like :hash_select, :select + before :each do + @hsh = { 1 => 2, 3 => 4, 5 => 6 } + @empty = {} + end + + it "yields two arguments: key and value" do + all_args = [] + { 1 => 2, 3 => 4 }.select { |*args| all_args << args } + all_args.sort.should == [[1, 2], [3, 4]] + end + + it "returns a Hash of entries for which block is true" do + a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.select { |k,v| v % 2 == 0 } + a_pairs.should.instance_of?(Hash) + a_pairs.sort.should == [['c', 4], ['d', 2]] + end + + it "processes entries with the same order as reject" do + h = { a: 9, c: 4, b: 5, d: 2 } + + select_pairs = [] + reject_pairs = [] + h.dup.select{ |*pair| select_pairs << pair } + h.reject { |*pair| reject_pairs << pair } + + select_pairs.should == reject_pairs + end + + it "returns an Enumerator when called on a non-empty hash without a block" do + @hsh.select.should.instance_of?(Enumerator) + end + + it "returns an Enumerator when called on an empty hash without a block" do + @empty.select.should.instance_of?(Enumerator) + end + + it "does not retain the default value" do + h = Hash.new(1) + h.select { true }.default.should == nil + h[:a] = 1 + h.select { true }.default.should == nil + end + + it "does not retain the default_proc" do + pr = proc { |h, k| h[k] = [] } + h = Hash.new(&pr) + h.select { true }.default_proc.should == nil + h[:a] = 1 + h.select { true }.default_proc.should == nil + end + + it "retains compare_by_identity flag" do + h = { a: 9, c: 4 }.compare_by_identity + h2 = h.select { |k, _| k == :a } + h2.compare_by_identity?.should == true + end + + it_behaves_like :hash_iteration_no_block, :select + + before :each do + @object = { 1 => 2, 3 => 4, 5 => 6 } + end + it_behaves_like :enumeratorized_with_origin_size, :select end describe "Hash#select!" do - it_behaves_like :hash_select!, :select! + before :each do + @hsh = { 1 => 2, 3 => 4, 5 => 6 } + @empty = {} + end + + it "is equivalent to keep_if if changes are made" do + h = { a: 2 } + h.select! { |k,v| v <= 1 }.should.equal? h + + h = { 1 => 2, 3 => 4 } + all_args_select = [] + h.dup.select! { |*args| all_args_select << args } + all_args_select.should == [[1, 2], [3, 4]] + end + + it "removes all entries if the block is false" do + h = { a: 1, b: 2, c: 3 } + h.select! { |k,v| false }.should.equal?(h) + h.should == {} + end + + it "returns nil if no changes were made" do + { a: 1 }.select! { |k,v| v <= 1 }.should == nil + end + + it "raises a FrozenError if called on an empty frozen instance" do + -> { HashSpecs.empty_frozen_hash.select! { false } }.should.raise(FrozenError) + end + + it "raises a FrozenError if called on a frozen instance that would not be modified" do + -> { HashSpecs.frozen_hash.select! { true } }.should.raise(FrozenError) + end + + it_behaves_like :hash_iteration_no_block, :select! + + before :each do + @object = { 1 => 2, 3 => 4, 5 => 6 } + end + it_behaves_like :enumeratorized_with_origin_size, :select! end diff --git a/spec/ruby/core/hash/shared/each.rb b/spec/ruby/core/hash/shared/each.rb deleted file mode 100644 index 657c5d2c529da7..00000000000000 --- a/spec/ruby/core/hash/shared/each.rb +++ /dev/null @@ -1,105 +0,0 @@ -describe :hash_each, shared: true do - - # This is inconsistent with below, MRI checks the block arity in rb_hash_each_pair() - it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do - all_args = [] - { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args } - all_args.sort.should == [[[1, 2]], [[3, 4]]] - end - - it "yields the key and value of each pair to a block expecting |key, value|" do - r = {} - h = { a: 1, b: 2, c: 3, d: 5 } - h.send(@method) { |k,v| r[k.to_s] = v.to_s }.should.equal?(h) - r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" } - end - - it "yields the key only to a block expecting |key,|" do - ary = [] - h = { "a" => 1, "b" => 2, "c" => 3 } - h.send(@method) { |k,| ary << k } - ary.sort.should == ["a", "b", "c"] - end - - it "always yields an Array of 2 elements, even when given a callable of arity 2" do - obj = Object.new - def obj.foo(key, value) - end - - -> { - { "a" => 1 }.send(@method, &obj.method(:foo)) - }.should.raise(ArgumentError) - - -> { - { "a" => 1 }.send(@method, &-> key, value { }) - }.should.raise(ArgumentError) - end - - it "yields an Array of 2 elements when given a callable of arity 1" do - obj = Object.new - def obj.foo(key_value) - ScratchPad << key_value - end - - ScratchPad.record([]) - { "a" => 1 }.send(@method, &obj.method(:foo)) - ScratchPad.recorded.should == [["a", 1]] - end - - it "raises an error for a Hash when an arity enforcing callable of arity >2 is passed in" do - obj = Object.new - def obj.foo(key, value, extra) - end - - -> { - { "a" => 1 }.send(@method, &obj.method(:foo)) - }.should.raise(ArgumentError) - end - - it "uses the same order as keys() and values()" do - h = { a: 1, b: 2, c: 3, d: 5 } - keys = [] - values = [] - - h.send(@method) do |k, v| - keys << k - values << v - end - - keys.should == h.keys - values.should == h.values - end - - # Confirming the argument-splatting works from child class for both k, v and [k, v] - it "properly expands (or not) child class's 'each'-yielded args" do - cls1 = Class.new(Hash) do - attr_accessor :k_v - def each - super do |k, v| - @k_v = [k, v] - yield k, v - end - end - end - - cls2 = Class.new(Hash) do - attr_accessor :k_v - def each - super do |k, v| - @k_v = [k, v] - yield([k, v]) - end - end - end - - obj1 = cls1.new - obj1['a'] = 'b' - obj1.map {|k, v| [k, v]}.should == [['a', 'b']] - obj1.k_v.should == ['a', 'b'] - - obj2 = cls2.new - obj2['a'] = 'b' - obj2.map {|k, v| [k, v]}.should == [['a', 'b']] - obj2.k_v.should == ['a', 'b'] - end -end diff --git a/spec/ruby/core/hash/shared/index.rb b/spec/ruby/core/hash/shared/index.rb deleted file mode 100644 index dd4e89a9b4752d..00000000000000 --- a/spec/ruby/core/hash/shared/index.rb +++ /dev/null @@ -1,37 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :hash_index, shared: true do - it "returns the corresponding key for value" do - suppress_warning do # for Hash#index - { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1 - end - end - - it "returns nil if the value is not found" do - suppress_warning do # for Hash#index - { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should == nil - end - end - - it "doesn't return default value if the value is not found" do - suppress_warning do # for Hash#index - Hash.new(5).send(@method, 5).should == nil - end - end - - it "compares values using ==" do - suppress_warning do # for Hash#index - { 1 => 0 }.send(@method, 0.0).should == 1 - { 1 => 0.0 }.send(@method, 0).should == 1 - end - - needle = mock('needle') - inhash = mock('inhash') - inhash.should_receive(:==).with(needle).and_return(true) - - suppress_warning do # for Hash#index - { 1 => inhash }.send(@method, needle).should == 1 - end - end -end diff --git a/spec/ruby/core/hash/shared/key.rb b/spec/ruby/core/hash/shared/key.rb deleted file mode 100644 index 17f9f814573d04..00000000000000 --- a/spec/ruby/core/hash/shared/key.rb +++ /dev/null @@ -1,38 +0,0 @@ -describe :hash_key_p, shared: true do - it "returns true if argument is a key" do - h = { a: 1, b: 2, c: 3, 4 => 0 } - h.send(@method, :a).should == true - h.send(@method, :b).should == true - h.send(@method, 2).should == false - h.send(@method, 4).should == true - - not_supported_on :opal do - h.send(@method, 'b').should == false - h.send(@method, 4.0).should == false - end - end - - it "returns true if the key's matching value was nil" do - { xyz: nil }.send(@method, :xyz).should == true - end - - it "returns true if the key's matching value was false" do - { xyz: false }.send(@method, :xyz).should == true - end - - it "returns true if the key is nil" do - { nil => 'b' }.send(@method, nil).should == true - { nil => nil }.send(@method, nil).should == true - end - - it "compares keys with the same #hash value via #eql?" do - x = mock('x') - x.stub!(:hash).and_return(42) - - y = mock('y') - y.stub!(:hash).and_return(42) - y.should_receive(:eql?).and_return(false) - - { x => nil }.send(@method, y).should == false - end -end diff --git a/spec/ruby/core/hash/shared/length.rb b/spec/ruby/core/hash/shared/length.rb deleted file mode 100644 index 24f5563759091f..00000000000000 --- a/spec/ruby/core/hash/shared/length.rb +++ /dev/null @@ -1,12 +0,0 @@ -describe :hash_length, shared: true do - it "returns the number of entries" do - { a: 1, b: 'c' }.send(@method).should == 2 - h = { a: 1, b: 2 } - h[:a] = 2 - h.send(@method).should == 2 - { a: 1, b: 1, c: 1 }.send(@method).should == 3 - {}.send(@method).should == 0 - Hash.new(5).send(@method).should == 0 - Hash.new { 5 }.send(@method).should == 0 - end -end diff --git a/spec/ruby/core/hash/shared/select.rb b/spec/ruby/core/hash/shared/select.rb deleted file mode 100644 index b4f6d1b3acb2d8..00000000000000 --- a/spec/ruby/core/hash/shared/select.rb +++ /dev/null @@ -1,112 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/iteration' -require_relative '../../enumerable/shared/enumeratorized' - -describe :hash_select, shared: true do - before :each do - @hsh = { 1 => 2, 3 => 4, 5 => 6 } - @empty = {} - end - - it "yields two arguments: key and value" do - all_args = [] - { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args } - all_args.sort.should == [[1, 2], [3, 4]] - end - - it "returns a Hash of entries for which block is true" do - a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.send(@method) { |k,v| v % 2 == 0 } - a_pairs.should.instance_of?(Hash) - a_pairs.sort.should == [['c', 4], ['d', 2]] - end - - it "processes entries with the same order as reject" do - h = { a: 9, c: 4, b: 5, d: 2 } - - select_pairs = [] - reject_pairs = [] - h.dup.send(@method) { |*pair| select_pairs << pair } - h.reject { |*pair| reject_pairs << pair } - - select_pairs.should == reject_pairs - end - - it "returns an Enumerator when called on a non-empty hash without a block" do - @hsh.send(@method).should.instance_of?(Enumerator) - end - - it "returns an Enumerator when called on an empty hash without a block" do - @empty.send(@method).should.instance_of?(Enumerator) - end - - it "does not retain the default value" do - h = Hash.new(1) - h.send(@method) { true }.default.should == nil - h[:a] = 1 - h.send(@method) { true }.default.should == nil - end - - it "does not retain the default_proc" do - pr = proc { |h, k| h[k] = [] } - h = Hash.new(&pr) - h.send(@method) { true }.default_proc.should == nil - h[:a] = 1 - h.send(@method) { true }.default_proc.should == nil - end - - it "retains compare_by_identity flag" do - h = { a: 9, c: 4 }.compare_by_identity - h2 = h.send(@method) { |k, _| k == :a } - h2.compare_by_identity?.should == true - end - - it_should_behave_like :hash_iteration_no_block - - before :each do - @object = { 1 => 2, 3 => 4, 5 => 6 } - end - it_should_behave_like :enumeratorized_with_origin_size -end - -describe :hash_select!, shared: true do - before :each do - @hsh = { 1 => 2, 3 => 4, 5 => 6 } - @empty = {} - end - - it "is equivalent to keep_if if changes are made" do - h = { a: 2 } - h.send(@method) { |k,v| v <= 1 }.should.equal? h - - h = { 1 => 2, 3 => 4 } - all_args_select = [] - h.dup.send(@method) { |*args| all_args_select << args } - all_args_select.should == [[1, 2], [3, 4]] - end - - it "removes all entries if the block is false" do - h = { a: 1, b: 2, c: 3 } - h.send(@method) { |k,v| false }.should.equal?(h) - h.should == {} - end - - it "returns nil if no changes were made" do - { a: 1 }.send(@method) { |k,v| v <= 1 }.should == nil - end - - it "raises a FrozenError if called on an empty frozen instance" do - -> { HashSpecs.empty_frozen_hash.send(@method) { false } }.should.raise(FrozenError) - end - - it "raises a FrozenError if called on a frozen instance that would not be modified" do - -> { HashSpecs.frozen_hash.send(@method) { true } }.should.raise(FrozenError) - end - - it_should_behave_like :hash_iteration_no_block - - before :each do - @object = { 1 => 2, 3 => 4, 5 => 6 } - end - it_should_behave_like :enumeratorized_with_origin_size -end diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb deleted file mode 100644 index 05ef63face67fe..00000000000000 --- a/spec/ruby/core/hash/shared/store.rb +++ /dev/null @@ -1,115 +0,0 @@ -require_relative '../fixtures/classes' - -describe :hash_store, shared: true do - it "associates the key with the value and return the value" do - h = { a: 1 } - h.send(@method, :b, 2).should == 2 - h.should == { b:2, a:1 } - end - - it "duplicates string keys using dup semantics" do - # dup doesn't copy singleton methods - key = +"foo" - def key.reverse() "bar" end - h = {} - h.send(@method, key, 0) - h.keys[0].reverse.should == "oof" - end - - it "stores unequal keys that hash to the same value" do - h = {} - k1 = ["x"] - k2 = ["y"] - # So they end up in the same bucket - k1.should_receive(:hash).and_return(0) - k2.should_receive(:hash).and_return(0) - - h.send(@method, k1, 1) - h.send(@method, k2, 2) - h.size.should == 2 - end - - it "accepts keys with private #hash method" do - key = HashSpecs::KeyWithPrivateHash.new - h = {} - h.send(@method, key, "foo") - h[key].should == "foo" - end - - it " accepts keys with an Integer hash" do - o = mock(hash: 1 << 100) - h = {} - h[o] = 1 - h[o].should == 1 - end - - it "duplicates and freezes string keys" do - key = +"foo" - h = {} - h.send(@method, key, 0) - key << "bar" - - h.should == { "foo" => 0 } - h.keys[0].should.frozen? - end - - it "doesn't duplicate and freeze already frozen string keys" do - key = "foo".freeze - h = {} - h.send(@method, key, 0) - h.keys[0].should.equal?(key) - end - - it "keeps the existing key in the hash if there is a matching one" do - h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } - key1 = HashSpecs::ByValueKey.new(13) - key2 = HashSpecs::ByValueKey.new(13) - h[key1] = 41 - key_in_hash = h.keys.last - key_in_hash.should.equal?(key1) - h[key2] = 42 - last_key = h.keys.last - last_key.should.equal?(key_in_hash) - last_key.should_not.equal?(key2) - end - - it "keeps the existing String key in the hash if there is a matching one" do - h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } - key1 = "foo".dup - key2 = "foo".dup - key1.should_not.equal?(key2) - h[key1] = 41 - frozen_key = h.keys.last - frozen_key.should_not.equal?(key1) - h[key2] = 42 - h.keys.last.should.equal?(frozen_key) - h.keys.last.should_not.equal?(key2) - end - - it "raises a FrozenError if called on a frozen instance" do - -> { HashSpecs.frozen_hash.send(@method, 1, 2) }.should.raise(FrozenError) - end - - it "does not raise an exception if changing the value of an existing key during iteration" do - hash = {1 => 2, 3 => 4, 5 => 6} - hash.each { hash.send(@method, 1, :foo) } - hash.should == {1 => :foo, 3 => 4, 5 => 6} - end - - it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do - code = <<-EOC - load '#{fixture __FILE__, "name.rb"}' - hash = {} - [true, false, 1, 2.0, "hello", :ok].each do |value| - hash[value] = 42 - raise "incorrect value" unless hash[value] == 42 - hash[value] = 43 - raise "incorrect value" unless hash[value] == 43 - end - puts "OK" - puts hash.size - EOC - result = ruby_exe(code, args: "2>&1") - result.should == "OK\n6\n" - end -end diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb deleted file mode 100644 index f88ca738a598e6..00000000000000 --- a/spec/ruby/core/hash/shared/to_s.rb +++ /dev/null @@ -1,124 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :hash_to_s, shared: true do - it "returns a string representation with same order as each()" do - h = { a: [1, 2], b: -2, d: -6, nil => nil } - expected = ruby_version_is("3.4") ? "{a: [1, 2], b: -2, d: -6, nil => nil}" : "{:a=>[1, 2], :b=>-2, :d=>-6, nil=>nil}" - h.send(@method).should == expected - end - - it "calls #inspect on keys and values" do - key = mock('key') - val = mock('val') - key.should_receive(:inspect).and_return('key') - val.should_receive(:inspect).and_return('val') - expected = ruby_version_is("3.4") ? "{key => val}" : "{key=>val}" - { key => val }.send(@method).should == expected - end - - it "does not call #to_s on a String returned from #inspect" do - str = +"abc" - str.should_not_receive(:to_s) - expected = ruby_version_is("3.4") ? '{a: "abc"}' : '{:a=>"abc"}' - { a: str }.send(@method).should == expected - end - - it "calls #to_s on the object returned from #inspect if the Object isn't a String" do - obj = mock("Hash#inspect/to_s calls #to_s") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_return("abc") - expected = ruby_version_is("3.4") ? "{a: abc}" : "{:a=>abc}" - { a: obj }.send(@method).should == expected - end - - it "does not call #to_str on the object returned from #inspect when it is not a String" do - obj = mock("Hash#inspect/to_s does not call #to_str") - obj.should_receive(:inspect).and_return(obj) - obj.should_not_receive(:to_str) - expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ - { a: obj }.send(@method).should =~ expected_pattern - end - - it "does not call #to_str on the object returned from #to_s when it is not a String" do - obj = mock("Hash#inspect/to_s does not call #to_str on #to_s result") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_return(obj) - obj.should_not_receive(:to_str) - expected_pattern = ruby_version_is("3.4") ? /^\{a: #\}$/ : /^\{:a=>#\}$/ - { a: obj }.send(@method).should =~ expected_pattern - end - - it "does not swallow exceptions raised by #to_s" do - obj = mock("Hash#inspect/to_s does not swallow #to_s exceptions") - obj.should_receive(:inspect).and_return(obj) - obj.should_receive(:to_s).and_raise(Exception) - - -> { { a: obj }.send(@method) }.should.raise(Exception) - end - - it "handles hashes with recursive values" do - x = {} - x[0] = x - expected = ruby_version_is("3.4") ? '{0 => {...}}' : '{0=>{...}}' - x.send(@method).should == expected - - x = {} - y = {} - x[0] = y - y[1] = x - expected_x = ruby_version_is("3.4") ? '{0 => {1 => {...}}}' : '{0=>{1=>{...}}}' - expected_y = ruby_version_is("3.4") ? '{1 => {0 => {...}}}' : '{1=>{0=>{...}}}' - x.send(@method).should == expected_x - y.send(@method).should == expected_y - end - - it "does not raise if inspected result is not default external encoding" do - utf_16be = mock("utf_16be") - utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE)) - expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}' - {a: utf_16be}.send(@method).should == expected - end - - it "works for keys and values whose #inspect return a frozen String" do - expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}" - { true => false }.to_s.should == expected - end - - ruby_version_is "3.4" do - it "adds quotes to symbol keys that are not valid symbol literals" do - { "needs-quotes": 1 }.send(@method).should == '{"needs-quotes": 1}' - end - - it "can be evaled" do - no_quote = '{a: 1, a!: 1, a?: 1}' - eval(no_quote).inspect.should == no_quote - [ - '{"": 1}', - '{"0": 1, "!": 1, "%": 1, "&": 1, "*": 1, "+": 1, "-": 1, "/": 1, "<": 1, ">": 1, "^": 1, "`": 1, "|": 1, "~": 1}', - '{"@a": 1, "$a": 1, "+@": 1, "a=": 1, "[]": 1}', - '{"a\"b": 1, "@@a": 1, "<=>": 1, "===": 1, "[]=": 1}', - ].each do |quote| - eval(quote).inspect.should == quote - end - end - - it "can be evaled when Encoding.default_external is changed" do - external = Encoding.default_external - - Encoding.default_external = Encoding::ASCII - utf8_ascii_hash = '{"\\u3042": 1}' - eval(utf8_ascii_hash).inspect.should == utf8_ascii_hash - - Encoding.default_external = Encoding::UTF_8 - utf8_hash = "{\u3042: 1}" - eval(utf8_hash).inspect.should == utf8_hash - - Encoding.default_external = Encoding::Windows_31J - sjis_hash = "{\x87]: 1}".dup.force_encoding('sjis') - eval(sjis_hash).inspect.should == sjis_hash - ensure - Encoding.default_external = external - end - end -end diff --git a/spec/ruby/core/hash/shared/update.rb b/spec/ruby/core/hash/shared/update.rb deleted file mode 100644 index 6dbad1d6d01b2c..00000000000000 --- a/spec/ruby/core/hash/shared/update.rb +++ /dev/null @@ -1,76 +0,0 @@ -describe :hash_update, shared: true do - it "adds the entries from other, overwriting duplicate keys. Returns self" do - h = { _1: 'a', _2: '3' } - h.send(@method, _1: '9', _9: 2).should.equal?(h) - h.should == { _1: "9", _2: "3", _9: 2 } - end - - it "sets any duplicate key to the value of block if passed a block" do - h1 = { a: 2, b: -1 } - h2 = { a: -2, c: 1 } - h1.send(@method, h2) { |k,x,y| 3.14 }.should.equal?(h1) - h1.should == { c: 1, b: -1, a: 3.14 } - - h1.send(@method, h1) { nil } - h1.should == { a: nil, b: nil, c: nil } - end - - it "tries to convert the passed argument to a hash using #to_hash" do - obj = mock('{1=>2}') - obj.should_receive(:to_hash).and_return({ 1 => 2 }) - { 3 => 4 }.send(@method, obj).should == { 1 => 2, 3 => 4 } - end - - it "does not call to_hash on hash subclasses" do - { 3 => 4 }.send(@method, HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } - end - - it "processes entries with same order as merge()" do - h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } - merge_bang_pairs = [] - merge_pairs = [] - h.merge(h) { |*arg| merge_pairs << arg } - h.send(@method, h) { |*arg| merge_bang_pairs << arg } - merge_bang_pairs.should == merge_pairs - end - - it "raises a FrozenError on a frozen instance that is modified" do - -> do - HashSpecs.frozen_hash.send(@method, 1 => 2) - end.should.raise(FrozenError) - end - - it "checks frozen status before coercing an object with #to_hash" do - obj = mock("to_hash frozen") - # This is necessary because mock cleanup code cannot run on the frozen - # object. - def obj.to_hash() raise Exception, "should not receive #to_hash" end - obj.freeze - - -> { HashSpecs.frozen_hash.send(@method, obj) }.should.raise(FrozenError) - end - - # see redmine #1571 - it "raises a FrozenError on a frozen instance that would not be modified" do - -> do - HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash) - end.should.raise(FrozenError) - end - - it "does not raise an exception if changing the value of an existing key during iteration" do - hash = {1 => 2, 3 => 4, 5 => 6} - hash2 = {1 => :foo, 3 => :bar} - hash.each { hash.send(@method, hash2) } - hash.should == {1 => :foo, 3 => :bar, 5 => 6} - end - - it "accepts multiple hashes" do - result = { a: 1 }.send(@method, { b: 2 }, { c: 3 }, { d: 4 }) - result.should == { a: 1, b: 2, c: 3, d: 4 } - end - - it "accepts zero arguments" do - hash = { a: 1 } - hash.send(@method).should.eql?(hash) - end -end diff --git a/spec/ruby/core/hash/shared/value.rb b/spec/ruby/core/hash/shared/value.rb deleted file mode 100644 index aac76c253e55f6..00000000000000 --- a/spec/ruby/core/hash/shared/value.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe :hash_value_p, shared: true do - it "returns true if the value exists in the hash" do - { a: :b }.send(@method, :a).should == false - { 1 => 2 }.send(@method, 2).should == true - h = Hash.new(5) - h.send(@method, 5).should == false - h = Hash.new { 5 } - h.send(@method, 5).should == false - end - - it "uses == semantics for comparing values" do - { 5 => 2.0 }.send(@method, 2).should == true - end -end diff --git a/spec/ruby/core/hash/shared/values_at.rb b/spec/ruby/core/hash/shared/values_at.rb deleted file mode 100644 index 4e4e60e7d6bca2..00000000000000 --- a/spec/ruby/core/hash/shared/values_at.rb +++ /dev/null @@ -1,9 +0,0 @@ -describe :hash_values_at, shared: true do - it "returns an array of values for the given keys" do - h = { a: 9, b: 'a', c: -10, d: nil } - h.send(@method).should.is_a?(Array) - h.send(@method).should == [] - h.send(@method, :a, :d, :b).should.is_a?(Array) - h.send(@method, :a, :d, :b).should == [9, nil, 'a'] - end -end diff --git a/spec/ruby/core/hash/size_spec.rb b/spec/ruby/core/hash/size_spec.rb index 1e8abd8d978107..5e5008a5dc7c38 100644 --- a/spec/ruby/core/hash/size_spec.rb +++ b/spec/ruby/core/hash/size_spec.rb @@ -1,7 +1,14 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' describe "Hash#size" do - it_behaves_like :hash_length, :size + it "returns the number of entries" do + { a: 1, b: 'c' }.size.should == 2 + h = { a: 1, b: 2 } + h[:a] = 2 + h.size.should == 2 + { a: 1, b: 1, c: 1 }.size.should == 3 + {}.size.should == 0 + Hash.new(5).size.should == 0 + Hash.new { 5 }.size.should == 0 + end end diff --git a/spec/ruby/core/hash/store_spec.rb b/spec/ruby/core/hash/store_spec.rb index 7e975380ec0f2e..7017d8ba2b2ba3 100644 --- a/spec/ruby/core/hash/store_spec.rb +++ b/spec/ruby/core/hash/store_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/store' describe "Hash#store" do - it_behaves_like :hash_store, :store + it "is an alias of Hash#[]=" do + Hash.instance_method(:store).should == Hash.instance_method(:[]=) + end end diff --git a/spec/ruby/core/hash/to_s_spec.rb b/spec/ruby/core/hash/to_s_spec.rb index e52b09962eb4fc..2915db6ef8757d 100644 --- a/spec/ruby/core/hash/to_s_spec.rb +++ b/spec/ruby/core/hash/to_s_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_s' describe "Hash#to_s" do - it_behaves_like :hash_to_s, :to_s + it "is an alias of Hash#inspect" do + Hash.instance_method(:to_s).should == Hash.instance_method(:inspect) + end end diff --git a/spec/ruby/core/hash/update_spec.rb b/spec/ruby/core/hash/update_spec.rb index 0975045ad10b5f..f3a3e6b4db2931 100644 --- a/spec/ruby/core/hash/update_spec.rb +++ b/spec/ruby/core/hash/update_spec.rb @@ -1,7 +1,79 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/update' describe "Hash#update" do - it_behaves_like :hash_update, :update + it "adds the entries from other, overwriting duplicate keys. Returns self" do + h = { _1: 'a', _2: '3' } + h.update(_1: '9', _9: 2).should.equal?(h) + h.should == { _1: "9", _2: "3", _9: 2 } + end + + it "sets any duplicate key to the value of block if passed a block" do + h1 = { a: 2, b: -1 } + h2 = { a: -2, c: 1 } + h1.update(h2) { |k,x,y| 3.14 }.should.equal?(h1) + h1.should == { c: 1, b: -1, a: 3.14 } + + h1.update(h1) { nil } + h1.should == { a: nil, b: nil, c: nil } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2}') + obj.should_receive(:to_hash).and_return({ 1 => 2 }) + { 3 => 4 }.update(obj).should == { 1 => 2, 3 => 4 } + end + + it "does not call to_hash on hash subclasses" do + { 3 => 4 }.update(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } + end + + it "processes entries with same order as merge()" do + h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } + merge_bang_pairs = [] + merge_pairs = [] + h.merge(h) { |*arg| merge_pairs << arg } + h.update(h) { |*arg| merge_bang_pairs << arg } + merge_bang_pairs.should == merge_pairs + end + + it "raises a FrozenError on a frozen instance that is modified" do + -> do + HashSpecs.frozen_hash.update(1 => 2) + end.should.raise(FrozenError) + end + + it "checks frozen status before coercing an object with #to_hash" do + obj = mock("to_hash frozen") + # This is necessary because mock cleanup code cannot run on the frozen + # object. + def obj.to_hash() raise Exception, "should not receive #to_hash" end + obj.freeze + + -> { HashSpecs.frozen_hash.update(obj) }.should.raise(FrozenError) + end + + # see redmine #1571 + it "raises a FrozenError on a frozen instance that would not be modified" do + -> do + HashSpecs.frozen_hash.update(HashSpecs.empty_frozen_hash) + end.should.raise(FrozenError) + end + + it "does not raise an exception if changing the value of an existing key during iteration" do + hash = {1 => 2, 3 => 4, 5 => 6} + hash2 = {1 => :foo, 3 => :bar} + hash.each { hash.update(hash2) } + hash.should == {1 => :foo, 3 => :bar, 5 => 6} + end + + it "accepts multiple hashes" do + result = { a: 1 }.update({ b: 2 }, { c: 3 }, { d: 4 }) + result.should == { a: 1, b: 2, c: 3, d: 4 } + end + + it "accepts zero arguments" do + hash = { a: 1 } + hash.update.should.eql?(hash) + end end diff --git a/spec/ruby/core/hash/value_spec.rb b/spec/ruby/core/hash/value_spec.rb index 0ab16a5d1b4e3d..9cfbe576d26a40 100644 --- a/spec/ruby/core/hash/value_spec.rb +++ b/spec/ruby/core/hash/value_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/value' describe "Hash#value?" do - it_behaves_like :hash_value_p, :value? + it "is an alias of Hash#has_value?" do + Hash.instance_method(:value?).should == Hash.instance_method(:has_value?) + end end diff --git a/spec/ruby/core/hash/values_at_spec.rb b/spec/ruby/core/hash/values_at_spec.rb index b620a279ba6d27..78dcd8df6ac22d 100644 --- a/spec/ruby/core/hash/values_at_spec.rb +++ b/spec/ruby/core/hash/values_at_spec.rb @@ -1,7 +1,11 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/values_at' describe "Hash#values_at" do - it_behaves_like :hash_values_at, :values_at + it "returns an array of values for the given keys" do + h = { a: 9, b: 'a', c: -10, d: nil } + h.values_at.should.is_a?(Array) + h.values_at.should == [] + h.values_at(:a, :d, :b).should.is_a?(Array) + h.values_at(:a, :d, :b).should == [9, nil, 'a'] + end end diff --git a/spec/ruby/core/io/buffer/for_spec.rb b/spec/ruby/core/io/buffer/for_spec.rb index 7971ce0b719ef1..4c614f74b05a6a 100644 --- a/spec/ruby/core/io/buffer/for_spec.rb +++ b/spec/ruby/core/io/buffer/for_spec.rb @@ -66,6 +66,7 @@ buffer.get_string.should == @string.b buffer.should_not.readonly? + buffer.should_not.locked? buffer.set_string("ghost shell") @string.should == "ghost shellg" diff --git a/spec/ruby/core/io/buffer/map_spec.rb b/spec/ruby/core/io/buffer/map_spec.rb index 23e837ce078dd1..4b28539ad82762 100644 --- a/spec/ruby/core/io/buffer/map_spec.rb +++ b/spec/ruby/core/io/buffer/map_spec.rb @@ -73,7 +73,7 @@ def open_big_file_fixture @buffer.should.valid? end - platform_is_not :windows, :openbsd do + guard -> { Process.respond_to?(:fork) } do it "is shareable across processes" do file_name = tmp("shared_buffer") @file = File.open(file_name, "w+") @@ -312,7 +312,7 @@ def open_big_file_fixture @file.read.should == "abcâdef\n".b end - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do it "is not shared across processes" do file_name = tmp("shared_buffer") @file = File.open(file_name, "w+") diff --git a/spec/ruby/core/io/buffer/shared_spec.rb b/spec/ruby/core/io/buffer/shared_spec.rb index daaa66c5ad8b3d..2cc93e6d08c07a 100644 --- a/spec/ruby/core/io/buffer/shared_spec.rb +++ b/spec/ruby/core/io/buffer/shared_spec.rb @@ -12,15 +12,13 @@ end it "is true for a non-private buffer created with .map" do - path = tmp("read_text.txt") - File.copy_stream(fixture(__dir__, "read_text.txt"), path) + path = fixture(__dir__, "read_text.txt") file = File.open(path, "r+") @buffer = IO::Buffer.map(file) @buffer.shared?.should == true ensure @buffer.free file.close - File.unlink(path) end it "is false for an unshared buffer" do diff --git a/spec/ruby/core/io/buffer/transfer_spec.rb b/spec/ruby/core/io/buffer/transfer_spec.rb index 02e029016ada3b..3bc08998dde737 100644 --- a/spec/ruby/core/io/buffer/transfer_spec.rb +++ b/spec/ruby/core/io/buffer/transfer_spec.rb @@ -91,8 +91,9 @@ context "with a slice of a buffer" do it "transfers source to a new slice, not touching the buffer" do @buffer = IO::Buffer.new(4) - slice = @buffer.slice(0, 2) @buffer.set_string("test") + slice = @buffer.slice(0, 2) + slice.get_string.should == "te" new_slice = slice.transfer slice.null?.should == true diff --git a/spec/ruby/core/io/buffer/valid_spec.rb b/spec/ruby/core/io/buffer/valid_spec.rb index 0a401728696861..b84bdd0cfd942e 100644 --- a/spec/ruby/core/io/buffer/valid_spec.rb +++ b/spec/ruby/core/io/buffer/valid_spec.rb @@ -57,17 +57,6 @@ @buffer.resize(3) slice.valid?.should == false end - - platform_is_not :linux do - # This test does not cause a copy-resize on Linux. - # `#resize` MAY cause the buffer to move, but there is no guarantee. - it "is false when buffer is copied on resize" do - @buffer = IO::Buffer.new(4, IO::Buffer::MAPPED) - slice = @buffer.slice(0, 2) - @buffer.resize(8) - slice.valid?.should == false - end - end end it "is false for a slice of a transferred buffer" do diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb index f61cca74633fcf..978fd8ef08c427 100644 --- a/spec/ruby/core/kernel/Integer_spec.rb +++ b/spec/ruby/core/kernel/Integer_spec.rb @@ -14,7 +14,9 @@ obj = mock("object") obj.should_receive(:to_int).and_return("1") obj.should_receive(:to_i).and_return(nil) - -> { Integer(obj) }.should.raise(TypeError) + -> { + Integer(obj) + }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives nil)") end it "return a result of to_i when to_int does not return an Integer" do @@ -24,8 +26,31 @@ Integer(obj).should == 42 end + it "returns a result of to_str" do + obj = mock("obj") + obj.should_receive(:to_str).and_return("1") + + Integer(obj).should == 1 + end + + it "returns a result of to_int when both to_int and to_str are defined" do + obj = mock("obj") + obj.should_receive(:to_int).and_return(1) + obj.should_not_receive(:to_str) + + Integer(obj).should == 1 + end + + it "returns a result of to_str when both to_str and to_i are defined" do + obj = mock("obj") + obj.should_receive(:to_str).and_return("1") + obj.should_not_receive(:to_i) + + Integer(obj).should == 1 + end + it "raises a TypeError when passed nil" do - -> { Integer(nil) }.should.raise(TypeError) + -> { Integer(nil) }.should.raise(TypeError, "can't convert nil into Integer") end it "returns an Integer object" do @@ -67,18 +92,24 @@ it "raises a TypeError if to_i returns a value that is not an Integer" do obj = mock("object") obj.should_receive(:to_i).and_return("1") - -> { Integer(obj) }.should.raise(TypeError) + -> { + Integer(obj) + }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives String)") end it "raises a TypeError if no to_int or to_i methods exist" do obj = mock("object") - -> { Integer(obj) }.should.raise(TypeError) + -> { + Integer(obj) + }.should.raise(TypeError, "can't convert MockObject into Integer") end it "raises a TypeError if to_int returns nil and no to_i exists" do obj = mock("object") obj.should_receive(:to_i).and_return(nil) - -> { Integer(obj) }.should.raise(TypeError) + -> { + Integer(obj) + }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives nil)") end it "raises a FloatDomainError when passed NaN" do @@ -576,7 +607,7 @@ end it "raises an ArgumentError if a base is given for a non-String value" do - -> { Integer(98, 15) }.should.raise(ArgumentError) + -> { Integer(98, 15) }.should.raise(ArgumentError, "base specified for non string value") end it "tries to convert the base to an integer using to_int" do diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb index 24ee0745321a2f..04cf806ef4bdf8 100644 --- a/spec/ruby/core/kernel/caller_locations_spec.rb +++ b/spec/ruby/core/kernel/caller_locations_spec.rb @@ -103,7 +103,10 @@ loc = nil tap { loc = caller_locations(1, 1)[0] } loc.label.should == "Kernel#tap" - loc.path.should == __FILE__ + # CRuby hides the file which defines the method: https://bugs.ruby-lang.org/issues/20968 + unless loc.path == __FILE__ + loc.path.should.start_with? "} end + it "displays all instance variables if #instance_variables_to_inspect is not defined" do + obj = BasicObject.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + method_inspect = Kernel.instance_method(:inspect) + + inspected = method_inspect.bind(obj).call.sub(/^#} + end + it "raises an error if #instance_variables_to_inspect returns an invalid value" do obj = Object.new obj.instance_eval do diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb index 19c19e55948777..62d954c2bf7eee 100644 --- a/spec/ruby/core/kernel/require_spec.rb +++ b/spec/ruby/core/kernel/require_spec.rb @@ -26,30 +26,24 @@ end out = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems --disable-did-you-mean') - features = out.lines.map { |line| File.basename(line.chomp, '.*') } + features = out.lines.map(&:chomp) # Ignore engine-specific internals case RUBY_ENGINE when "jruby" - features -= %w[java util] - else - features -= %w[encdb transdb windows_1252 windows_31] + features -= %w[java.rb jruby/util.rb] + when "ruby" + so = RbConfig::CONFIG['DLEXT'] + features -= ["windows_1252.#{so}", "windows_31.#{so}"] + features.reject! { |feature| feature.end_with? "encdb.#{so}" } + features.reject! { |feature| feature.end_with? "transdb.#{so}" } + features.reject! { |feature| feature.include?('-fake') } end - features.reject! { |feature| feature.end_with?('-fake') } - features.sort.should == provided.sort - - requires = provided - ruby_version_is "4.0" do - if RUBY_ENGINE != "jruby" - requires = requires.map { |f| f == "pathname" ? "pathname.so" : f } - end - end - - ruby_version_is "4.1" do - requires = requires.map { |f| f == "monitor" ? "monitor.so" : f } - end + features_no_ext = features.map { |path| File.basename(path, '.*') } + features_no_ext.sort.should == provided.sort + requires = features code = requires.map { |f| "puts require #{f.inspect}\n" }.join required = ruby_exe(code, options: '--disable-gems') required.should == "false\n" * requires.size diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb index fe77b5f45bc4bf..b40bd95f594673 100644 --- a/spec/ruby/core/kernel/shared/sprintf.rb +++ b/spec/ruby/core/kernel/shared/sprintf.rb @@ -58,11 +58,6 @@ def obj.to_i; 10; end it "works well with large numbers" do @method.call("%#{f}", 1234567890987654321).should == "1234567890987654321" end - - it "converts to the empty string if precision is 0 and value is 0" do - @method.call("%.#{f}", 0).should == "" - @method.call("%.0#{f}", 0).should == "" - end end end @@ -110,6 +105,20 @@ def obj.to_i; 10; end @method.call("%X", -1).should == "..F" end end + + %w[b B d i u o x X].each do |f| + describe f do + it "converts to the empty string if precision is 0 and value is 0" do + @method.call("%.#{f}", 0).should == "" + @method.call("%.0#{f}", 0).should == "" + end + + it "pads the empty string if precision is 0 and value is 0" do + @method.call("%2.#{f}", 0).should == " " + @method.call("%2.0#{f}", 0).should == " " + end + end + end end describe "float formats" do @@ -299,6 +308,10 @@ def obj.to_i; 10; end @method.call("%c", "abc").should == "a" end + it "displays only the first character if argument is a string of several multibyte characters" do + @method.call("%c", "あいうえお").should == "あ" + end + it "displays no characters if argument is an empty string" do @method.call("%c", "").should == "" end @@ -594,11 +607,28 @@ def obj.to_str @method.call("%#b", 0).should == "0" @method.call("%#B", 0).should == "0" - @method.call("%#o", 0).should == "0" - @method.call("%#x", 0).should == "0" @method.call("%#X", 0).should == "0" end + + it "does nothing for zero argument when combined with zero precision" do + @method.call("%#.0b", 0).should == "" + @method.call("%#.0B", 0).should == "" + + @method.call("%#.0x", 0).should == "" + @method.call("%#.0X", 0).should == "" + end + end + + context "applies to format o" do + it "does nothing for zero argument" do + @method.call("%#o", 0).should == "0" + @method.call("%#.1o", 0).should == "0" + end + + it "increases the precision if precision zero is requested with zero argument" do + @method.call("%#.0o", 0).should == "0" + end end context "applies to formats aAeEfgG" do diff --git a/spec/ruby/core/method/inspect_spec.rb b/spec/ruby/core/method/inspect_spec.rb index e0fe1afdd0c0dc..97ff2d8c11de63 100644 --- a/spec/ruby/core/method/inspect_spec.rb +++ b/spec/ruby/core/method/inspect_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' require_relative 'shared/to_s' +require_relative 'shared/aliased_inspect' describe "Method#inspect" do it_behaves_like :method_to_s, :inspect + it_behaves_like :method_to_s_aliased, :inspect, -> meth { meth } end diff --git a/spec/ruby/core/method/original_name_spec.rb b/spec/ruby/core/method/original_name_spec.rb index 8fec0e7c33cee5..b92cf35154eff8 100644 --- a/spec/ruby/core/method/original_name_spec.rb +++ b/spec/ruby/core/method/original_name_spec.rb @@ -40,4 +40,20 @@ klass.new.method(:renamed).original_name.should == :my_method klass.new.method(:aliased).original_name.should == :my_method end + + it "returns the source UnboundMethod's name for Kernel#is_a? and Kernel#kind_of?" do + klass = Class.new { define_method(:my_is_a?, ::Kernel.instance_method(:is_a?)) } + klass.new.method(:my_is_a?).original_name.should == :is_a? + + klass = Class.new { define_method(:my_kind_of?, ::Kernel.instance_method(:kind_of?)) } + klass.new.method(:my_kind_of?).original_name.should == :kind_of? + end + + it "preserves the source name when aliasing a define_method'd Kernel method" do + klass = Class.new do + define_method(:my_is_a?, ::Kernel.instance_method(:is_a?)) + alias_method :renamed_is_a?, :my_is_a? + end + klass.new.method(:renamed_is_a?).original_name.should == :is_a? + end end diff --git a/spec/ruby/core/method/shared/aliased_inspect.rb b/spec/ruby/core/method/shared/aliased_inspect.rb new file mode 100644 index 00000000000000..2a622c2f97f93a --- /dev/null +++ b/spec/ruby/core/method/shared/aliased_inspect.rb @@ -0,0 +1,31 @@ +describe :method_to_s_aliased, shared: true do + # @object converts a bound Method to either a Method (identity) or an + # UnboundMethod (-> meth { meth.unbind }), so these expectations cover both + # Method#to_s/#inspect and UnboundMethod#to_s/#inspect. + + it "shows the original name in parentheses for an aliased method" do + klass = Class.new do + def original_method; end + alias_method :renamed_method, :original_method + end + @object.call(klass.new.method(:renamed_method)).send(@method).should.include? '#renamed_method(original_method)' + end + + it "shows the source UnboundMethod's name in parentheses for a define_method'd method" do + klass = Class.new { define_method(:renamed_is_a?, ::Kernel.instance_method(:is_a?)) } + @object.call(klass.new.method(:renamed_is_a?)).send(@method).should.include? '#renamed_is_a?(is_a?)' + end + + it "does not annotate a directly looked-up Kernel method with a shared internal name" do + @object.call(Object.new.method(:is_a?)).send(@method).should_not.include? '(kind_of?)' + @object.call(Object.new.method(:kind_of?)).send(@method).should_not.include? '(is_a?)' + end + + it "shows the source name when aliasing a define_method'd Kernel method" do + klass = Class.new do + define_method(:my_is_a?, ::Kernel.instance_method(:is_a?)) + alias_method :renamed_is_a?, :my_is_a? + end + @object.call(klass.new.method(:renamed_is_a?)).send(@method).should.include? '#renamed_is_a?(is_a?)' + end +end diff --git a/spec/ruby/core/method/to_s_spec.rb b/spec/ruby/core/method/to_s_spec.rb index 9f190113029559..ba0b4fa3c6361d 100644 --- a/spec/ruby/core/method/to_s_spec.rb +++ b/spec/ruby/core/method/to_s_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' require_relative 'shared/to_s' +require_relative 'shared/aliased_inspect' describe "Method#to_s" do it_behaves_like :method_to_s, :to_s + it_behaves_like :method_to_s_aliased, :to_s, -> meth { meth } end diff --git a/spec/ruby/core/mutex/sleep_spec.rb b/spec/ruby/core/mutex/sleep_spec.rb index a78e4c4b623db8..71b089d251713c 100644 --- a/spec/ruby/core/mutex/sleep_spec.rb +++ b/spec/ruby/core/mutex/sleep_spec.rb @@ -82,6 +82,14 @@ th.value.should.is_a?(Integer) end + it "accepts nil as a sleep duration" do + m = Mutex.new + -> { + m.lock + m.sleep(nil) + }.should block_caller + end + it "wakes up when requesting sleep times near or equal to zero" do times = [] val = 1 diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb index 62e8f2ee4ae910..5a4c478e7499a2 100644 --- a/spec/ruby/core/process/constants_spec.rb +++ b/spec/ruby/core/process/constants_spec.rb @@ -2,7 +2,7 @@ describe "Process::Constants" do platform_is :darwin, :netbsd, :freebsd do - it "are all present on BSD-like systems" do + describe "on BSD-like systems" do %i[ WNOHANG WUNTRACED @@ -20,27 +20,31 @@ RLIMIT_NPROC RLIMIT_NOFILE ].each do |const| - Process.const_defined?(const).should == true - Process.const_get(const).should.instance_of?(Integer) + it "defines #{const}" do + Process.const_defined?(const).should == true + Process.const_get(const).should.instance_of?(Integer) + end end end end platform_is :darwin do - it "are all present on Darwin" do + describe "on Darwin" do %i[ RLIM_SAVED_MAX RLIM_SAVED_CUR RLIMIT_AS ].each do |const| - Process.const_defined?(const).should == true - Process.const_get(const).should.instance_of?(Integer) + it "defines #{const}" do + Process.const_defined?(const).should == true + Process.const_get(const).should.instance_of?(Integer) + end end end end platform_is :linux do - it "are all present on Linux" do + describe "on Linux" do %i[ WNOHANG WUNTRACED @@ -61,37 +65,43 @@ RLIM_SAVED_MAX RLIM_SAVED_CUR ].each do |const| - Process.const_defined?(const).should == true - Process.const_get(const).should.instance_of?(Integer) + it "defines #{const}" do + Process.const_defined?(const).should == true + Process.const_get(const).should.instance_of?(Integer) + end end end end platform_is :netbsd, :freebsd do - it "are all present on NetBSD and FreeBSD" do + describe "on NetBSD and FreeBSD" do %i[ RLIMIT_SBSIZE RLIMIT_AS ].each do |const| - Process.const_defined?(const).should == true - Process.const_get(const).should.instance_of?(Integer) + it "defines #{const}" do + Process.const_defined?(const).should == true + Process.const_get(const).should.instance_of?(Integer) + end end end end platform_is :freebsd do - it "are all present on FreeBSD" do + describe "on FreeBSD" do %i[ RLIMIT_NPTS ].each do |const| - Process.const_defined?(const).should == true - Process.const_get(const).should.instance_of?(Integer) + it "defines #{const}" do + Process.const_defined?(const).should == true + Process.const_get(const).should.instance_of?(Integer) + end end end end platform_is :windows do - it "does not define RLIMIT constants" do + describe "on Windows" do %i[ RLIMIT_CPU RLIMIT_FSIZE @@ -107,7 +117,9 @@ RLIM_SAVED_MAX RLIM_SAVED_CUR ].each do |const| - Process.const_defined?(const).should == false + it "does not define #{const}" do + Process.const_defined?(const).should == false + end end end end diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb index d79c2b74c4aeee..127db876ad0848 100644 --- a/spec/ruby/core/string/shared/each_line.rb +++ b/spec/ruby/core/string/shared/each_line.rb @@ -46,14 +46,36 @@ a.should == ["one\ntwo\r\nthree"] end - it "yields paragraphs (broken by 2 or more successive newlines) when passed '' and replaces multiple newlines with only two ones" do - a = [] - "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s } - a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"] + context "when passed '' (paragraph mode, broken by 2 or more successive newlines)" do + it "replaces multiple newlines with only two ones" do + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"] - a = [] - "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s } - a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"] + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"] + end + + it 'handles \r\n-style newlines' do + a = [] + "hello\nworld\r\n\r\n\nand\nuniverse\n\r\n\n\n\n".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\r\n\r\n", "and\nuniverse\n\r\n"] + + a = [] + "hello\r\nworld\n\n\nand\nuniverse\n\n\n\r\n\r\ndog".send(@method, '') { |s| a << s } + a.should == ["hello\r\nworld\n\n", "and\nuniverse\n\n", "dog"] + end + + it "removes trailing newlines with `chomp: true`" do + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '', chomp: true) { |s| a << s } + a.should == ["hello\nworld", "and\nuniverse"] + + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '', chomp: true) { |s| a << s } + a.should == ["hello\nworld", "and\nuniverse", "dog"] + end end describe "uses $/" do diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb index 3b02a2e005481b..efc09d4a356a6e 100644 --- a/spec/ruby/core/thread/raise_spec.rb +++ b/spec/ruby/core/thread/raise_spec.rb @@ -136,6 +136,31 @@ def exception(*args) [ScratchPad.recorded, @thr, []] ] end + + it "calls #set_backtrace only in the caller thread" do + cls = Class.new(Exception) do + attr_accessor :log + def initialize(*args) + @log = [] # This is shared because the super #exception uses a shallow clone + super + end + + def set_backtrace(backtrace) + @log << [Thread.current, backtrace] + super + end + end + exc = cls.new + + backtrace = ["a.rb:1"] + + @thr.raise exc, "Thread#raise #set_backtrace spec", backtrace + @thr.join + ScratchPad.recorded.should.is_a?(cls) + exc.log.should == [ + [Thread.current, backtrace] + ] + end end describe "Thread#raise on a running thread" do diff --git a/spec/ruby/core/unboundmethod/inspect_spec.rb b/spec/ruby/core/unboundmethod/inspect_spec.rb index cecf542fcd4567..3abed94f7f82fc 100644 --- a/spec/ruby/core/unboundmethod/inspect_spec.rb +++ b/spec/ruby/core/unboundmethod/inspect_spec.rb @@ -1,7 +1,9 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/to_s' +require_relative '../method/shared/aliased_inspect' describe "UnboundMethod#inspect" do it_behaves_like :unboundmethod_to_s, :inspect + it_behaves_like :method_to_s_aliased, :inspect, -> meth { meth.unbind } end diff --git a/spec/ruby/core/unboundmethod/original_name_spec.rb b/spec/ruby/core/unboundmethod/original_name_spec.rb index fa9a6fcc637f50..cd5f55805dfa1a 100644 --- a/spec/ruby/core/unboundmethod/original_name_spec.rb +++ b/spec/ruby/core/unboundmethod/original_name_spec.rb @@ -40,4 +40,20 @@ klass.instance_method(:renamed).original_name.should == :my_method klass.instance_method(:aliased).original_name.should == :my_method end + + it "returns the source UnboundMethod's name for Kernel#is_a? and Kernel#kind_of?" do + klass = Class.new { define_method(:my_is_a?, ::Kernel.instance_method(:is_a?)) } + klass.instance_method(:my_is_a?).original_name.should == :is_a? + + klass = Class.new { define_method(:my_kind_of?, ::Kernel.instance_method(:kind_of?)) } + klass.instance_method(:my_kind_of?).original_name.should == :kind_of? + end + + it "preserves the source name when aliasing a define_method'd Kernel method" do + klass = Class.new do + define_method(:my_is_a?, ::Kernel.instance_method(:is_a?)) + alias_method :renamed_is_a?, :my_is_a? + end + klass.instance_method(:renamed_is_a?).original_name.should == :is_a? + end end diff --git a/spec/ruby/core/unboundmethod/to_s_spec.rb b/spec/ruby/core/unboundmethod/to_s_spec.rb index a508229b49c9c6..615d88675b1e69 100644 --- a/spec/ruby/core/unboundmethod/to_s_spec.rb +++ b/spec/ruby/core/unboundmethod/to_s_spec.rb @@ -1,7 +1,9 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/to_s' +require_relative '../method/shared/aliased_inspect' describe "UnboundMethod#to_s" do it_behaves_like :unboundmethod_to_s, :to_s + it_behaves_like :method_to_s_aliased, :to_s, -> meth { meth.unbind } end diff --git a/spec/ruby/language/array_spec.rb b/spec/ruby/language/array_spec.rb index 3601eb0e00ba21..78cf36c201cae7 100644 --- a/spec/ruby/language/array_spec.rb +++ b/spec/ruby/language/array_spec.rb @@ -155,8 +155,12 @@ def obj.to_a; [2, 3, 4]; end b = [1, 0] [*a, 3, *a, *b].should == [1, 2, 3, 1, 2, 1, 0] end -end - -describe "The packing splat operator (*)" do + ruby_version_is "4.0" do + it "does not call #to_a on nil" do + e = nil + e.should_not_receive(:to_a) + [*e].should == [] + end + end end diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index 8355062e446327..41881bf20ae111 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -27,6 +27,41 @@ def bar; @calls << :bar; end @calls.should == [:foo, :bar] end + it "matches an Integer literal whose value does not fit in a 32-bit int" do + big = 10_000_000_000 + case big + when 10_000_000_000; true + else false + end.should == true + + case -3_000_000_000 + when -3_000_000_000; true + else false + end.should == true + end + + it "matches an arbitrary-precision Integer literal" do + huge = 1267650600228229401496703205376 + case huge + when 1267650600228229401496703205376; true + else false + end.should == true + end + + it "dispatches correctly with mixed small and large Integer literals" do + pick = -> x { + case x + when 1267650600228229401496703205376 then :beyond_long + when 10_000_000_000 then :beyond_int + when 1 then :fits_int + else :other + end + } + + [1267650600228229401496703205376, 10_000_000_000, 1, :nope].map(&pick).should == + [:beyond_long, :beyond_int, :fits_int, :other] + end + it "evaluates the body of the when clause whose range expression includes the case target expression" do case 5 when 21..30; false diff --git a/spec/ruby/language/defined_spec.rb b/spec/ruby/language/defined_spec.rb index 3fd611d09e6f96..6846179a7c38b6 100644 --- a/spec/ruby/language/defined_spec.rb +++ b/spec/ruby/language/defined_spec.rb @@ -211,6 +211,22 @@ }.should complain(/warning: possibly useless use of defined\? in void context/, verbose: true) end end + + describe "for a protected method" do + it "returns 'method' when the receiver is a subclass instance" do + DefinedSpecs::ProtectedBase.new.defined_on(DefinedSpecs::ProtectedSubclass.new).should == "method" + end + + it "returns 'method' when the receiver is the base class instance" do + DefinedSpecs::ProtectedSubclass.new.defined_on(DefinedSpecs::ProtectedBase.new).should == "method" + end + + ruby_bug "#22076", ""..."4.1" do + it "returns 'method' when the receiver is a sibling class instance via a shared included module" do + DefinedSpecs::ProtectedIncluderA.new.defined_on(DefinedSpecs::ProtectedIncluderB.new).should == "method" + end + end + end end describe "The defined? keyword for an expression" do diff --git a/spec/ruby/language/fixtures/defined.rb b/spec/ruby/language/fixtures/defined.rb index 3761cfa5bd4087..15bd7c50cf41f2 100644 --- a/spec/ruby/language/fixtures/defined.rb +++ b/spec/ruby/language/fixtures/defined.rb @@ -299,6 +299,33 @@ def method_no_args super end end + + class ProtectedBase + def m; end + protected :m + def defined_on(o) + defined?(o.m) + end + end + + class ProtectedSubclass < ProtectedBase + end + + module ProtectedInModule + def m; end + protected :m + def defined_on(o) + defined?(o.m) + end + end + + class ProtectedIncluderA + include ProtectedInModule + end + + class ProtectedIncluderB + include ProtectedInModule + end end class Object diff --git a/spec/ruby/language/lambda_spec.rb b/spec/ruby/language/lambda_spec.rb index 2a2953bd97a8e9..c6239e32bb8afe 100644 --- a/spec/ruby/language/lambda_spec.rb +++ b/spec/ruby/language/lambda_spec.rb @@ -14,6 +14,12 @@ def create_lambda klass.new.create_lambda.should.instance_of?(Proc) end + it "is not just syntactic sugar for Kernel#lambda" do + should_not_receive(:lambda) + + -> {} + end + it "does not execute the block" do -> { fail }.should.instance_of?(Proc) end diff --git a/spec/ruby/library/socket/ipsocket/inspect_spec.rb b/spec/ruby/library/socket/ipsocket/inspect_spec.rb new file mode 100644 index 00000000000000..85780a16f6f35d --- /dev/null +++ b/spec/ruby/library/socket/ipsocket/inspect_spec.rb @@ -0,0 +1,24 @@ +require_relative '../spec_helper' + +describe 'IPSocket#inspect' do + it "returns a String with the fd, family, address and port for TCPSocket" do + @server = TCPServer.new("127.0.0.1", 0) + @socket = TCPSocket.new("127.0.0.1", @server.addr[1]) + port = @socket.addr[1] + + @socket.inspect.should == "#" + ensure + @socket&.close + @server&.close + end + + it 'returns a String with the fd, family, address and port for UDPSocket' do + @socket = UDPSocket.new + @socket.bind('127.0.0.1', 0) + port = @socket.addr[1] + + @socket.inspect.should == "#" + ensure + @socket&.close + end +end diff --git a/spec/ruby/library/socket/socket/tcp_spec.rb b/spec/ruby/library/socket/socket/tcp_spec.rb index f52198b0028be2..cc3c9381c7285d 100644 --- a/spec/ruby/library/socket/socket/tcp_spec.rb +++ b/spec/ruby/library/socket/socket/tcp_spec.rb @@ -56,15 +56,33 @@ it 'connects to the server' do @client = Socket.tcp(@host, @port) - @client.write('hello') - connection, _ = @server.accept - begin connection.recv(5).should == 'hello' ensure connection.close end end + + ruby_version_is "4.0" do + it 'connects to the server when passed open_timeout argument' do + @client = Socket.tcp(@host, @port, open_timeout: 60) + @client.write('open_timeout') + connection, _ = @server.accept + begin + connection.recv(12).should == 'open_timeout' + ensure + connection.close + end + end + + it 'raises Errno::ETIMEDOUT with :open_timeout when no server is listening on the given address' do + -> { + Socket.tcp("192.0.2.1", 80, open_timeout: 0) + }.should.raise(Errno::ETIMEDOUT) + rescue Errno::ENETUNREACH + skip "all network interfaces down" + end + end end diff --git a/spec/ruby/library/socket/spec_helper.rb b/spec/ruby/library/socket/spec_helper.rb index b33663e02da119..86f3a610869ecc 100644 --- a/spec/ruby/library/socket/spec_helper.rb +++ b/spec/ruby/library/socket/spec_helper.rb @@ -1,12 +1,14 @@ require_relative '../../spec_helper' require 'socket' -MSpec.enable_feature :sock_packet if Socket.const_defined?(:SOCK_PACKET) -MSpec.enable_feature :udp_cork if Socket.const_defined?(:UDP_CORK) -MSpec.enable_feature :tcp_cork if Socket.const_defined?(:TCP_CORK) -MSpec.enable_feature :pktinfo if Socket.const_defined?(:IP_PKTINFO) -MSpec.enable_feature :ipv6_pktinfo if Socket.const_defined?(:IPV6_PKTINFO) -MSpec.enable_feature :ip_mtu if Socket.const_defined?(:IP_MTU) -MSpec.enable_feature :ipv6_nexthop if Socket.const_defined?(:IPV6_NEXTHOP) -MSpec.enable_feature :tcp_info if Socket.const_defined?(:TCP_INFO) -MSpec.enable_feature :ancillary_data if Socket.const_defined?(:AncillaryData) +# We force enable all features on Linux because anyway Linux implements all these features, +# and we want a constant number of spec examples across Ruby implementations, even if they don't define these constants. +MSpec.enable_feature :sock_packet if platform_is(:linux) || Socket.const_defined?(:SOCK_PACKET) +MSpec.enable_feature :udp_cork if platform_is(:linux) || Socket.const_defined?(:UDP_CORK) +MSpec.enable_feature :tcp_cork if platform_is(:linux) || Socket.const_defined?(:TCP_CORK) +MSpec.enable_feature :pktinfo if platform_is(:linux) || Socket.const_defined?(:IP_PKTINFO) +MSpec.enable_feature :ipv6_pktinfo if platform_is(:linux) || Socket.const_defined?(:IPV6_PKTINFO) +MSpec.enable_feature :ip_mtu if platform_is(:linux) || Socket.const_defined?(:IP_MTU) +MSpec.enable_feature :ipv6_nexthop if platform_is(:linux) || Socket.const_defined?(:IPV6_NEXTHOP) +MSpec.enable_feature :tcp_info if platform_is(:linux) || Socket.const_defined?(:TCP_INFO) +MSpec.enable_feature :ancillary_data if platform_is(:linux) || Socket.const_defined?(:AncillaryData) diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb index cf4834526d391b..9c15dced4f2e63 100644 --- a/spec/ruby/library/socket/tcpsocket/shared/new.rb +++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb @@ -19,8 +19,17 @@ TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0) }.should.raise(IO::TimeoutError) rescue Errno::ENETUNREACH - # In the case all network interfaces down. - # raise_error cannot deal with multiple expected exceptions + skip "all network interfaces down" + end + + ruby_version_is "4.0" do + it 'raises IO::TimeoutError with :open_timeout when no server is listening on the given address' do + -> { + TCPSocket.send(@method, "192.0.2.1", 80, open_timeout: 0) + }.should.raise(IO::TimeoutError) + rescue Errno::ENETUNREACH + skip "all network interfaces down" + end end describe "with a running server" do @@ -98,5 +107,12 @@ @socket = TCPSocket.send(@method, @hostname, @server.port, connect_timeout: 1) @socket.should.instance_of?(TCPSocket) end + + ruby_version_is "4.0" do + it "connects to a server when passed open_timeout argument" do + @socket = TCPSocket.send(@method, @hostname, @server.port, open_timeout: 1) + @socket.should.instance_of?(TCPSocket) + end + end end end diff --git a/spec/ruby/library/socket/udpsocket/inspect_spec.rb b/spec/ruby/library/socket/udpsocket/inspect_spec.rb deleted file mode 100644 index e212120b1442df..00000000000000 --- a/spec/ruby/library/socket/udpsocket/inspect_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require_relative '../spec_helper' - -describe 'UDPSocket#inspect' do - before do - @socket = UDPSocket.new - @socket.bind('127.0.0.1', 0) - end - - after do - @socket.close - end - - it 'returns a String with the fd, family, address and port' do - port = @socket.addr[1] - @socket.inspect.should == "#" - end -end diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c index 2637ad27ac570a..0baa114d7b1a42 100644 --- a/spec/ruby/optional/capi/ext/gc_spec.c +++ b/spec/ruby/optional/capi/ext/gc_spec.c @@ -18,6 +18,32 @@ VALUE rb_gc_register_address_outside_init; VALUE rb_gc_register_mark_object_not_referenced_float; +static VALUE spec_RB_GC_GUARD_keep_alive(VALUE self, VALUE array_with_string) { + VALUE string = rb_ary_entry(array_with_string, 0); + char* ptr = RSTRING_PTR(string); + // Without the RB_GC_GUARD(string) below, string could be GC'd, and ptr become invalid + rb_gc(); + char copy[4]; + copy[0] = ptr[0]; + copy[1] = ptr[1]; + copy[2] = ptr[2]; + copy[3] = '\0'; + RB_GC_GUARD(string); + return rb_str_new_cstr(copy); +} + +static VALUE spec_RB_GC_GUARD(VALUE self, VALUE object) { + RB_GC_GUARD(object); + return object; +} + +static VALUE spec_RB_GC_GUARD_raw(VALUE self, VALUE number) { + long l = NUM2LONG(number); + VALUE value = (VALUE) l; + RB_GC_GUARD(value); + return Qnil; +} + static VALUE registered_tagged_address(VALUE self) { return registered_tagged_value; } @@ -124,6 +150,9 @@ void Init_gc_spec(void) { rb_gc_register_mark_object_not_referenced_float = DBL2NUM(1.61); rb_gc_register_mark_object(rb_gc_register_mark_object_not_referenced_float); + rb_define_method(cls, "RB_GC_GUARD_keep_alive", spec_RB_GC_GUARD_keep_alive, 1); + rb_define_method(cls, "RB_GC_GUARD", spec_RB_GC_GUARD, 1); + rb_define_method(cls, "RB_GC_GUARD_raw", spec_RB_GC_GUARD_raw, 1); rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0); rb_define_method(cls, "registered_reference_address", registered_reference_address, 0); rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0); diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb index 6695026c6f9d88..8b70ea4758a55e 100644 --- a/spec/ruby/optional/capi/gc_spec.rb +++ b/spec/ruby/optional/capi/gc_spec.rb @@ -7,6 +7,24 @@ @f = CApiGCSpecs.new end + describe "RB_GC_GUARD" do + it "forces an object to on stack so it cannot be GC'd early" do + @f.RB_GC_GUARD_keep_alive([%w[ab cd ef].join]).should == "abc" + end + + it "can be used with any Ruby object" do + @f.RB_GC_GUARD(true).should == true + @f.RB_GC_GUARD(42).should == 42 + @f.RB_GC_GUARD(self).should == self + end + + it "tolerates being passed invalid pointers" do + (0...256).each do |address| + @f.RB_GC_GUARD_raw(address).should == nil + end + end + end + describe "rb_gc_register_address" do it "correctly gets the value from a registered address" do @f.registered_tagged_address.should == 10 diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 3e095f05c83c44..569fc8891a1a9d 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -1149,7 +1149,7 @@ def inspect end it "can format a nil VALUE as a pointer and gives the same output as sprintf in C" do - res = @s.rb_sprintf7("%p", nil); + res = @s.rb_sprintf7("%p", nil) res[0].should == res[1] end @@ -1159,14 +1159,14 @@ def inspect end it "can format a raw number a pointer and gives the same output as sprintf in C" do - res = @s.rb_sprintf7("%p", 0x223643); + res = @s.rb_sprintf7("%p", 0x223643) res[0].should == res[1] end end describe "rb_vsprintf" do it "returns a formatted String from a variable number of arguments" do - s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7); + s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7) s.should == "abc, 42, 2.70" end end diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb index 07b6a30de2c5f1..84591669060c37 100644 --- a/spec/ruby/shared/kernel/raise.rb +++ b/spec/ruby/shared/kernel/raise.rb @@ -152,7 +152,7 @@ def e.exception it "supports automatic cause chaining from a previous exception" do begin - raise StandardError,"first error" + raise StandardError, "first error" rescue => cause -> { @object.raise("second error") }.should.raise(RuntimeError, "second error", cause:) end From 9e8ad9ac0489d8390bc11e5b18f4af065d60898c Mon Sep 17 00:00:00 2001 From: kai-matsudate Date: Sun, 31 May 2026 23:12:28 +0900 Subject: [PATCH 2/4] [ruby/prism] Reject modifier conditionals in `if`/`unless` predicates Prism accepted a modifier conditional as the predicate of an `if`/`unless` (and `elsif`), while parse.y rejects it: if a if b then end # parse.y: SyntaxError, prism: accepted unless a unless b then end The `if`/`unless`/`elsif` predicate was parsed with a floor of `PM_BINDING_POWER_MODIFIER`, which is inclusive of the modifier conditionals (`if`/`unless`/`while`/`until`, left binding power `PM_BINDING_POWER_MODIFIER`), so they were absorbed into the predicate. `while`/`until` predicates already use `PM_BINDING_POWER_COMPOSITION` and reject these correctly. Raise the floor to `PM_BINDING_POWER_MODIFIER + 1` so the predicate still absorbs `and`/`or` (and tighter operators) but excludes the modifier conditionals, matching parse.y and aligning `if`/`unless` with `while`/`until`. https://github.com/ruby/prism/commit/0fa8d8f8d8 --- prism/prism.c | 4 ++-- .../errors/modifier_conditional_in_predicate.txt | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 test/prism/errors/modifier_conditional_in_predicate.txt diff --git a/prism/prism.c b/prism/prism.c index a8bbcea09745c1..c9d501520aba02 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15486,7 +15486,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl pm_token_t keyword = parser->previous; pm_token_t then_keyword = { 0 }; - pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, context, &then_keyword, (uint16_t) (depth + 1)); + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER + 1, context, &then_keyword, (uint16_t) (depth + 1)); pm_statements_node_t *statements = NULL; if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { @@ -15524,7 +15524,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl pm_token_t elsif_keyword = parser->current; parser_lex(parser); - pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1)); + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER + 1, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1)); pm_accepts_block_stack_push(parser, true); pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1)); diff --git a/test/prism/errors/modifier_conditional_in_predicate.txt b/test/prism/errors/modifier_conditional_in_predicate.txt new file mode 100644 index 00000000000000..5b89ee4a260bfd --- /dev/null +++ b/test/prism/errors/modifier_conditional_in_predicate.txt @@ -0,0 +1,12 @@ +if a if b then end + ^~ expected `then` or `;` or '\n' + ^~ unexpected 'if', ignoring it + ^~~~ unexpected 'then', expecting end-of-input + ^~~~ unexpected 'then', ignoring it + +unless a unless b then end + ^~~~~~ expected `then` or `;` or '\n' + ^~~~~~ unexpected 'unless', ignoring it + ^~~~ unexpected 'then', expecting end-of-input + ^~~~ unexpected 'then', ignoring it + From c49e778a80e0979224d810f9909023b43f80a435 Mon Sep 17 00:00:00 2001 From: kai-matsudate Date: Tue, 2 Jun 2026 02:12:31 +0900 Subject: [PATCH 3/4] [ruby/prism] Use PM_BINDING_POWER_COMPOSITION for the conditional predicate floor The `+ 1` form is meant to express associativity, not a binding-power floor; use the named constant instead, matching while/until conditions. https://github.com/ruby/prism/commit/53d1ee76c6 --- prism/prism.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index c9d501520aba02..a2e04ed106db84 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15486,7 +15486,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl pm_token_t keyword = parser->previous; pm_token_t then_keyword = { 0 }; - pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER + 1, context, &then_keyword, (uint16_t) (depth + 1)); + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1)); pm_statements_node_t *statements = NULL; if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) { @@ -15524,7 +15524,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl pm_token_t elsif_keyword = parser->current; parser_lex(parser); - pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER + 1, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1)); + pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1)); pm_accepts_block_stack_push(parser, true); pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1)); From cc2547595ebd13e48b08e4f86c1d1a5d16f43756 Mon Sep 17 00:00:00 2001 From: Kunshan Wang Date: Mon, 1 Jun 2026 15:11:05 +0800 Subject: [PATCH 4/4] Ensure DTrace probes capture all GC marking events `gc_prof_mark_timer_start` and `gc_prof_mark_timer_stop` include DTrace hooks for the `MARK_BEGIN` and `MARK_END` events, respectively. Previously, those probes are only triggered in `gc_marks`. However, `gc_marks_continue` and `gc_rest` also contain marking activities, but are not captured by the probes. We move the invocation of `gc_prof_mark_timer_start` and `gc_prof_mark_timer_stop` into `gc_marking_enter` and `gc_marking_exit` to ensure all marking activities are captured by the probes. --- gc/default/default.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gc/default/default.c b/gc/default/default.c index 291ff91b810002..145140dfbfbc4e 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -5965,7 +5965,6 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark) static bool gc_marks(rb_objspace_t *objspace, int full_mark) { - gc_prof_mark_timer_start(objspace); gc_marking_enter(objspace); bool marking_finished = false; @@ -5986,7 +5985,6 @@ gc_marks(rb_objspace_t *objspace, int full_mark) #endif gc_marking_exit(objspace); - gc_prof_mark_timer_stop(objspace); return marking_finished; } @@ -6882,6 +6880,8 @@ gc_marking_enter(rb_objspace_t *objspace) { GC_ASSERT(during_gc != 0); + gc_prof_mark_timer_start(objspace); + if (MEASURE_GC) { gc_clock_start(&objspace->profile.marking_start_time); } @@ -6897,6 +6897,8 @@ gc_marking_exit(rb_objspace_t *objspace) if (MEASURE_GC) { objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time); } + + gc_prof_mark_timer_stop(objspace); } static void