diff options
Diffstat (limited to 'spec/ruby/core/data')
| -rw-r--r-- | spec/ruby/core/data/deconstruct_keys_spec.rb | 96 | ||||
| -rw-r--r-- | spec/ruby/core/data/fixtures/classes.rb | 13 | ||||
| -rw-r--r-- | spec/ruby/core/data/hash_spec.rb | 2 | ||||
| -rw-r--r-- | spec/ruby/core/data/initialize_spec.rb | 98 | ||||
| -rw-r--r-- | spec/ruby/core/data/inspect_spec.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/data/shared/inspect.rb | 62 | ||||
| -rw-r--r-- | spec/ruby/core/data/to_h_spec.rb | 8 | ||||
| -rw-r--r-- | spec/ruby/core/data/to_s_spec.rb | 61 | ||||
| -rw-r--r-- | spec/ruby/core/data/with_spec.rb | 14 |
9 files changed, 219 insertions, 141 deletions
diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb index df378f8b98..7e81f966ea 100644 --- a/spec/ruby/core/data/deconstruct_keys_spec.rb +++ b/spec/ruby/core/data/deconstruct_keys_spec.rb @@ -15,7 +15,7 @@ describe "Data#deconstruct_keys" do -> { d.deconstruct_keys - }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/) + }.should.raise(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/) end it "returns only specified keys" do @@ -34,29 +34,6 @@ describe "Data#deconstruct_keys" do d.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2} end - it "accepts argument position number as well but returns them as keys" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) - - d.deconstruct_keys([0, 1]).should == {0 => 1, 1 => 2} - d.deconstruct_keys([0] ).should == {0 => 1} - d.deconstruct_keys([-1] ).should == {-1 => 2} - end - - it "ignores incorrect position numbers" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) - - d.deconstruct_keys([0, 3]).should == {0 => 1} - end - - it "support mixing attribute names and argument position numbers" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) - - d.deconstruct_keys([0, :x]).should == {0 => 1, :x => 1} - end - it "returns an empty hash when there are more keys than attributes" do klass = Data.define(:x, :y) d = klass.new(1, 2) @@ -72,14 +49,6 @@ describe "Data#deconstruct_keys" do d.deconstruct_keys([:x, :a]).should == {x: 1} end - it "returns at first not existing argument position number" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) - - d.deconstruct_keys([3, 0]).should == {} - d.deconstruct_keys([0, 3]).should == {0 => 1} - end - it "accepts nil argument and return all the attributes" do klass = Data.define(:x, :y) d = klass.new(1, 2) @@ -87,44 +56,55 @@ describe "Data#deconstruct_keys" do d.deconstruct_keys(nil).should == {x: 1, y: 2} end - it "tries to convert a key with #to_int if index is not a String nor a Symbol, but responds to #to_int" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) + 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) - key = mock("to_int") - key.should_receive(:to_int).and_return(1) + key = mock("to_str") + key.should_receive(:to_str).and_return("y") - d.deconstruct_keys([key]).should == { key => 2 } - end + d.deconstruct_keys([key]).should == { "y" => 2 } + end - it "raises a TypeError if the conversion with #to_int does not return an Integer" do - klass = Data.define(:x, :y) - d = klass.new(1, 2) + it "raise an error on argument position number" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) - key = mock("to_int") - key.should_receive(:to_int).and_return("not an Integer") + -> { + d.deconstruct_keys([0, 1]) + }.should.raise(TypeError, "0 is not a symbol nor a string") + end - -> { - d.deconstruct_keys([key]) - }.should raise_error(TypeError, /can't convert MockObject to Integer/) - end + it "raises a TypeError if the conversion with #to_str does not return a String" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) - it "raises TypeError if index is not a String, a Symbol and not convertible to Integer " do - klass = Data.define(:x, :y) - d = klass.new(1, 2) + key = mock("to_str") + key.should_receive(:to_str).and_return(0) - -> { - d.deconstruct_keys([0, []]) - }.should raise_error(TypeError, "no implicit conversion of Array into Integer") + -> { + d.deconstruct_keys([key]) + }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) + end + + it "raises TypeError if index is not a Symbol and not convertible to String" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + -> { + d.deconstruct_keys([0, []]) + }.should.raise(TypeError, "0 is not a symbol nor a string") + end end it "raise TypeError if passed anything except nil or array" do klass = Data.define(:x, :y) d = klass.new(1, 2) - -> { d.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/) - -> { d.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/) - -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/) - -> { d.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/) + -> { d.deconstruct_keys('x') }.should.raise(TypeError, /expected Array or nil/) + -> { d.deconstruct_keys(1) }.should.raise(TypeError, /expected Array or nil/) + -> { d.deconstruct_keys(:x) }.should.raise(TypeError, /expected Array or nil/) + -> { d.deconstruct_keys({}) }.should.raise(TypeError, /expected Array or nil/) end end diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb index ffd361d781..147293ee45 100644 --- a/spec/ruby/core/data/fixtures/classes.rb +++ b/spec/ruby/core/data/fixtures/classes.rb @@ -1,6 +1,7 @@ module DataSpecs if Data.respond_to?(:define) Measure = Data.define(:amount, :unit) + Single = Data.define(:value) class MeasureWithOverriddenName < Measure def self.name @@ -8,6 +9,12 @@ module DataSpecs end end + class SingleWithOverriddenName < Single + def self.name + "A" + end + end + class DataSubclass < Data; end MeasureSubclass = Class.new(Measure) do @@ -24,5 +31,11 @@ module DataSpecs ScratchPad.record [:initialize, rest, kw] end end + + Area = Data.define(:width, :height, :area) do + def initialize(width:, height:) + super(width: width, height: height, area: width * height) + end + end end end diff --git a/spec/ruby/core/data/hash_spec.rb b/spec/ruby/core/data/hash_spec.rb index c23f08a71d..bab146c92e 100644 --- a/spec/ruby/core/data/hash_spec.rb +++ b/spec/ruby/core/data/hash_spec.rb @@ -6,7 +6,7 @@ describe "Data#hash" do a = DataSpecs::Measure.new(42, "km") b = DataSpecs::Measure.new(42, "km") a.hash.should == b.hash - a.hash.should be_an_instance_of(Integer) + a.hash.should.instance_of?(Integer) end it "returns different hashes for objects with different values" do diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb index 9d272780a8..0320ca880c 100644 --- a/spec/ruby/core/data/initialize_spec.rb +++ b/spec/ruby/core/data/initialize_spec.rb @@ -2,6 +2,16 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Data#initialize" do + context "with no members" do + ruby_bug "#21819", ""..."4.0.1" do + it "is frozen" do + data = Data.define + + data.new.should.frozen? + end + end + end + it "accepts positional arguments" do data = DataSpecs::Measure.new(42, "km") @@ -37,10 +47,27 @@ describe "Data#initialize" do 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, **{}) + + data.value.should == 42 + + data = DataSpecs::Measure.new(42, "km", **{}) + + data.amount.should == 42 + data.unit.should == "km" + end + it "raises ArgumentError if no arguments are given" do -> { DataSpecs::Measure.new - }.should raise_error(ArgumentError) { |e| + }.should.raise(ArgumentError) { |e| e.message.should.include?("missing keywords: :amount, :unit") } end @@ -48,19 +75,61 @@ describe "Data#initialize" do it "raises ArgumentError if at least one argument is missing" do -> { DataSpecs::Measure.new(unit: "km") - }.should raise_error(ArgumentError) { |e| + }.should.raise(ArgumentError) { |e| e.message.should.include?("missing keyword: :amount") } 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") - }.should raise_error(ArgumentError) { |e| + }.should.raise(ArgumentError) { |e| e.message.should.include?("unknown keyword: :system") } 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") @@ -69,7 +138,7 @@ describe "Data#initialize" do end it "supports Data with no fields" do - -> { DataSpecs::Empty.new }.should_not raise_error + -> { DataSpecs::Empty.new }.should_not.raise end it "can be overridden" do @@ -110,5 +179,26 @@ describe "Data#initialize" do DataSpecs::DataWithOverriddenInitialize[amount: 42, unit: "m"] ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}] end + + it "accepts positional arguments with empty keyword arguments" do + data = DataSpecs::SingleWithOverriddenName.new(42, **{}) + + data.value.should == 42 + + data = DataSpecs::MeasureWithOverriddenName.new(42, "km", **{}) + + data.amount.should == 42 + data.unit.should == "km" + end + + # See https://github.com/ruby/psych/pull/765 + it "can be deserialized by calling Data.instance_method(:initialize)" do + d1 = DataSpecs::Area.new(width: 2, height: 3) + d1.area.should == 6 + + d2 = DataSpecs::Area.allocate + Data.instance_method(:initialize).bind_call(d2, **d1.to_h) + d2.should == d1 + end end end diff --git a/spec/ruby/core/data/inspect_spec.rb b/spec/ruby/core/data/inspect_spec.rb index 38642910a0..e5b9689ca5 100644 --- a/spec/ruby/core/data/inspect_spec.rb +++ b/spec/ruby/core/data/inspect_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/inspect' +require_relative 'fixtures/classes' describe "Data#inspect" do - it_behaves_like :data_inspect, :inspect + it "is an alias of Data#to_s" do + DataSpecs::Measure.instance_method(:inspect).should == DataSpecs::Measure.instance_method(:to_s) + 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 6cd5664da7..0000000000 --- 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 == '#<data DataSpecs::Measure amount=42, unit="km">' - end - - it "returns a string representation without the class name for anonymous structs" do - Data.define(:a).new("").send(@method).should == '#<data 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 - Foo = Data.define(:a) - DOC - - c::Foo.new("").send(@method).should == '#<data a="">' - 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 == '#<data a="">' - end - - it "does not call #name method" do - struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") - struct.send(@method).should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">' - 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 == '#<data amount=42, unit="km">' - 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 == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>" - 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 =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/ - end - end -end diff --git a/spec/ruby/core/data/to_h_spec.rb b/spec/ruby/core/data/to_h_spec.rb index 64816b7251..41925cf3b2 100644 --- a/spec/ruby/core/data/to_h_spec.rb +++ b/spec/ruby/core/data/to_h_spec.rb @@ -28,18 +28,18 @@ describe "Data#to_h" do data = DataSpecs::Measure.new(amount: 42, unit: 'km') -> do data.to_h { |k, v| [k.to_s, v*v, 1] } - end.should raise_error(ArgumentError, /element has wrong array length/) + end.should.raise(ArgumentError, /element has wrong array length/) -> do data.to_h { |k, v| [k] } - end.should raise_error(ArgumentError, /element has wrong array length/) + end.should.raise(ArgumentError, /element has wrong array length/) end it "raises TypeError if block returns something other than Array" do data = DataSpecs::Measure.new(amount: 42, unit: 'km') -> do data.to_h { |k, v| "not-array" } - end.should raise_error(TypeError, /wrong element type String/) + end.should.raise(TypeError, /wrong element type String/) end it "coerces returned pair to Array with #to_ary" do @@ -57,7 +57,7 @@ describe "Data#to_h" do -> do data.to_h { |k| x } - end.should raise_error(TypeError, /wrong element type MockObject/) + end.should.raise(TypeError, /wrong element type MockObject/) end end end diff --git a/spec/ruby/core/data/to_s_spec.rb b/spec/ruby/core/data/to_s_spec.rb index 2b4a670e8e..e436c21109 100644 --- a/spec/ruby/core/data/to_s_spec.rb +++ b/spec/ruby/core/data/to_s_spec.rb @@ -1,6 +1,63 @@ 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 "returns a string representation showing members and values" do + a = DataSpecs::Measure.new(42, "km") + a.to_s.should == '#<data DataSpecs::Measure amount=42, unit="km">' + end + + it "returns a string representation without the class name for anonymous structs" do + Data.define(:a).new("").to_s.should == '#<data 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 + Foo = Data.define(:a) + DOC + + c::Foo.new("").to_s.should == '#<data a="">' + 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("").to_s.should == '#<data a="">' + end + + it "does not call #name method" do + struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") + struct.to_s.should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">' + 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.to_s.should == '#<data amount=42, unit="km">' + 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.to_s.should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>" + 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.to_s.should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/ + end + end end diff --git a/spec/ruby/core/data/with_spec.rb b/spec/ruby/core/data/with_spec.rb index fd0a99d1fa..b74df185c7 100644 --- a/spec/ruby/core/data/with_spec.rb +++ b/spec/ruby/core/data/with_spec.rb @@ -28,7 +28,7 @@ describe "Data#with" do -> { data.with(4, "m") - }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)") + }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 0)") end it "does not depend on the Data.new method" do @@ -44,14 +44,12 @@ describe "Data#with" do data_copy.unit.should == "m" end - ruby_version_is "3.3" do - it "calls #initialize" do - data = DataSpecs::DataWithOverriddenInitialize.new(42, "m") - ScratchPad.clear + it "calls #initialize" do + data = DataSpecs::DataWithOverriddenInitialize.new(42, "m") + ScratchPad.clear - data.with(amount: 0) + data.with(amount: 0) - ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}] - end + ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}] end end |
