Skip to content

Add Range#minmax specs - #777

Merged
eregon merged 4 commits into
ruby:masterfrom
sambostock:range-minmax
Nov 13, 2020
Merged

Add Range#minmax specs#777
eregon merged 4 commits into
ruby:masterfrom
sambostock:range-minmax

Conversation

@sambostock

@sambostocksambostock commented Jul 4, 2020

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Member

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.

@eregoneregon left a comment

Copy link
Copy Markdown
Member

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!

Comment threadcore/range/minmax_spec.rb Outdated

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

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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.

Comment threadcore/range/minmax_spec.rb
Comment threadcore/range/minmax_spec.rb
Comment threadcore/range/minmax_spec.rb Outdated
@x = mock('x')
@y = mock('y')

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

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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.

@eregoneregon left a comment

Copy link
Copy Markdown
Member

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.

Comment threadcore/range/minmax_spec.rb
Comment threadcore/range/minmax_spec.rb Outdated
@eregon

Copy link
Copy Markdown
Member

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

@eregoneregon left a comment

Copy link
Copy Markdown
Member

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:masterNov 13, 2020
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@sambostock@eregon