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 tests for exotic rescued error capturing #774

Merged
merged 2 commits into from
Jun 25, 2020

Conversation

sambostock
Copy link
Contributor

The idiomatic way to capture exceptions is to assign to a local variable

rescue => local_variable

However, other kinds of LVALUEs also work

rescue => $global_variable
rescue => @@class_variable
rescue => @instance_variable
rescue => Constant
rescue => receiver&.setter_method
rescue => receiver.setter_method
rescue => receiver[:key]

This adds specs documenting their usage.


Closes #773
cc. @chrisseaton

Comment on lines 27 to 36
# Standard use case
'can capture the raised exception in a local variable' => RescueSpecs::LocalVariableCaptor,
# Exotic use cases
'can capture the raised exception in a class variable' => RescueSpecs::ClassVariableCaptor,
'can capture the raised exception in a constant' => RescueSpecs::ConstantCaptor,
'can capture the raised exception in a global variable' => RescueSpecs::GlobalVariableCaptor,
'can capture the raised exception in an instance variable' => RescueSpecs::InstanceVariableCaptor,
'can capture the raised exception using a safely navigated setter method' => RescueSpecs::SafeNavigationSetterCaptor,
'can capture the raised exception using a setter method' => RescueSpecs::SetterCaptor,
'can capture the raised exception using a square brackets setter' => RescueSpecs::SquareBracketsCaptor,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since most cases would be almost identical, but some required a supporting object on which to set a class/instance variable, or call a setter method, I decided to implement all cases using supporting objects with a shared API, and list them out concisely.

Copy link
Member

Choose a reason for hiding this comment

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

In general we try to avoid dynamically-generated specs in ruby/spec.
I think a middle ground here would be to still to have 1 it per case, but share the logic with the should etc in a method, which you can define here under the describe:

  def should_capture(captor)
    captor.capture('some text').should == :caught # Ensure rescue body still runs
    captor.captured_error.message.should == 'some text'
  end

  it 'can capture the raised exception in a local variable' do
    should_capture(RescueSpecs::LocalVariableCaptor.new)
  end

  ...

Copy link
Member

Choose a reason for hiding this comment

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

The common part can capture the raised exception can also be factored out with an extra describe around.

Copy link
Member

Choose a reason for hiding this comment

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

@sambostock Could you do that change? Then I think it's ready to merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eregon I've pushed a commit that does something similar to your suggestion.

describe 'can capture the raised exception' do
  it 'in a local variable' do
    RescueSpecs::LocalVariableCaptor.should_capture_exception
  end

Let me know what you think!

@sambostock
Copy link
Contributor Author

All CI runs succeeded except CI / specs (windows, 2.5.8) which encountered the following failure:

A child mspec-run process died unexpectedly while running D:/a/spec/spec/core/binding/local_variable_set_spec.rb

Seems unrelated, so I'll just push another commit to try again.

@sambostock
Copy link
Contributor Author

Looks like CI / specs (windows, 2.5.8) is continuing to fail. It looks to be the same error discussed in #765, and is unrelated to this change.

@andrykonchin
Copy link
Member

andrykonchin commented Jun 23, 2020

cc @eregon

I've re-run a build several times and it fails either on MacOS or Windows with some infrastructure error.

@eregon
Copy link
Member

eregon commented Jun 23, 2020

It looks like it passes now.
The failure on Windows 2.5.8 is known: #765

The idiomatic way to capture exceptions is to assign to a local variable

    rescue => local_variable

However, other kinds of LVALUEs also work

    rescue => $global_variable
    rescue => @@class_variable
    rescue => @instance_variable
    rescue => Constant
    rescue => receiver&.setter_method
    rescue => receiver.setter_method
    rescue => receiver[:key]

Some of the tests involve side effects to the global state. We can
remove the constant and class variable, but the best we can go for the
global variable is to nil it out. This is effectively identical to
removing it though, as nil is the default when accessing an undefined
global.
@sambostock
Copy link
Contributor Author

I rebased on latest master, but as mentioned by @eregon, a different test was causing intermittent timeouts on CI / specs (macos, 2.7.1), which I have documented in #775.

In the mean time, I pushed an amended commit to trigger a new run, which passed.

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 great, thanks for the new specs!

@eregon eregon merged commit 148947d into ruby:master Jun 25, 2020
@sambostock sambostock deleted the exotic-error-capturing branch June 26, 2020 04:20
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.

Exotic rescue error capturing not specified
3 participants