Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-20446] Add missing specs for Data #3811

Merged
merged 5 commits into from
Mar 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading