This commit is contained in:
Benoit Daloze 2024-02-26 14:45:30 +01:00
Родитель 40c9a3dad5
Коммит 815db5c028
18 изменённых файлов: 329 добавлений и 130 удалений

Просмотреть файл

@ -63,7 +63,6 @@ Lint/LiteralInInterpolation:
- 'language/string_spec.rb'
- 'language/symbol_spec.rb'
- 'language/undef_spec.rb'
- 'library/net/ftp/connect_spec.rb'
# Offense count: 8
# Cop supports --auto-correct.

Просмотреть файл

@ -73,6 +73,11 @@ describe "Encoding#replicate" do
Encoding::US_ASCII.replicate('MY-US-ASCII')
}.should complain(/warning: Encoding#replicate is deprecated and will be removed in Ruby 3.3; use the original encoding instead/)
end
it "raises EncodingError if too many encodings" do
code = '1_000.times {|i| Encoding::US_ASCII.replicate("R_#{i}") }'
ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
end
end
ruby_version_is "3.3" do

Просмотреть файл

@ -342,4 +342,10 @@ module EnumerableSpecs
@block.call(*args)
end
end
# Set is a core class since Ruby 3.2
ruby_version_is '3.2' do
class SetSubclass < Set
end
end
end # EnumerableSpecs utility classes

Просмотреть файл

@ -0,0 +1,29 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "3.2" do
describe "Enumerable#to_set" do
it "returns a new Set created from self" do
[1, 2, 3].to_set.should == Set[1, 2, 3]
{a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]]
end
it "passes down passed blocks" do
[1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9]
end
it "instantiates an object of provided as the first argument set class" do
set = [1, 2, 3].to_set(EnumerableSpecs::SetSubclass)
set.should be_kind_of(EnumerableSpecs::SetSubclass)
set.to_a.sort.should == [1, 2, 3]
end
it "does not need explicit `require 'set'`" do
output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1')
puts [1, 2, 3].to_set
RUBY
output.chomp.should == "#<Set: {1, 2, 3}>"
end
end
end

Просмотреть файл

@ -1,7 +1,12 @@
require_relative '../../spec_helper'
require_relative 'shared/update_time'
describe "File.lutime" do
platform_is_not :windows do
platform_is_not :windows do
describe "File.lutime" do
it_behaves_like :update_time, :lutime
end
describe "File.lutime" do
before :each do
@atime = Time.utc(2000)
@mtime = Time.utc(2001)

Просмотреть файл

@ -0,0 +1,105 @@
describe :update_time, shared: true do
before :all do
@time_is_float = platform_is :windows
end
before :each do
@atime = Time.now
@mtime = Time.now
@file1 = tmp("specs_file_utime1")
@file2 = tmp("specs_file_utime2")
touch @file1
touch @file2
end
after :each do
rm_r @file1, @file2
end
it "sets the access and modification time of each file" do
File.send(@method, @atime, @mtime, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end
it "uses the current times if two nil values are passed" do
tn = Time.now
File.send(@method, nil, nil, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(tn, 0.050)
File.mtime(@file1).should be_close(tn, 0.050)
File.atime(@file2).should be_close(tn, 0.050)
File.mtime(@file2).should be_close(tn, 0.050)
else
File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
end
end
it "accepts an object that has a #to_path method" do
File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
end
it "accepts numeric atime and mtime arguments" do
if @time_is_float
File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2)
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2)
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end
it "may set nanosecond precision" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
File.send(@method, t, t, @file1)
File.atime(@file1).nsec.should.between?(0, 123500000)
File.mtime(@file1).nsec.should.between?(0, 123500000)
end
it "returns the number of filenames in the arguments" do
File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2
end
platform_is :linux do
platform_is wordsize: 64 do
it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
# Amazon Linux 2023 returns 2486-07-02 in this example
# http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.send(@method, time, time, @file1)
[559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
[559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
end

Просмотреть файл

@ -1,102 +1,6 @@
require_relative '../../spec_helper'
require_relative 'shared/update_time'
describe "File.utime" do
before :all do
@time_is_float = platform_is :windows
end
before :each do
@atime = Time.now
@mtime = Time.now
@file1 = tmp("specs_file_utime1")
@file2 = tmp("specs_file_utime2")
touch @file1
touch @file2
end
after :each do
rm_r @file1, @file2
end
it "sets the access and modification time of each file" do
File.utime(@atime, @mtime, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end
it "uses the current times if two nil values are passed" do
tn = Time.now
File.utime(nil, nil, @file1, @file2)
if @time_is_float
File.atime(@file1).should be_close(tn, 0.050)
File.mtime(@file1).should be_close(tn, 0.050)
File.atime(@file2).should be_close(tn, 0.050)
File.mtime(@file2).should be_close(tn, 0.050)
else
File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
end
end
it "accepts an object that has a #to_path method" do
File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
end
it "accepts numeric atime and mtime arguments" do
if @time_is_float
File.utime(@atime.to_f, @mtime.to_f, @file1, @file2)
File.atime(@file1).should be_close(@atime, 0.0001)
File.mtime(@file1).should be_close(@mtime, 0.0001)
File.atime(@file2).should be_close(@atime, 0.0001)
File.mtime(@file2).should be_close(@mtime, 0.0001)
else
File.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
end
end
it "may set nanosecond precision" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
File.utime(t, t, @file1)
File.atime(@file1).nsec.should.between?(0, 123500000)
File.mtime(@file1).nsec.should.between?(0, 123500000)
end
it "returns the number of filenames in the arguments" do
File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2
end
platform_is :linux do
platform_is wordsize: 64 do
it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
# https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
# "Therefore, timestamps should not overflow until May 2446."
# https://lwn.net/Articles/804382/
# "On-disk timestamps hitting the y2038 limit..."
# The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
# https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
# Amazon Linux 2023 returns 2486-07-02 in this example
# http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
time = Time.at(1<<44)
File.utime(time, time, @file1)
[559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
[559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
end
end
end
it_behaves_like :update_time, :utime
end

Просмотреть файл

@ -4,3 +4,11 @@ require_relative '../../shared/file/exist'
describe "FileTest.exist?" do
it_behaves_like :file_exist, :exist?, FileTest
end
ruby_version_is "3.2" do
describe "FileTest.exists?" do
it "has been removed" do
FileTest.should_not.respond_to?(:exists?)
end
end
end

Просмотреть файл

@ -14,6 +14,20 @@ describe "Kernel#!~" do
(obj !~ :foo).should == false
end
ruby_version_is ""..."3.2" do
it "returns true if self does not respond to #=~" do
suppress_warning do
(Object.new !~ :foo).should == true
end
end
end
ruby_version_is "3.2" do
it "raises NoMethodError if self does not respond to #=~" do
-> { Object.new !~ :foo }.should raise_error(NoMethodError)
end
end
it 'can be overridden in subclasses' do
obj = KernelSpecs::NotMatch.new
(obj !~ :bar).should == :foo

Просмотреть файл

@ -22,6 +22,8 @@ describe "Method#parameters" do
local_is_not_parameter = {}
end
def forward_parameters(...) end
def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end
define_method(:one_optional_defined_method) {|x = 1|}
@ -267,6 +269,20 @@ describe "Method#parameters" do
end
end
ruby_version_is ""..."3.1" do
it "returns [:rest, :*], [:block, :&] for forward parameters operator" do
m = MethodSpecs::Methods.new
m.method(:forward_parameters).parameters.should == [[:rest, :*], [:block, :&]]
end
end
ruby_version_is "3.1" do
it "returns [:rest, :*], [:keyrest, :**], [:block, :&] for forward parameters operator" do
m = MethodSpecs::Methods.new
m.method(:forward_parameters).parameters.should == [[:rest, :*], [:keyrest, :**], [:block, :&]]
end
end
it "returns the args and block for a splat and block argument" do
m = MethodSpecs::Methods.new
m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]]

Просмотреть файл

@ -33,6 +33,16 @@ describe "Proc#parameters" do
it "regards named parameters in lambda as optional if lambda: false keyword used" do
-> x { }.parameters(lambda: false).first.first.should == :opt
end
it "regards named parameters in procs and lambdas as required if lambda keyword is truthy" do
proc {|x| }.parameters(lambda: 123).first.first.should == :req
-> x { }.parameters(lambda: 123).first.first.should == :req
end
it "ignores the lambda keyword if it is nil" do
proc {|x|}.parameters(lambda: nil).first.first.should == :opt
-> x { }.parameters(lambda: nil).first.first.should == :req
end
end
it "regards optional keyword parameters in procs as optional" do
@ -160,4 +170,8 @@ describe "Proc#parameters" do
[:block, :_]
]
end
it "returns :nokey for **nil parameter" do
proc { |**nil| }.parameters.should == [[:nokey]]
end
end

Просмотреть файл

@ -10,6 +10,18 @@ describe "String#to_i" do
"1_2_3asdf".to_i.should == 123
end
it "ignores multiple non-consecutive underscoes when the first digit is 0" do
(2..16).each do |base|
"0_0_010".to_i(base).should == base;
end
end
it "bails out at the first double underscore if the first digit is 0" do
(2..16).each do |base|
"010__1".to_i(base).should == base;
end
end
it "ignores leading whitespaces" do
[ " 123", " 123", "\r\n\r\n123", "\t\t123",
"\r\n\t\n123", " \t\n\r\t 123"].each do |str|

Просмотреть файл

@ -29,7 +29,7 @@ describe "Net::FTP#connect" do
ruby_version_is ""..."3.1" do
it "prints a small debug line when in debug mode" do
@ftp.debug_mode = true
-> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/#{"connect: "}#{@server.hostname}#{", "}#{@server.server_port}#{"\\nget: 220 Dummy FTP Server ready!"}/)
-> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/connect: #{@server.hostname}, #{@server.server_port}\nget: 220 Dummy FTP Server ready!/)
@ftp.debug_mode = false
end
end

Просмотреть файл

@ -0,0 +1,12 @@
require_relative '../../spec_helper'
describe 'Set' do
ruby_version_is '3.2' do
it 'is available without explicit requiring' do
output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1')
puts Set.new([1, 2, 3])
RUBY
output.chomp.should == "#<Set: {1, 2, 3}>"
end
end
end

Просмотреть файл

@ -2,7 +2,7 @@
require_relative 'spec_helper'
require_relative 'fixtures/encoding'
load_extension('encoding')
extension_path = load_extension('encoding')
describe :rb_enc_get_index, shared: true do
it "returns the index of the encoding of a String" do
@ -559,19 +559,19 @@ describe "C-API Encoding function" do
describe "rb_ascii8bit_encindex" do
it "returns an index for the ASCII-8BIT encoding" do
@s.rb_ascii8bit_encindex().should >= 0
@s.rb_ascii8bit_encindex().should == 0
end
end
describe "rb_utf8_encindex" do
it "returns an index for the UTF-8 encoding" do
@s.rb_utf8_encindex().should >= 0
@s.rb_utf8_encindex().should == 1
end
end
describe "rb_usascii_encindex" do
it "returns an index for the US-ASCII encoding" do
@s.rb_usascii_encindex().should >= 0
@s.rb_usascii_encindex().should == 2
end
end
@ -721,4 +721,27 @@ describe "C-API Encoding function" do
str.bytes.should == [0, 0x24]
end
end
describe "rb_define_dummy_encoding" do
it "defines the dummy encoding" do
@s.rb_define_dummy_encoding("FOO")
enc = Encoding.find("FOO")
enc.should.dummy?
end
it "returns the index of the dummy encoding" do
index = @s.rb_define_dummy_encoding("BAR")
index.should == Encoding.list.size - 1
end
ruby_version_is "3.2" do
it "raises EncodingError if too many encodings" do
code = <<-RUBY
require #{extension_path.dump}
1_000.times {|i| CApiEncodingSpecs.new.rb_define_dummy_encoding("R_\#{i}") }
RUBY
ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
end
end
end
end

Просмотреть файл

@ -320,6 +320,10 @@ static VALUE encoding_spec_rb_enc_left_char_head(VALUE self, VALUE str, VALUE of
return LONG2NUM(result - ptr);
}
static VALUE encoding_spec_rb_define_dummy_encoding(VALUE self, VALUE name) {
return INT2NUM(rb_define_dummy_encoding(RSTRING_PTR(name)));
}
void Init_encoding_spec(void) {
VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@ -379,6 +383,7 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2);
rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1);
rb_define_method(cls, "rb_enc_left_char_head", encoding_spec_rb_enc_left_char_head, 2);
rb_define_method(cls, "rb_define_dummy_encoding", encoding_spec_rb_define_dummy_encoding, 1);
}
#ifdef __cplusplus

Просмотреть файл

@ -51,10 +51,6 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) {
return INT2FIX(RSTRING_LEN(str));
}
VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) {
return rb_str_to_interned_str(str);
}
VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) {
VALUE buf;
@ -576,11 +572,19 @@ static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) {
return rb_str_unlocktmp(str);
}
static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) {
rb_encoding *e = rb_to_encoding(enc);
return rb_enc_interned_str_cstr(RSTRING_PTR(str), e);
}
static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) {
return rb_str_to_interned_str(str);
}
void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3);
rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1);
rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2);
rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2);
rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2);
@ -677,6 +681,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1);
rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1);
rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1);
rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2);
rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1);
}
#ifdef __cplusplus

Просмотреть файл

@ -498,25 +498,6 @@ describe "C-API String function" do
end
end
describe "rb_fstring" do
it 'returns self if the String is frozen' do
input = 'foo'.freeze
output = @s.rb_fstring(input)
output.should equal(input)
output.should.frozen?
end
it 'returns a frozen copy if the String is not frozen' do
input = 'foo'
output = @s.rb_fstring(input)
output.should.frozen?
output.should_not equal(input)
output.should == 'foo'
end
end
describe "rb_str_subseq" do
it "returns a byte-indexed substring" do
str = "\x00\x01\x02\x03\x04".force_encoding("binary")
@ -1227,4 +1208,59 @@ end
-> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
end
end
describe "rb_enc_interned_str_cstr" do
it "returns a frozen string" do
str = "hello"
val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
val.should.is_a?(String)
val.encoding.should == Encoding::US_ASCII
val.should.frozen?
end
it "returns the same frozen string" do
str = "hello"
result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
result1.should.equal?(result2)
end
it "returns different frozen strings for different encodings" do
str = "hello"
result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8)
result1.should_not.equal?(result2)
end
it "returns the same string as String#-@" do
@s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello")
end
end
describe "rb_str_to_interned_str" do
it "returns a frozen string" do
str = "hello"
result = @s.rb_str_to_interned_str(str)
result.should.is_a?(String)
result.should.frozen?
end
it "returns the same frozen string" do
str = "hello"
result1 = @s.rb_str_to_interned_str(str)
result2 = @s.rb_str_to_interned_str(str)
result1.should.equal?(result2)
end
it "returns different frozen strings for different encodings" do
result1 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::US_ASCII))
result2 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::UTF_8))
result1.should_not.equal?(result2)
end
it "returns the same string as String#-@" do
@s.rb_str_to_interned_str("hello").should.equal?(-"hello")
end
end
end