summaryrefslogtreecommitdiff
path: root/spec/ruby/core/struct
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/struct')
-rw-r--r--spec/ruby/core/struct/constants_spec.rb15
-rw-r--r--spec/ruby/core/struct/deconstruct_keys_spec.rb146
-rw-r--r--spec/ruby/core/struct/deconstruct_spec.rb12
-rw-r--r--spec/ruby/core/struct/fixtures/classes.rb6
-rw-r--r--spec/ruby/core/struct/initialize_spec.rb18
-rw-r--r--spec/ruby/core/struct/inspect_spec.rb5
-rw-r--r--spec/ruby/core/struct/keyword_init_spec.rb40
-rw-r--r--spec/ruby/core/struct/new_spec.rb35
-rw-r--r--spec/ruby/core/struct/shared/inspect.rb35
-rw-r--r--spec/ruby/core/struct/values_at_spec.rb55
10 files changed, 273 insertions, 94 deletions
diff --git a/spec/ruby/core/struct/constants_spec.rb b/spec/ruby/core/struct/constants_spec.rb
new file mode 100644
index 0000000000..fa61a4b912
--- /dev/null
+++ b/spec/ruby/core/struct/constants_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Struct::Group" do
+ it "is no longer defined" do
+ Struct.should_not.const_defined?(:Group)
+ end
+ end
+
+ describe "Struct::Passwd" do
+ it "is no longer defined" do
+ Struct.should_not.const_defined?(:Passwd)
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/deconstruct_keys_spec.rb b/spec/ruby/core/struct/deconstruct_keys_spec.rb
index f0a1f50ad3..b4c84c49df 100644
--- a/spec/ruby/core/struct/deconstruct_keys_spec.rb
+++ b/spec/ruby/core/struct/deconstruct_keys_spec.rb
@@ -1,78 +1,76 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Struct#deconstruct_keys" do
- it "returns a hash of attributes" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
-
- s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
- end
-
- it "requires one argument" do
- struct = Struct.new(:x)
- obj = struct.new(1)
-
- -> {
- obj.deconstruct_keys
- }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
- end
-
- it "returns only specified keys" do
- struct = Struct.new(:x, :y, :z)
- s = struct.new(1, 2, 3)
-
- s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
- s.deconstruct_keys([:x] ).should == {x: 1}
- s.deconstruct_keys([] ).should == {}
- end
-
- it "accepts string attribute names" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
-
- s.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
- end
-
- it "accepts argument position number as well but returns them as keys" do
- struct = Struct.new(:x, :y, :z)
- s = struct.new(10, 20, 30)
-
- s.deconstruct_keys([0, 1, 2]).should == {0 => 10, 1 => 20, 2 => 30}
- s.deconstruct_keys([0, 1] ).should == {0 => 10, 1 => 20}
- s.deconstruct_keys([0] ).should == {0 => 10}
- end
-
- it "returns an empty hash when there are more keys than attributes" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
-
- s.deconstruct_keys([:x, :y, :a]).should == {}
- end
-
- it "returns at first not existing attribute name" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
-
- s.deconstruct_keys([:a, :x]).should == {}
- s.deconstruct_keys([:x, :a]).should == {x: 1}
- end
-
- it "accepts nil argument and return all the attributes" do
- struct = Struct.new(:x, :y)
- obj = struct.new(1, 2)
-
- obj.deconstruct_keys(nil).should == {x: 1, y: 2}
- end
-
- it "raise TypeError if passed anything accept nil or array" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
-
- -> { s.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/)
- -> { s.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/)
- -> { s.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/)
- -> { s.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/)
- end
+describe "Struct#deconstruct_keys" do
+ it "returns a hash of attributes" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ end
+
+ it "requires one argument" do
+ struct = Struct.new(:x)
+ obj = struct.new(1)
+
+ -> {
+ obj.deconstruct_keys
+ }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
+
+ it "returns only specified keys" do
+ struct = Struct.new(:x, :y, :z)
+ s = struct.new(1, 2, 3)
+
+ s.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ s.deconstruct_keys([:x] ).should == {x: 1}
+ s.deconstruct_keys([] ).should == {}
+ end
+
+ it "accepts string attribute names" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
+ end
+
+ it "accepts argument position number as well but returns them as keys" do
+ struct = Struct.new(:x, :y, :z)
+ s = struct.new(10, 20, 30)
+
+ s.deconstruct_keys([0, 1, 2]).should == {0 => 10, 1 => 20, 2 => 30}
+ s.deconstruct_keys([0, 1] ).should == {0 => 10, 1 => 20}
+ s.deconstruct_keys([0] ).should == {0 => 10}
+ end
+
+ it "returns an empty hash when there are more keys than attributes" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys([:x, :y, :a]).should == {}
+ end
+
+ it "returns at first not existing attribute name" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ s.deconstruct_keys([:a, :x]).should == {}
+ s.deconstruct_keys([:x, :a]).should == {x: 1}
+ end
+
+ it "accepts nil argument and return all the attributes" do
+ struct = Struct.new(:x, :y)
+ obj = struct.new(1, 2)
+
+ obj.deconstruct_keys(nil).should == {x: 1, y: 2}
+ end
+
+ it "raise TypeError if passed anything except nil or array" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
+
+ -> { s.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/)
+ -> { s.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/)
end
end
diff --git a/spec/ruby/core/struct/deconstruct_spec.rb b/spec/ruby/core/struct/deconstruct_spec.rb
index 7518a40987..32d4f6bac4 100644
--- a/spec/ruby/core/struct/deconstruct_spec.rb
+++ b/spec/ruby/core/struct/deconstruct_spec.rb
@@ -1,12 +1,10 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Struct#deconstruct" do
- it "returns an array of attribute values" do
- struct = Struct.new(:x, :y)
- s = struct.new(1, 2)
+describe "Struct#deconstruct" do
+ it "returns an array of attribute values" do
+ struct = Struct.new(:x, :y)
+ s = struct.new(1, 2)
- s.deconstruct.should == [1, 2]
- end
+ s.deconstruct.should == [1, 2]
end
end
diff --git a/spec/ruby/core/struct/fixtures/classes.rb b/spec/ruby/core/struct/fixtures/classes.rb
index 6d620f9060..bf838d05df 100644
--- a/spec/ruby/core/struct/fixtures/classes.rb
+++ b/spec/ruby/core/struct/fixtures/classes.rb
@@ -13,6 +13,12 @@ module StructClasses
end
end
+ class StructWithOverriddenName < Struct.new(:a)
+ def self.name
+ "A"
+ end
+ end
+
class SubclassX < Struct
end
diff --git a/spec/ruby/core/struct/initialize_spec.rb b/spec/ruby/core/struct/initialize_spec.rb
index e82289071a..a5ebe9551c 100644
--- a/spec/ruby/core/struct/initialize_spec.rb
+++ b/spec/ruby/core/struct/initialize_spec.rb
@@ -40,4 +40,22 @@ describe "Struct#initialize" do
it "can be overridden" do
StructClasses::SubclassX.new(:y).new.key.should == :value
end
+
+ ruby_version_is "3.1"..."3.2" do
+ it "warns about passing only keyword arguments" do
+ -> {
+ StructClasses::Ruby.new(version: "3.1", platform: "OS")
+ }.should complain(/warning: Passing only keyword arguments/)
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "can be initialized with keyword arguments" do
+ positional_args = StructClasses::Ruby.new("3.2", "OS")
+ keyword_args = StructClasses::Ruby.new(version: "3.2", platform: "OS")
+
+ positional_args.version.should == keyword_args.version
+ positional_args.platform.should == keyword_args.platform
+ end
+ end
end
diff --git a/spec/ruby/core/struct/inspect_spec.rb b/spec/ruby/core/struct/inspect_spec.rb
index 83e13597ba..657b06abc1 100644
--- a/spec/ruby/core/struct/inspect_spec.rb
+++ b/spec/ruby/core/struct/inspect_spec.rb
@@ -3,10 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/inspect'
describe "Struct#inspect" do
- it "returns a string representation showing members and values" do
- car = StructClasses::Car.new('Ford', 'Ranger')
- car.inspect.should == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>'
- end
-
it_behaves_like :struct_inspect, :inspect
end
diff --git a/spec/ruby/core/struct/keyword_init_spec.rb b/spec/ruby/core/struct/keyword_init_spec.rb
new file mode 100644
index 0000000000..8de4c14351
--- /dev/null
+++ b/spec/ruby/core/struct/keyword_init_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.1" do
+ # See https://bugs.ruby-lang.org/issues/18008
+ describe "StructClass#keyword_init?" do
+ it "returns true for a struct that accepts keyword arguments to initialize" do
+ struct = Struct.new(:arg, keyword_init: true)
+ struct.keyword_init?.should be_true
+ end
+
+ it "returns false for a struct that does not accept keyword arguments to initialize" do
+ struct = Struct.new(:arg, keyword_init: false)
+ struct.keyword_init?.should be_false
+ end
+
+ it "returns nil for a struct that did not explicitly specify keyword_init" do
+ struct = Struct.new(:arg)
+ struct.keyword_init?.should be_nil
+ end
+
+ it "returns nil for a struct that does specify keyword_init to be nil" do
+ struct = Struct.new(:arg, keyword_init: nil)
+ struct.keyword_init?.should be_nil
+ end
+
+ it "returns true for any truthy value, not just for true" do
+ struct = Struct.new(:arg, keyword_init: 1)
+ struct.keyword_init?.should be_true
+
+ struct = Struct.new(:arg, keyword_init: "")
+ struct.keyword_init?.should be_true
+
+ struct = Struct.new(:arg, keyword_init: [])
+ struct.keyword_init?.should be_true
+
+ struct = Struct.new(:arg, keyword_init: {})
+ struct.keyword_init?.should be_true
+ end
+ end
+end
diff --git a/spec/ruby/core/struct/new_spec.rb b/spec/ruby/core/struct/new_spec.rb
index cbbec829d7..a94eb852e1 100644
--- a/spec/ruby/core/struct/new_spec.rb
+++ b/spec/ruby/core/struct/new_spec.rb
@@ -47,6 +47,11 @@ describe "Struct.new" do
Struct.const_defined?("Animal2").should be_false
end
+ it "allows non-ASCII member name" do
+ name = "r\xe9sum\xe9".dup.force_encoding(Encoding::ISO_8859_1).to_sym
+ struct = Struct.new(name)
+ struct.new("foo").send(name).should == "foo"
+ end
it "fails with invalid constant name as first argument" do
-> { Struct.new('animal', :name, :legs, :eyeballs) }.should raise_error(NameError)
@@ -62,8 +67,34 @@ describe "Struct.new" do
-> { Struct.new(:animal, ['chris', 'evan']) }.should raise_error(TypeError)
end
- it "raises a ArgumentError if passed a Hash with an unknown key" do
- -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(ArgumentError)
+ ruby_version_is ""..."3.2" do
+ it "raises a TypeError or ArgumentError if passed a Hash with an unknown key" do
+ # CRuby < 3.2 raises ArgumentError: unknown keyword: :name, but that seems a bug:
+ # https://bugs.ruby-lang.org/issues/18632
+ -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(StandardError) { |e|
+ [ArgumentError, TypeError].should.include?(e.class)
+ }
+ end
+ end
+
+ ruby_version_is "3.2" do
+ it "raises a TypeError if passed a Hash with an unknown key" do
+ -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "raises ArgumentError if not provided any arguments" do
+ -> { Struct.new }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "works when not provided any arguments" do
+ c = Struct.new
+ c.should be_kind_of(Class)
+ c.superclass.should == Struct
+ end
end
it "raises ArgumentError when there is a duplicate member" do
diff --git a/spec/ruby/core/struct/shared/inspect.rb b/spec/ruby/core/struct/shared/inspect.rb
index 90594a5452..1a0fb6a6b2 100644
--- a/spec/ruby/core/struct/shared/inspect.rb
+++ b/spec/ruby/core/struct/shared/inspect.rb
@@ -1,5 +1,40 @@
describe :struct_inspect, shared: true do
+ it "returns a string representation showing members and values" do
+ car = StructClasses::Car.new('Ford', 'Ranger')
+ car.send(@method).should == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>'
+ end
+
it "returns a string representation without the class name for anonymous structs" do
Struct.new(:a).new("").send(@method).should == '#<struct a="">'
end
+
+ it "returns a string representation without the class name for structs nested in anonymous classes" do
+ c = Class.new
+ c.class_eval <<~DOC
+ class Foo < Struct.new(:a); end
+ DOC
+
+ c::Foo.new("").send(@method).should == '#<struct a="">'
+ end
+
+ it "returns a string representation without the class name for structs nested in anonymous modules" do
+ m = Module.new
+ m.module_eval <<~DOC
+ class Foo < Struct.new(:a); end
+ DOC
+
+ m::Foo.new("").send(@method).should == '#<struct a="">'
+ end
+
+ it "does not call #name method" do
+ struct = StructClasses::StructWithOverriddenName.new("")
+ struct.send(@method).should == '#<struct StructClasses::StructWithOverriddenName a="">'
+ end
+
+ it "does not call #name method when struct is anonymous" do
+ struct = Struct.new(:a)
+ def struct.name; "A"; end
+
+ struct.new("").send(@method).should == '#<struct a="">'
+ end
end
diff --git a/spec/ruby/core/struct/values_at_spec.rb b/spec/ruby/core/struct/values_at_spec.rb
index e7d287cba2..5e5a496600 100644
--- a/spec/ruby/core/struct/values_at_spec.rb
+++ b/spec/ruby/core/struct/values_at_spec.rb
@@ -1,16 +1,59 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Should be synchronized with core/array/values_at_spec.rb
describe "Struct#values_at" do
- it "returns an array of values" do
+ before do
clazz = Struct.new(:name, :director, :year)
- movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002)
- movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park']
- movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002]
+ @movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002)
+ end
+
+ context "when passed a list of Integers" do
+ it "returns an array containing each value given by one of integers" do
+ @movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park']
+ end
+
+ it "raises IndexError if any of integers is out of range" do
+ -> { @movie.values_at(3) }.should raise_error(IndexError, "offset 3 too large for struct(size:3)")
+ -> { @movie.values_at(-4) }.should raise_error(IndexError, "offset -4 too small for struct(size:3)")
+ end
+ end
+
+ context "when passed an integer Range" do
+ it "returns an array containing each value given by the elements of the range" do
+ @movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002]
+ end
+
+ it "fills with nil values for range elements larger than the structure" do
+ @movie.values_at(0..3).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, nil]
+ end
+
+ it "raises RangeError if any element of the range is negative and out of range" do
+ -> { @movie.values_at(-4..3) }.should raise_error(RangeError, "-4..3 out of range")
+ end
+
+ it "supports endless Range" do
+ @movie.values_at(0..).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002]
+ end
+
+ it "supports beginningless Range" do
+ @movie.values_at(..2).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002]
+ end
+ end
+
+ it "supports multiple integer Ranges" do
+ @movie.values_at(0..2, 1..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 'Chan-wook Park', 2002]
+ end
+
+ it "supports mixing integer Ranges and Integers" do
+ @movie.values_at(0..2, 2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 2002]
+ end
+
+ it "returns a new empty Array if no arguments given" do
+ @movie.values_at().should == []
end
it "fails when passed unsupported types" do
- car = StructClasses::Car.new('Ford', 'Ranger')
- -> { car.values_at('make') }.should raise_error(TypeError)
+ -> { @movie.values_at('make') }.should raise_error(TypeError, "no implicit conversion of String into Integer")
end
end