summaryrefslogtreecommitdiff
path: root/spec/ruby/shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/shared')
-rw-r--r--spec/ruby/shared/file/executable.rb35
-rw-r--r--spec/ruby/shared/file/executable_real.rb35
-rw-r--r--spec/ruby/shared/file/exist.rb5
-rw-r--r--spec/ruby/shared/file/readable.rb16
-rw-r--r--spec/ruby/shared/file/readable_real.rb16
-rw-r--r--spec/ruby/shared/file/writable.rb16
-rw-r--r--spec/ruby/shared/file/writable_real.rb16
-rw-r--r--spec/ruby/shared/kernel/at_exit.rb70
-rw-r--r--spec/ruby/shared/kernel/complex.rb133
-rw-r--r--spec/ruby/shared/kernel/fixtures/END.rb3
-rw-r--r--spec/ruby/shared/kernel/fixtures/at_exit.rb3
-rw-r--r--spec/ruby/shared/kernel/object_id.rb28
-rw-r--r--spec/ruby/shared/kernel/raise.rb45
-rw-r--r--spec/ruby/shared/process/exit.rb12
-rw-r--r--spec/ruby/shared/queue/deque.rb81
-rw-r--r--spec/ruby/shared/rational/Rational.rb57
-rw-r--r--spec/ruby/shared/rational/coerce.rb34
-rw-r--r--spec/ruby/shared/rational/divmod.rb4
-rw-r--r--spec/ruby/shared/rational/exponent.rb8
-rw-r--r--spec/ruby/shared/rational/marshal_dump.rb5
-rw-r--r--spec/ruby/shared/rational/marshal_load.rb5
-rw-r--r--spec/ruby/shared/rational/minus.rb48
-rw-r--r--spec/ruby/shared/rational/quo.rb5
-rw-r--r--spec/ruby/shared/rational/to_f.rb6
-rw-r--r--spec/ruby/shared/rational/truncate.rb26
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb83
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb9
-rw-r--r--spec/ruby/shared/string/end_with.rb9
-rw-r--r--spec/ruby/shared/string/start_with.rb12
-rw-r--r--spec/ruby/shared/string/times.rb32
-rw-r--r--spec/ruby/shared/types/rb_num2dbl_fails.rb17
31 files changed, 705 insertions, 169 deletions
diff --git a/spec/ruby/shared/file/executable.rb b/spec/ruby/shared/file/executable.rb
index 7b5c4c580c..baa156de98 100644
--- a/spec/ruby/shared/file/executable.rb
+++ b/spec/ruby/shared/file/executable.rb
@@ -39,6 +39,41 @@ describe :file_executable, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ before :each do
+ @file = tmp('temp3.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if file owner has permission to execute" do
+ File.chmod(0766, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if group has permission to execute" do
+ File.chmod(0676, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if other have permission to execute" do
+ File.chmod(0667, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "return false if nobody has permission to execute" do
+ File.chmod(0666, @file)
+ @object.send(@method, @file).should == false
+ end
+ end
+ end
+ end
end
describe :file_executable_missing, shared: true do
diff --git a/spec/ruby/shared/file/executable_real.rb b/spec/ruby/shared/file/executable_real.rb
index ce3d5ca176..bf2734ea07 100644
--- a/spec/ruby/shared/file/executable_real.rb
+++ b/spec/ruby/shared/file/executable_real.rb
@@ -37,6 +37,41 @@ describe :file_executable_real, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ before :each do
+ @file = tmp('temp3.txt')
+ touch @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns true if file owner has permission to execute" do
+ File.chmod(0766, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if group has permission to execute" do
+ File.chmod(0676, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "returns true if other have permission to execute" do
+ File.chmod(0667, @file)
+ @object.send(@method, @file).should == true
+ end
+
+ it "return false if nobody has permission to execute" do
+ File.chmod(0666, @file)
+ @object.send(@method, @file).should == false
+ end
+ end
+ end
+ end
end
describe :file_executable_real_missing, shared: true do
diff --git a/spec/ruby/shared/file/exist.rb b/spec/ruby/shared/file/exist.rb
index 3bd97711b4..67424146c5 100644
--- a/spec/ruby/shared/file/exist.rb
+++ b/spec/ruby/shared/file/exist.rb
@@ -4,11 +4,6 @@ describe :file_exist, shared: true do
@object.send(@method, 'a_fake_file').should == false
end
- it "returns true if the file exist using the alias exists?" do
- @object.send(@method, __FILE__).should == true
- @object.send(@method, 'a_fake_file').should == false
- end
-
it "raises an ArgumentError if not passed one argument" do
-> { @object.send(@method) }.should raise_error(ArgumentError)
-> { @object.send(@method, __FILE__, __FILE__) }.should raise_error(ArgumentError)
diff --git a/spec/ruby/shared/file/readable.rb b/spec/ruby/shared/file/readable.rb
index eb2ca06812..7b45e23e36 100644
--- a/spec/ruby/shared/file/readable.rb
+++ b/spec/ruby/shared/file/readable.rb
@@ -24,6 +24,22 @@ describe :file_readable, shared: true do
it "accepts an object that has a #to_path method" do
@object.send(@method, mock_to_path(@file2)).should == true
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0333, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_readable_missing, shared: true do
diff --git a/spec/ruby/shared/file/readable_real.rb b/spec/ruby/shared/file/readable_real.rb
index b6e53ac76d..32d38bc7a2 100644
--- a/spec/ruby/shared/file/readable_real.rb
+++ b/spec/ruby/shared/file/readable_real.rb
@@ -14,6 +14,22 @@ describe :file_readable_real, shared: true do
it "accepts an object that has a #to_path method" do
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0333, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_readable_real_missing, shared: true do
diff --git a/spec/ruby/shared/file/writable.rb b/spec/ruby/shared/file/writable.rb
index 4bb8aedce6..65ea2c1781 100644
--- a/spec/ruby/shared/file/writable.rb
+++ b/spec/ruby/shared/file/writable.rb
@@ -19,6 +19,22 @@ describe :file_writable, shared: true do
it "accepts an object that has a #to_path method" do
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
end
+
+ platform_is_not :windows do
+ as_superuser do
+ context "when run by a superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0555, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_writable_missing, shared: true do
diff --git a/spec/ruby/shared/file/writable_real.rb b/spec/ruby/shared/file/writable_real.rb
index e9721fd379..b4a0a58c6e 100644
--- a/spec/ruby/shared/file/writable_real.rb
+++ b/spec/ruby/shared/file/writable_real.rb
@@ -24,6 +24,22 @@ describe :file_writable_real, shared: true do
-> { @object.send(@method, nil) }.should raise_error(TypeError)
-> { @object.send(@method, false) }.should raise_error(TypeError)
end
+
+ platform_is_not :windows do
+ as_real_superuser do
+ context "when run by a real superuser" do
+ it "returns true unconditionally" do
+ file = tmp('temp.txt')
+ touch file
+
+ File.chmod(0555, file)
+ @object.send(@method, file).should == true
+
+ rm_r file
+ end
+ end
+ end
+ end
end
describe :file_writable_real_missing, shared: true do
diff --git a/spec/ruby/shared/kernel/at_exit.rb b/spec/ruby/shared/kernel/at_exit.rb
new file mode 100644
index 0000000000..16d41cb01c
--- /dev/null
+++ b/spec/ruby/shared/kernel/at_exit.rb
@@ -0,0 +1,70 @@
+describe :kernel_at_exit, shared: true do
+ it "runs after all other code" do
+ ruby_exe("#{@method} { print 5 }; print 6").should == "65"
+ end
+
+ it "runs in reverse order of registration" do
+ code = "#{@method} { print 4 }; #{@method} { print 5 }; print 6; #{@method} { print 7 }"
+ ruby_exe(code).should == "6754"
+ end
+
+ it "allows calling exit inside a handler" do
+ code = "#{@method} { print 3 }; #{@method} { print 4; exit; print 5 }; #{@method} { print 6 }"
+ ruby_exe(code).should == "643"
+ end
+
+ it "gives access to the last raised exception - global variables $! and $@" do
+ code = <<-EOC
+ #{@method} {
+ puts "The exception matches: \#{$! == $exception && $@ == $exception.backtrace} (message=\#{$!.message})"
+ }
+
+ begin
+ raise "foo"
+ rescue => $exception
+ raise
+ end
+ EOC
+
+ result = ruby_exe(code, args: "2>&1", exit_status: 1)
+ result.lines.should.include?("The exception matches: true (message=foo)\n")
+ end
+
+ it "both exceptions in a handler and in the main script are printed" do
+ code = "#{@method} { raise 'at_exit_error' }; raise 'main_script_error'"
+ result = ruby_exe(code, args: "2>&1", exit_status: 1)
+ result.should.include?('at_exit_error (RuntimeError)')
+ result.should.include?('main_script_error (RuntimeError)')
+ end
+
+ it "decides the exit status if both at_exit and the main script raise SystemExit" do
+ ruby_exe("#{@method} { exit 43 }; exit 42", args: "2>&1", exit_status: 43)
+ $?.exitstatus.should == 43
+ end
+
+ it "runs all handlers even if some raise exceptions" do
+ code = "#{@method} { STDERR.puts 'last' }; #{@method} { exit 43 }; #{@method} { STDERR.puts 'first' }; exit 42"
+ result = ruby_exe(code, args: "2>&1", exit_status: 43)
+ result.should == "first\nlast\n"
+ $?.exitstatus.should == 43
+ end
+
+ it "runs handlers even if the main script fails to parse" do
+ script = fixture(__FILE__, "#{@method}.rb")
+ result = ruby_exe('{', options: "-r#{script}", args: "2>&1", exit_status: 1)
+ $?.should_not.success?
+ result.should.include?("handler ran\n")
+
+ # it's tempting not to rely on error message and rely only on exception class name,
+ # but CRuby before 3.2 doesn't print class name for syntax error
+ result.should include_any_of("syntax error", "SyntaxError")
+ end
+
+ it "calls the nested handler right after the outer one if a handler is nested into another handler" do
+ ruby_exe(<<~ruby).should == "last\nbefore\nafter\nnested\nfirst\n"
+ #{@method} { puts :first }
+ #{@method} { puts :before; #{@method} { puts :nested }; puts :after };
+ #{@method} { puts :last }
+ ruby
+ end
+end
diff --git a/spec/ruby/shared/kernel/complex.rb b/spec/ruby/shared/kernel/complex.rb
new file mode 100644
index 0000000000..98ee0b2b3f
--- /dev/null
+++ b/spec/ruby/shared/kernel/complex.rb
@@ -0,0 +1,133 @@
+# Specs shared by Kernel#Complex() and String#to_c()
+describe :kernel_complex, shared: true do
+
+ it "returns a Complex object" do
+ @object.send(@method, '9').should be_an_instance_of(Complex)
+ end
+
+ it "understands integers" do
+ @object.send(@method, '20').should == Complex(20)
+ end
+
+ it "understands negative integers" do
+ @object.send(@method, '-3').should == Complex(-3)
+ end
+
+ it "understands fractions (numerator/denominator) for the real part" do
+ @object.send(@method, '2/3').should == Complex(Rational(2, 3))
+ end
+
+ it "understands fractions (numerator/denominator) for the imaginary part" do
+ @object.send(@method, '4+2/3i').should == Complex(4, Rational(2, 3))
+ end
+
+ it "understands negative fractions (-numerator/denominator) for the real part" do
+ @object.send(@method, '-2/3').should == Complex(Rational(-2, 3))
+ end
+
+ it "understands negative fractions (-numerator/denominator) for the imaginary part" do
+ @object.send(@method, '7-2/3i').should == Complex(7, Rational(-2, 3))
+ end
+
+ it "understands floats (a.b) for the real part" do
+ @object.send(@method, '2.3').should == Complex(2.3)
+ end
+
+ it "understands floats (a.b) for the imaginary part" do
+ @object.send(@method, '4+2.3i').should == Complex(4, 2.3)
+ end
+
+ it "understands negative floats (-a.b) for the real part" do
+ @object.send(@method, '-2.33').should == Complex(-2.33)
+ end
+
+ it "understands negative floats (-a.b) for the imaginary part" do
+ @object.send(@method, '7-28.771i').should == Complex(7, -28.771)
+ end
+
+ it "understands an integer followed by 'i' to mean that integer is the imaginary part" do
+ @object.send(@method, '35i').should == Complex(0,35)
+ end
+
+ it "understands a negative integer followed by 'i' to mean that negative integer is the imaginary part" do
+ @object.send(@method, '-29i').should == Complex(0,-29)
+ end
+
+ it "understands an 'i' by itself as denoting a complex number with an imaginary part of 1" do
+ @object.send(@method, 'i').should == Complex(0,1)
+ end
+
+ it "understands a '-i' by itself as denoting a complex number with an imaginary part of -1" do
+ @object.send(@method, '-i').should == Complex(0,-1)
+ end
+
+ it "understands 'a+bi' to mean a complex number with 'a' as the real part, 'b' as the imaginary" do
+ @object.send(@method, '79+4i').should == Complex(79,4)
+ end
+
+ it "understands 'a-bi' to mean a complex number with 'a' as the real part, '-b' as the imaginary" do
+ @object.send(@method, '79-4i').should == Complex(79,-4)
+ end
+
+ it "understands 'a+i' to mean a complex number with 'a' as the real part, 1i as the imaginary" do
+ @object.send(@method, '79+i').should == Complex(79, 1)
+ end
+
+ it "understands 'a-i' to mean a complex number with 'a' as the real part, -1i as the imaginary" do
+ @object.send(@method, '79-i').should == Complex(79, -1)
+ end
+
+ it "understands i, I, j, and J imaginary units" do
+ @object.send(@method, '79+4i').should == Complex(79, 4)
+ @object.send(@method, '79+4I').should == Complex(79, 4)
+ @object.send(@method, '79+4j').should == Complex(79, 4)
+ @object.send(@method, '79+4J').should == Complex(79, 4)
+ end
+
+ it "understands scientific notation for the real part" do
+ @object.send(@method, '2e3+4i').should == Complex(2e3,4)
+ end
+
+ it "understands negative scientific notation for the real part" do
+ @object.send(@method, '-2e3+4i').should == Complex(-2e3,4)
+ end
+
+ it "understands scientific notation for the imaginary part" do
+ @object.send(@method, '4+2e3i').should == Complex(4, 2e3)
+ end
+
+ it "understands negative scientific notation for the imaginary part" do
+ @object.send(@method, '4-2e3i').should == Complex(4, -2e3)
+ end
+
+ it "understands scientific notation for the real and imaginary part in the same String" do
+ @object.send(@method, '2e3+2e4i').should == Complex(2e3,2e4)
+ end
+
+ it "understands negative scientific notation for the real and imaginary part in the same String" do
+ @object.send(@method, '-2e3-2e4i').should == Complex(-2e3,-2e4)
+ end
+
+ it "understands scientific notation with e and E" do
+ @object.send(@method, '2e3+2e4i').should == Complex(2e3, 2e4)
+ @object.send(@method, '2E3+2E4i').should == Complex(2e3, 2e4)
+ end
+
+ it "understands 'm@a' to mean a complex number in polar form with 'm' as the modulus, 'a' as the argument" do
+ @object.send(@method, '79@4').should == Complex.polar(79, 4)
+ @object.send(@method, '-79@4').should == Complex.polar(-79, 4)
+ @object.send(@method, '79@-4').should == Complex.polar(79, -4)
+ end
+
+ it "ignores leading whitespaces" do
+ @object.send(@method, ' 79+4i').should == Complex(79, 4)
+ end
+
+ it "ignores trailing whitespaces" do
+ @object.send(@method, '79+4i ').should == Complex(79, 4)
+ end
+
+ it "understands _" do
+ @object.send(@method, '7_9+4_0i').should == Complex(79, 40)
+ end
+end
diff --git a/spec/ruby/shared/kernel/fixtures/END.rb b/spec/ruby/shared/kernel/fixtures/END.rb
new file mode 100644
index 0000000000..cc8ac17c36
--- /dev/null
+++ b/spec/ruby/shared/kernel/fixtures/END.rb
@@ -0,0 +1,3 @@
+END {
+ STDERR.puts "handler ran"
+}
diff --git a/spec/ruby/shared/kernel/fixtures/at_exit.rb b/spec/ruby/shared/kernel/fixtures/at_exit.rb
new file mode 100644
index 0000000000..e7bc8baf52
--- /dev/null
+++ b/spec/ruby/shared/kernel/fixtures/at_exit.rb
@@ -0,0 +1,3 @@
+at_exit do
+ STDERR.puts "handler ran"
+end
diff --git a/spec/ruby/shared/kernel/object_id.rb b/spec/ruby/shared/kernel/object_id.rb
index 7acdb27554..099df8ff94 100644
--- a/spec/ruby/shared/kernel/object_id.rb
+++ b/spec/ruby/shared/kernel/object_id.rb
@@ -52,10 +52,30 @@ describe :object_id, shared: true do
o1.send(@method).should_not == o2.send(@method)
end
- it "returns a different value for two String literals" do
- o1 = "hello"
- o2 = "hello"
- o1.send(@method).should_not == o2.send(@method)
+ guard -> { "test".frozen? && "test".equal?("test") } do # --enable-frozen-string-literal in $RUBYOPT
+ it "returns the same value for two identical String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should == o2.send(@method)
+ end
+ end
+
+ guard -> { "test".frozen? && !"test".equal?("test") } do # chilled string literals
+ it "returns a different frozen value for two String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should_not == o2.send(@method)
+ o1.frozen?.should == true
+ o2.frozen?.should == true
+ end
+ end
+
+ guard -> { !"test".frozen? } do
+ it "returns a different value for two String literals" do
+ o1 = "hello"
+ o2 = "hello"
+ o1.send(@method).should_not == o2.send(@method)
+ end
end
it "returns a different value for an object and its dup" do
diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb
index 765ba0f929..1917a4c923 100644
--- a/spec/ruby/shared/kernel/raise.rb
+++ b/spec/ruby/shared/kernel/raise.rb
@@ -29,11 +29,41 @@ describe :kernel_raise, shared: true do
@data = data
end
end
- -> { @object.raise(data_error, {:data => 42}) }.should raise_error(data_error) do |ex|
- ex.data.should == {:data => 42}
+
+ -> { @object.raise(data_error, {data: 42}) }.should raise_error(data_error) do |ex|
+ ex.data.should == {data: 42}
end
end
+ # https://bugs.ruby-lang.org/issues/8257#note-36
+ it "allows extra keyword arguments for compatibility" do
+ data_error = Class.new(StandardError) do
+ attr_reader :data
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ -> { @object.raise(data_error, data: 42) }.should raise_error(data_error) do |ex|
+ ex.data.should == {data: 42}
+ end
+ end
+
+ it "does not allow message and extra keyword arguments" do
+ data_error = Class.new(StandardError) do
+ attr_reader :data
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ -> { @object.raise(data_error, {a: 1}, b: 2) }.should raise_error(StandardError) do |e|
+ [TypeError, ArgumentError].should.include?(e.class)
+ end
+
+ -> { @object.raise(data_error, {a: 1}, [], b: 2) }.should raise_error(ArgumentError)
+ end
+
it "raises RuntimeError if no exception class is given" do
-> { @object.raise }.should raise_error(RuntimeError, "")
end
@@ -116,4 +146,15 @@ describe :kernel_raise, shared: true do
@object.raise(ArgumentError, "message", caller)
end.should raise_error(ArgumentError, "message")
end
+
+ ruby_version_is "3.4" do
+ locations = caller_locations(1, 2)
+ it "allows Exception, message, and backtrace_locations parameters" do
+ -> do
+ @object.raise(ArgumentError, "message", locations)
+ end.should raise_error(ArgumentError, "message") { |error|
+ error.backtrace_locations.map(&:to_s).should == locations.map(&:to_s)
+ }
+ end
+ end
end
diff --git a/spec/ruby/shared/process/exit.rb b/spec/ruby/shared/process/exit.rb
index ae8abaea40..1e073614a3 100644
--- a/spec/ruby/shared/process/exit.rb
+++ b/spec/ruby/shared/process/exit.rb
@@ -21,6 +21,12 @@ describe :process_exit, shared: true do
end
end
+ it "raises a SystemExit with message 'exit'" do
+ -> { @object.exit }.should raise_error(SystemExit) { |e|
+ e.message.should == "exit"
+ }
+ end
+
it "tries to convert the passed argument to an Integer using #to_int" do
obj = mock('5')
obj.should_receive(:to_int).and_return(5)
@@ -98,6 +104,12 @@ describe :process_exit!, shared: true do
$?.exitstatus.should == 21
end
+ it "skips ensure clauses" do
+ out = ruby_exe("begin; STDERR.puts 'before'; #{@object}.send(:exit!, 21); ensure; STDERR.puts 'ensure'; end", args: '2>&1', exit_status: 21)
+ out.should == "before\n"
+ $?.exitstatus.should == 21
+ end
+
it "overrides the original exception and exit status when called from #at_exit" do
code = <<-RUBY
at_exit do
diff --git a/spec/ruby/shared/queue/deque.rb b/spec/ruby/shared/queue/deque.rb
index 8b755dd9b7..0abba5301e 100644
--- a/spec/ruby/shared/queue/deque.rb
+++ b/spec/ruby/shared/queue/deque.rb
@@ -37,6 +37,15 @@ describe :queue_deq, shared: true do
q.send(@method).should == 1
end
+ it "converts false-ish for non_blocking to boolean" do
+ q = @object.call
+ q << 1
+ q << 2
+
+ q.send(@method, false).should == 1
+ q.send(@method, nil).should == 2
+ end
+
it "returns nil for a closed empty queue" do
q = @object.call
q.close
@@ -55,6 +64,70 @@ describe :queue_deq, shared: true do
t.join
end
+ describe "with a timeout" do
+ ruby_version_is "3.2" do
+ it "returns an item if one is available in time" do
+ q = @object.call
+
+ t = Thread.new {
+ q.send(@method, timeout: TIME_TOLERANCE).should == 1
+ }
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q << 1
+ t.join
+ end
+
+ it "returns nil if no item is available in time" do
+ q = @object.call
+
+ Thread.new {
+ q.send(@method, timeout: 0.001).should == nil
+ }.join
+ end
+
+ it "does nothing if the timeout is nil" do
+ q = @object.call
+ t = Thread.new {
+ q.send(@method, timeout: nil).should == 1
+ }
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q << 1
+ t.join
+ end
+
+ it "immediately returns nil if no item is available and the timeout is 0" do
+ q = @object.call
+ q << 1
+ q.send(@method, timeout: 0).should == 1
+ q.send(@method, timeout: 0).should == nil
+ end
+
+ it "raise TypeError if timeout is not a valid numeric" do
+ q = @object.call
+ -> {
+ q.send(@method, timeout: "1")
+ }.should raise_error(TypeError, "no implicit conversion to float from string")
+
+ -> {
+ q.send(@method, timeout: false)
+ }.should raise_error(TypeError, "no implicit conversion to float from false")
+ end
+
+ it "raise ArgumentError if non_block = true is passed too" do
+ q = @object.call
+ -> {
+ q.send(@method, true, timeout: 1)
+ }.should raise_error(ArgumentError, "can't set a timeout if non_block is enabled")
+ end
+
+ it "returns nil for a closed empty queue" do
+ q = @object.call
+ q.close
+ q.send(@method, timeout: 0).should == nil
+ end
+ end
+ end
+
describe "in non-blocking mode" do
it "removes an item from the queue" do
q = @object.call
@@ -81,5 +154,13 @@ describe :queue_deq, shared: true do
q.close
-> { q.send(@method, true) }.should raise_error(ThreadError)
end
+
+ it "converts true-ish non_blocking argument to true" do
+ q = @object.call
+
+ -> { q.send(@method, true) }.should raise_error(ThreadError)
+ -> { q.send(@method, 1) }.should raise_error(ThreadError)
+ -> { q.send(@method, "") }.should raise_error(ThreadError)
+ end
end
end
diff --git a/spec/ruby/shared/rational/Rational.rb b/spec/ruby/shared/rational/Rational.rb
index 936a90c086..500f7ed271 100644
--- a/spec/ruby/shared/rational/Rational.rb
+++ b/spec/ruby/shared/rational/Rational.rb
@@ -65,40 +65,45 @@ describe :kernel_Rational, shared: true do
r_s.should == r
r_s.should_not == f_r
end
+ end
- describe "when passed a Numeric" do
- it "calls #to_r to convert the first argument to a Rational" do
- num = RationalSpecs::SubNumeric.new(2)
+ describe "when passed a Numeric" do
+ it "calls #to_r to convert the first argument to a Rational" do
+ num = RationalSpecs::SubNumeric.new(2)
- Rational(num).should == Rational(2)
- end
+ Rational(num).should == Rational(2)
end
+ end
- describe "when passed a Complex" do
- it "returns a Rational from the real part if the imaginary part is 0" do
- Rational(Complex(1, 0)).should == Rational(1)
- end
-
- it "raises a RangeError if the imaginary part is not 0" do
- -> { Rational(Complex(1, 2)) }.should raise_error(RangeError)
- end
+ describe "when passed a Complex" do
+ it "returns a Rational from the real part if the imaginary part is 0" do
+ Rational(Complex(1, 0)).should == Rational(1)
end
- it "raises a TypeError if the first argument is nil" do
- -> { Rational(nil) }.should raise_error(TypeError)
+ it "raises a RangeError if the imaginary part is not 0" do
+ -> { Rational(Complex(1, 2)) }.should raise_error(RangeError)
end
+ end
- it "raises a TypeError if the second argument is nil" do
- -> { Rational(1, nil) }.should raise_error(TypeError)
- end
+ it "raises a ZeroDivisionError if the second argument is 0" do
+ -> { Rational(1, 0) }.should raise_error(ZeroDivisionError, "divided by 0")
+ -> { Rational(1, 0.0) }.should raise_error(ZeroDivisionError, "divided by 0")
+ end
- it "raises a TypeError if the first argument is a Symbol" do
- -> { Rational(:sym) }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the first argument is nil" do
+ -> { Rational(nil) }.should raise_error(TypeError)
+ end
- it "raises a TypeError if the second argument is a Symbol" do
- -> { Rational(1, :sym) }.should raise_error(TypeError)
- end
+ it "raises a TypeError if the second argument is nil" do
+ -> { Rational(1, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the first argument is a Symbol" do
+ -> { Rational(:sym) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is a Symbol" do
+ -> { Rational(1, :sym) }.should raise_error(TypeError)
end
describe "when passed exception: false" do
@@ -138,4 +143,8 @@ describe :kernel_Rational, shared: true do
end
end
end
+
+ it "freezes its result" do
+ Rational(1).frozen?.should == true
+ end
end
diff --git a/spec/ruby/shared/rational/coerce.rb b/spec/ruby/shared/rational/coerce.rb
deleted file mode 100644
index ccc8901ba0..0000000000
--- a/spec/ruby/shared/rational/coerce.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require_relative '../../spec_helper'
-
-require 'bigdecimal'
-
-describe :rational_coerce, shared: true do
- it "returns the passed argument, self as Float, when given a Float" do
- result = Rational(3, 4).coerce(1.0)
- result.should == [1.0, 0.75]
- result.first.is_a?(Float).should be_true
- result.last.is_a?(Float).should be_true
- end
-
- it "returns the passed argument, self as Rational, when given an Integer" do
- result = Rational(3, 4).coerce(10)
- result.should == [Rational(10, 1), Rational(3, 4)]
- result.first.is_a?(Rational).should be_true
- result.last.is_a?(Rational).should be_true
- end
-
- it "coerces to Rational, when given a Complex" do
- Rational(3, 4).coerce(Complex(5)).should == [Rational(5, 1), Rational(3, 4)]
- Rational(12, 4).coerce(Complex(5, 1)).should == [Complex(5, 1), Complex(3)]
- end
-
- it "returns [argument, self] when given a Rational" do
- Rational(3, 7).coerce(Rational(9, 2)).should == [Rational(9, 2), Rational(3, 7)]
- end
-
- it "raises an error when passed a BigDecimal" do
- -> {
- Rational(500, 3).coerce(BigDecimal('166.666666666'))
- }.should raise_error(TypeError, /BigDecimal can't be coerced into Rational/)
- end
-end
diff --git a/spec/ruby/shared/rational/divmod.rb b/spec/ruby/shared/rational/divmod.rb
index 471cd7a967..9e23a18186 100644
--- a/spec/ruby/shared/rational/divmod.rb
+++ b/spec/ruby/shared/rational/divmod.rb
@@ -6,7 +6,7 @@ describe :rational_divmod_rat, shared: true do
Rational(7, 4).divmod(Rational(-1, 2)).should eql([-4, Rational(-1, 4)])
Rational(0, 4).divmod(Rational(4, 3)).should eql([0, Rational(0, 1)])
- Rational(bignum_value, 4).divmod(Rational(4, 3)).should eql([1729382256910270464, Rational(0, 1)])
+ Rational(bignum_value, 4).divmod(Rational(4, 3)).should eql([3458764513820540928, Rational(0, 1)])
end
it "raises a ZeroDivisionError when passed a Rational with a numerator of 0" do
@@ -19,7 +19,7 @@ describe :rational_divmod_int, shared: true do
Rational(7, 4).divmod(2).should eql([0, Rational(7, 4)])
Rational(7, 4).divmod(-2).should eql([-1, Rational(-1, 4)])
- Rational(bignum_value, 4).divmod(3).should == [768614336404564650, Rational(2, 1)]
+ Rational(bignum_value, 4).divmod(3).should eql([1537228672809129301, Rational(1, 1)])
end
it "raises a ZeroDivisionError when passed 0" do
diff --git a/spec/ruby/shared/rational/exponent.rb b/spec/ruby/shared/rational/exponent.rb
index 3fd02de08f..b0e9b23574 100644
--- a/spec/ruby/shared/rational/exponent.rb
+++ b/spec/ruby/shared/rational/exponent.rb
@@ -40,10 +40,10 @@ describe :rational_exponent, shared: true do
(Rational(-3, 4) ** -4).should == Rational(256, 81)
(Rational(3, -4) ** -4).should == Rational(256, 81)
- (Rational(bignum_value, 4) ** 4).should == Rational(28269553036454149273332760011886696253239742350009903329945699220681916416, 1)
- (Rational(3, bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81)
- (Rational(-bignum_value, 4) ** -4).should == Rational(1, 28269553036454149273332760011886696253239742350009903329945699220681916416)
- (Rational(3, -bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81)
+ (Rational(bignum_value, 4) ** 4).should == Rational(452312848583266388373324160190187140051835877600158453279131187530910662656, 1)
+ (Rational(3, bignum_value) ** -4).should == Rational(115792089237316195423570985008687907853269984665640564039457584007913129639936, 81)
+ (Rational(-bignum_value, 4) ** -4).should == Rational(1, 452312848583266388373324160190187140051835877600158453279131187530910662656)
+ (Rational(3, -bignum_value) ** -4).should == Rational(115792089237316195423570985008687907853269984665640564039457584007913129639936, 81)
end
# Guard against the Mathn library
diff --git a/spec/ruby/shared/rational/marshal_dump.rb b/spec/ruby/shared/rational/marshal_dump.rb
deleted file mode 100644
index 09782b45a5..0000000000
--- a/spec/ruby/shared/rational/marshal_dump.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_marshal_dump, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/rational/marshal_load.rb b/spec/ruby/shared/rational/marshal_load.rb
deleted file mode 100644
index 20bdd6fdf4..0000000000
--- a/spec/ruby/shared/rational/marshal_load.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_marshal_load, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/rational/minus.rb b/spec/ruby/shared/rational/minus.rb
deleted file mode 100644
index 0a0946fdb9..0000000000
--- a/spec/ruby/shared/rational/minus.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_minus_rat, shared: true do
- it "returns the result of subtracting other from self as a Rational" do
- (Rational(3, 4) - Rational(0, 1)).should eql(Rational(3, 4))
- (Rational(3, 4) - Rational(1, 4)).should eql(Rational(1, 2))
-
- (Rational(3, 4) - Rational(2, 1)).should eql(Rational(-5, 4))
- end
-end
-
-describe :rational_minus_int, shared: true do
- it "returns the result of subtracting other from self as a Rational" do
- (Rational(3, 4) - 1).should eql(Rational(-1, 4))
- (Rational(3, 4) - 2).should eql(Rational(-5, 4))
- end
-end
-
-describe :rational_minus_float, shared: true do
- it "returns the result of subtracting other from self as a Float" do
- (Rational(3, 4) - 0.2).should eql(0.55)
- (Rational(3, 4) - 2.5).should eql(-1.75)
- end
-end
-
-describe :rational_minus, shared: true do
- it "calls #coerce on the passed argument with self" do
- rational = Rational(3, 4)
- obj = mock("Object")
- obj.should_receive(:coerce).with(rational).and_return([1, 2])
-
- rational - obj
- end
-
- it "calls #- on the coerced Rational with the coerced Object" do
- rational = Rational(3, 4)
-
- coerced_rational = mock("Coerced Rational")
- coerced_rational.should_receive(:-).and_return(:result)
-
- coerced_obj = mock("Coerced Object")
-
- obj = mock("Object")
- obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj])
-
- (rational - obj).should == :result
- end
-end
diff --git a/spec/ruby/shared/rational/quo.rb b/spec/ruby/shared/rational/quo.rb
deleted file mode 100644
index 53b32fed2f..0000000000
--- a/spec/ruby/shared/rational/quo.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require_relative '../../spec_helper'
-
-describe :rational_quo, shared: true do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/shared/rational/to_f.rb b/spec/ruby/shared/rational/to_f.rb
index 56e0b61d68..472a585daa 100644
--- a/spec/ruby/shared/rational/to_f.rb
+++ b/spec/ruby/shared/rational/to_f.rb
@@ -7,4 +7,10 @@ describe :rational_to_f, shared: true do
Rational(-1, 4).to_f.should eql(-0.25)
Rational(-1, -4).to_f.should eql(0.25)
end
+
+ it "converts to a Float for large numerator and denominator" do
+ num = 1000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146009
+ den = 2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ Rational(num, den).to_f.should == 500.0
+ end
end
diff --git a/spec/ruby/shared/rational/truncate.rb b/spec/ruby/shared/rational/truncate.rb
index 761dd3113a..df5198ca02 100644
--- a/spec/ruby/shared/rational/truncate.rb
+++ b/spec/ruby/shared/rational/truncate.rb
@@ -17,6 +17,18 @@ describe :rational_truncate, shared: true do
end
end
+ describe "with an explicit precision = 0" do
+ it "returns an integer" do
+ @rational.truncate(0).should be_kind_of(Integer)
+ end
+
+ it "returns the truncated value toward 0" do
+ @rational.truncate(0).should == 314
+ Rational(1, 2).truncate(0).should == 0
+ Rational(-1, 2).truncate(0).should == 0
+ end
+ end
+
describe "with a precision < 0" do
it "returns an integer" do
@rational.truncate(-2).should be_kind_of(Integer)
@@ -42,4 +54,18 @@ describe :rational_truncate, shared: true do
@rational.truncate(3).should == Rational(62857, 200)
end
end
+
+ describe "with an invalid value for precision" do
+ it "raises a TypeError" do
+ -> { @rational.truncate(nil) }.should raise_error(TypeError, "not an integer")
+ -> { @rational.truncate(1.0) }.should raise_error(TypeError, "not an integer")
+ -> { @rational.truncate('') }.should raise_error(TypeError, "not an integer")
+ end
+
+ it "does not call to_int on the argument" do
+ object = Object.new
+ object.should_not_receive(:to_int)
+ -> { @rational.truncate(object) }.should raise_error(TypeError, "not an integer")
+ end
+ end
end
diff --git a/spec/ruby/shared/sizedqueue/enque.rb b/spec/ruby/shared/sizedqueue/enque.rb
index 6ef12349f8..2f25517675 100644
--- a/spec/ruby/shared/sizedqueue/enque.rb
+++ b/spec/ruby/shared/sizedqueue/enque.rb
@@ -37,7 +37,7 @@ describe :sizedqueue_enq, shared: true do
q << 1
t = Thread.new {
- -> { q.send(@method, 2) }.should raise_error(ClosedQueueError)
+ -> { q.send(@method, 2) }.should raise_error(ClosedQueueError, "queue closed")
}
Thread.pass until q.num_waiting == 1
@@ -47,4 +47,85 @@ describe :sizedqueue_enq, shared: true do
t.join
q.pop.should == 1
end
+
+ describe "with a timeout" do
+ ruby_version_is "3.2" do
+ it "returns self if the item was pushed in time" do
+ q = @object.call(1)
+ q << 1
+
+ t = Thread.new {
+ q.send(@method, 2, timeout: TIME_TOLERANCE).should == q
+ }
+ Thread.pass until t.status == "sleep" && q.num_waiting == 1
+ q.pop
+ t.join
+ end
+
+ it "does nothing if the timeout is nil" do
+ q = @object.call(1)
+ q << 1
+ t = Thread.new {
+ q.send(@method, 2, timeout: nil).should == q
+ }
+ t.join(0.2).should == nil
+ q.pop
+ t.join
+ end
+
+ it "returns nil if no space is available and timeout is 0" do
+ q = @object.call(1)
+ q.send(@method, 1, timeout: 0).should == q
+ q.send(@method, 2, timeout: 0).should == nil
+ end
+
+ it "returns nil if no space is available in time" do
+ q = @object.call(1)
+ q << 1
+ Thread.new {
+ q.send(@method, 2, timeout: 0.001).should == nil
+ }.join
+ end
+
+ it "raise TypeError if timeout is not a valid numeric" do
+ q = @object.call(1)
+ -> {
+ q.send(@method, 2, timeout: "1")
+ }.should raise_error(TypeError, "no implicit conversion to float from string")
+
+ -> {
+ q.send(@method, 2, timeout: false)
+ }.should raise_error(TypeError, "no implicit conversion to float from false")
+ end
+
+ it "raise ArgumentError if non_block = true is passed too" do
+ q = @object.call(1)
+ -> {
+ q.send(@method, 2, true, timeout: 1)
+ }.should raise_error(ArgumentError, "can't set a timeout if non_block is enabled")
+ end
+
+ it "raise ClosedQueueError when closed before enqueued" do
+ q = @object.call(1)
+ q.close
+ -> { q.send(@method, 2, timeout: 1) }.should raise_error(ClosedQueueError, "queue closed")
+ end
+
+ it "interrupts enqueuing threads with ClosedQueueError when the queue is closed" do
+ q = @object.call(1)
+ q << 1
+
+ t = Thread.new {
+ -> { q.send(@method, 1, timeout: TIME_TOLERANCE) }.should raise_error(ClosedQueueError, "queue closed")
+ }
+
+ Thread.pass until q.num_waiting == 1
+
+ q.close
+
+ t.join
+ q.pop.should == 1
+ end
+ end
+ end
end
diff --git a/spec/ruby/shared/sizedqueue/new.rb b/spec/ruby/shared/sizedqueue/new.rb
index 713785fb50..2573194efb 100644
--- a/spec/ruby/shared/sizedqueue/new.rb
+++ b/spec/ruby/shared/sizedqueue/new.rb
@@ -1,7 +1,12 @@
describe :sizedqueue_new, shared: true do
- it "raises a TypeError when the given argument is not Numeric" do
- -> { @object.call("foo") }.should raise_error(TypeError)
+ it "raises a TypeError when the given argument doesn't respond to #to_int" do
+ -> { @object.call("12") }.should raise_error(TypeError)
-> { @object.call(Object.new) }.should raise_error(TypeError)
+
+ @object.call(12.9).max.should == 12
+ object = Object.new
+ object.define_singleton_method(:to_int) { 42 }
+ @object.call(object).max.should == 42
end
it "raises an argument error when no argument is given" do
diff --git a/spec/ruby/shared/string/end_with.rb b/spec/ruby/shared/string/end_with.rb
index 5f2a011235..94a7f97513 100644
--- a/spec/ruby/shared/string/end_with.rb
+++ b/spec/ruby/shared/string/end_with.rb
@@ -38,7 +38,7 @@ describe :end_with, shared: true do
it "uses only the needed arguments" do
find = mock('h')
find.should_not_receive(:to_str)
- "hello".send(@method).should.end_with?("o",find)
+ "hello".send(@method).should.end_with?("o", find)
end
it "works for multibyte strings" do
@@ -51,4 +51,11 @@ describe :end_with, shared: true do
"あれ".send(@method).end_with?(pat)
end.should raise_error(Encoding::CompatibilityError)
end
+
+ it "checks that we are starting to match at the head of a character" do
+ "\xC3\xA9".send(@method).should_not.end_with?("\xA9")
+ "\xe3\x81\x82".send(@method).should_not.end_with?("\x82")
+ "ab".dup.force_encoding("UTF-16BE").send(@method).should_not.end_with?(
+ "b".dup.force_encoding("UTF-16BE"))
+ end
end
diff --git a/spec/ruby/shared/string/start_with.rb b/spec/ruby/shared/string/start_with.rb
index d8d6e13f6a..4b947a3bbf 100644
--- a/spec/ruby/shared/string/start_with.rb
+++ b/spec/ruby/shared/string/start_with.rb
@@ -69,4 +69,16 @@ describe :start_with, shared: true do
Regexp.last_match.should be_nil
$1.should be_nil
end
+
+ ruby_version_is ""..."3.3" do
+ it "does not check that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should.start_with?("\xC3")
+ end
+ end
+
+ ruby_version_is "3.3" do # #19784
+ it "checks that we are not matching part of a character" do
+ "\xC3\xA9".send(@method).should_not.start_with?("\xC3")
+ end
+ end
end
diff --git a/spec/ruby/shared/string/times.rb b/spec/ruby/shared/string/times.rb
index cd4edf5340..aaf748bad9 100644
--- a/spec/ruby/shared/string/times.rb
+++ b/spec/ruby/shared/string/times.rb
@@ -32,36 +32,14 @@ describe :string_times, shared: true do
@object.call("", max_long).should == ""
end
- ruby_version_is ''...'3.0' do
- it "returns subclass instances" do
- @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString)
- @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString)
- @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString)
- end
- end
-
- ruby_version_is '3.0' do
- it "returns String instances" do
- @object.call(MyString.new("cool"), 0).should be_an_instance_of(String)
- @object.call(MyString.new("cool"), 1).should be_an_instance_of(String)
- @object.call(MyString.new("cool"), 2).should be_an_instance_of(String)
- end
- end
-
- ruby_version_is ''...'2.7' do
- it "always taints the result when self is tainted" do
- ["", "OK", MyString.new(""), MyString.new("OK")].each do |str|
- str.taint
-
- [0, 1, 2].each do |arg|
- @object.call(str, arg).should.tainted?
- end
- end
- end
+ it "returns String instances" do
+ @object.call(MyString.new("cool"), 0).should be_an_instance_of(String)
+ @object.call(MyString.new("cool"), 1).should be_an_instance_of(String)
+ @object.call(MyString.new("cool"), 2).should be_an_instance_of(String)
end
it "returns a String in the same encoding as self" do
- str = "\xE3\x81\x82".force_encoding Encoding::UTF_8
+ str = "\xE3\x81\x82".dup.force_encoding Encoding::UTF_8
result = @object.call(str, 2)
result.encoding.should equal(Encoding::UTF_8)
end
diff --git a/spec/ruby/shared/types/rb_num2dbl_fails.rb b/spec/ruby/shared/types/rb_num2dbl_fails.rb
new file mode 100644
index 0000000000..ec7cc11986
--- /dev/null
+++ b/spec/ruby/shared/types/rb_num2dbl_fails.rb
@@ -0,0 +1,17 @@
+#
+# Shared tests for rb_num2dbl related conversion failures.
+#
+# Usage example:
+# it_behaves_like :rb_num2dbl_fails, nil, -> v { o = A.new; o.foo(v) }
+#
+
+describe :rb_num2dbl_fails, shared: true do
+ it "fails if string is provided" do
+ -> { @object.call("123") }.should raise_error(TypeError, "no implicit conversion to float from string")
+ end
+
+ it "fails if boolean is provided" do
+ -> { @object.call(true) }.should raise_error(TypeError, "no implicit conversion to float from true")
+ -> { @object.call(false) }.should raise_error(TypeError, "no implicit conversion to float from false")
+ end
+end