Skip to content

Commit

Permalink
[GR-20446] Add missing specs for Data
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4491
  • Loading branch information
andrykonchin committed Mar 9, 2025
2 parents fa1a18a + a07ec4d commit f32117b
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 9 deletions.
107 changes: 107 additions & 0 deletions spec/ruby/core/data/deconstruct_keys_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

ruby_version_is "3.2" do
describe "Data#deconstruct" do
it "returns a hash of attributes" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)
d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
end

it "requires one argument" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)

-> {
d.deconstruct_keys
}.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
end

it "returns only specified keys" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)

d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
d.deconstruct_keys([:x] ).should == {x: 1}
d.deconstruct_keys([] ).should == {}
end

it "accepts string attribute names" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)
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)
d.deconstruct_keys([:x, :y, :x]).should == {}
end

it "returns at first not existing attribute name" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)

d.deconstruct_keys([:a, :x]).should == {}
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)

d.deconstruct_keys(nil).should == {x: 1, y: 2}
end

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)

-> {
d.deconstruct_keys([0, []])
}.should raise_error(TypeError, "no implicit conversion of Array into Integer")
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/)
end
end
end
10 changes: 10 additions & 0 deletions spec/ruby/core/data/deconstruct_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

ruby_version_is "3.2" do
describe "Data#deconstruct" do
it "returns an array of attribute values" do
DataSpecs::Measure.new(42, "km").deconstruct.should == [42, "km"]
end
end
end
65 changes: 65 additions & 0 deletions spec/ruby/core/data/eql_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

ruby_version_is "3.2" do
describe "Data#eql?" do
it "returns true if the other is the same object" do
a = DataSpecs::Measure.new(42, "km")
a.should.eql?(a)
end

it "returns true if the other has all the same fields" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "km")
a.should.eql?(b)
end

it "returns false if the other is a different object or has different fields" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "mi")
a.should_not.eql?(b)
end

it "returns false if other is of a different class" do
a = DataSpecs::Measure.new(42, "km")
klass = Data.define(*DataSpecs::Measure.members)
b = klass.new(42, "km")
a.should_not.eql?(b)
end

it "returns false if any corresponding elements are not equal with #eql?" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42.0, "mi")
a.should_not.eql?(b)
end

context "recursive structure" do
it "returns true the other is the same object" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: 42, unit: a)

a.should.eql?(a)
end

it "returns true if the other has all the same fields" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: 42, unit: a)

b = DataSpecs::Measure.allocate
b.send(:initialize, amount: 42, unit: b)

a.should.eql?(b)
end

it "returns false if any corresponding elements are not equal with #eql?" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: a, unit: "km")

b = DataSpecs::Measure.allocate
b.send(:initialize, amount: b, unit: "mi")

a.should_not.eql?(b)
end
end
end
end
65 changes: 65 additions & 0 deletions spec/ruby/core/data/equal_value_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

ruby_version_is "3.2" do
describe "Data#==" do
it "returns true if the other is the same object" do
a = DataSpecs::Measure.new(42, "km")
a.should == a
end

it "returns true if the other has all the same fields" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "km")
a.should == b
end

it "returns false if the other is a different object or has different fields" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "mi")
a.should_not == b
end

it "returns false if other is of a different class" do
a = DataSpecs::Measure.new(42, "km")
klass = Data.define(*DataSpecs::Measure.members)
b = klass.new(42, "km")
a.should_not == b
end

it "returns false if any corresponding elements are not equal with #==" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42.0, "mi")
a.should_not == b
end

context "recursive structure" do
it "returns true the other is the same object" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: 42, unit: a)

a.should == a
end

it "returns true if the other has all the same fields" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: 42, unit: a)

b = DataSpecs::Measure.allocate
b.send(:initialize, amount: 42, unit: b)

a.should == b
end

it "returns false if any corresponding elements are not equal with #==" do
a = DataSpecs::Measure.allocate
a.send(:initialize, amount: a, unit: "km")

b = DataSpecs::Measure.allocate
b.send(:initialize, amount: b, unit: "mi")

a.should_not == b
end
end
end
end
6 changes: 6 additions & 0 deletions spec/ruby/core/data/fixtures/classes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ module DataSpecs
guard -> { ruby_version_is "3.2" and Data.respond_to?(:define) } do
Measure = Data.define(:amount, :unit)

class MeasureWithOverriddenName < Measure
def self.name
"A"
end
end

class DataSubclass < Data; end
end
end
27 changes: 27 additions & 0 deletions spec/ruby/core/data/hash_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

ruby_version_is "3.2" do
describe "Data#hash" do
it "returns the same integer for objects with the same content" 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)
end

it "returns different hashes for objects with different values" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "ml")
a.hash.should_not == b.hash

a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(13, "km")
a.hash.should_not == b.hash
end

it "returns different hashes for different classes" do
Data.define(:x).new(1).hash.should != Data.define(:x).new(1).hash
end
end
end
8 changes: 8 additions & 0 deletions spec/ruby/core/data/inspect_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require_relative '../../spec_helper'
require_relative 'shared/inspect'

ruby_version_is "3.2" do
describe "Data#inspect" do
it_behaves_like :data_inspect, :inspect
end
end
54 changes: 54 additions & 0 deletions spec/ruby/core/data/shared/inspect.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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
end
end
8 changes: 8 additions & 0 deletions spec/ruby/core/data/to_s_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require_relative '../../spec_helper'
require_relative 'shared/inspect'

ruby_version_is "3.2" do
describe "Data#to_s" do
it_behaves_like :data_inspect, :to_s
end
end
Loading

0 comments on commit f32117b

Please sign in to comment.