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

Add Range#minmax specs #777

Merged
merged 4 commits into from
Nov 13, 2020
Merged

Add Range#minmax specs #777

merged 4 commits into from
Nov 13, 2020

Conversation

sambostock
Copy link
Contributor

@sambostock sambostock commented Jul 4, 2020

This adds specs documenting the behaviour of Range#minmax, as updated in Ruby 2.7 (see #745). The specs specifically check that as of 2.7, simple ranges have their minmax computed in an optimized manner, without enumerating the range. They also check a few other cases, to ensure other minmax functionality works as expected.

While writing these specs, I discovered a bug in the handling of non-numeric exclusive ranges in MRI, which I have submitted a fix for in ruby/ruby#3285. In the meantime, I have included a temporary monkey-patch, so the correct specs can be written.

⚠️ Before Merging

  • Rebase and remove monkey-patch commit after Range#minmax fix is released

Questions for Reviewers

  • Are these specs sufficient? Should numeric ranges be documented as well? How much of Enumerable#minmax's specs should be duplicated?
  • Is this the best way to split the specs?
    I initially had completely separate specs, but tried to reduce duplication by grouping them. I also experimented with shared examples, but I couldn't really get it to work and it produced specs that were unclear.

@eregon
Copy link
Member

eregon commented Jul 4, 2020

To replace the monkey-patch, let's use a ruby_bug "2.7.0"...."2.8" guard as described in https://github.com/ruby/spec/blob/master/CONTRIBUTING.md#guard-for-bug

Maybe 2.7.2/2.7.3 could be used as upper bound, but first we need ruby/ruby#3285 (comment) then. We can always adjust it later.

Copy link
Member

@eregon eregon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, thanks for the new specs!


describe 'without enumerating the range' do
before(:each) do
# Prevent enumeration, but allow members to respond_to? :succ
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify like: to respond_to?(:succ) => false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually the opposite. This ensures they respond_to? :succ, but that if it is ever actually called, the test fails.

The MRI implementation checks that it could iterate from the range beginning via .succ, even if it does not actually end up calling it, so we need to ensure the objects respond to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way, it means I should clarify the comment.

core/range/minmax_spec.rb Show resolved Hide resolved
core/range/minmax_spec.rb Show resolved Hide resolved
@x = mock('x')
@y = mock('y')

@x.should_receive(:<=>).with(@y).any_number_of_times.and_return(-1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment like # x < y would be helpful to read to understand the intent of those lines faster

This adds specs for Range#minmax, since the implementation provided by
Enumerable is overidden as of 2.7, to try to skip enumerating the range.
@sambostock
Copy link
Contributor Author

After going through the code a little more, I realized there were some edge cases I didn't include. I'll update with those soon, but I got distracted by stumbling across another bug in one of the edge cases, this time in both Range#max and Range#minmax.

Copy link
Member

@eregon eregon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure looks more readable now, thanks!

Regarding CI, Range#minmax on an inclusive range should raise RangeError on a beginless range seems a bug, can you file a ticket & use ruby_bug?

For Range#minmax on an exclusive range should raise RangeError on a beginless range it's just a different error message, so the spec should be adapted.

core/range/minmax_spec.rb Show resolved Hide resolved
core/range/minmax_spec.rb Outdated Show resolved Hide resolved
@eregon
Copy link
Member

eregon commented Oct 24, 2020

@sambostock could you address my latest comments? Then it should be ready to merge.

Copy link
Member

@eregon eregon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the specs, I did the final touches to integrate it.

@eregon eregon merged commit 059711a into ruby:master Nov 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants