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

Error Lifecycle Tests #33

Closed
wants to merge 15 commits into from
Closed
49 changes: 49 additions & 0 deletions test/error_lifecycle.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class MyStandardError < StandardError; end
class MyException< Exception; end

class ErrorLifeCycleTester
attr_reader :inner_attempted, :inner_else, :inner_rescue, :inner_ensure, :inner_ensure_has_time_to_finish,
:outer_rescue, :outer_else, :outer_ensure, :outer_ensure_has_time_to_finish

def subject(error_to_raise, error_to_rescue)
@inner_attempted = false
@inner_else = false
@inner_rescue = false
@inner_ensure = false
@inner_ensure_has_time_to_finish = false

@outer_rescue = false
@outer_else = false
@outer_ensure = false
@outer_ensure_has_time_to_finish = false

begin
Timeout.timeout(0.001, error_to_raise) do
@inner_attempted = true
nil while true
rescue error_to_rescue
@inner_rescue = true
else
@inner_else = true
ensure
@inner_ensure = true
t = Time.now; nil while Time.now < t+1
@inner_ensure_has_time_to_finish = true
end
rescue Exception
@outer_rescue = true
else
@outer_else = true
ensure
@outer_ensure = true
t = Time.now; nil while Time.now < t+1
@outer_ensure_has_time_to_finish = true
end

# this is here to avoid cluttering the "UNDESIRED?" section of each test,
# can be flatted into the main tests
unless @outer_else ^ @outer_rescue
raise "something strange happened with the outer_rescue variables"
end
end
end
93 changes: 93 additions & 0 deletions test/test_error_lifecycle.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
require 'test/unit'
require 'timeout'
require 'thread'

class TestErrorLifecycle < Test::Unit::TestCase

# Behavior marked "UNDESIRED?" is done so as John's opinion, these can/should be removed before the PR is merged

require_relative 'error_lifecycle.rb'

def core_assertions(s)
assert s.inner_attempted
assert !s.inner_else
assert s.inner_ensure
assert s.outer_ensure

# This can result in user's expectation of total possible time
# being very wrong
# t = Time.now; Timeout.timeout(0.1){begin; sleep 1; ensure; sleep 2; end} rescue puts Time.now-t
# => 2.106306
assert s.inner_ensure_has_time_to_finish
assert s.outer_ensure_has_time_to_finish
end

# when an exception to raise is not specified and the inner code does not catch Exception
def test_1
s = ErrorLifeCycleTester.new
s.subject(nil, StandardError)
core_assertions(s)

assert !s.inner_rescue
assert s.outer_rescue
end

# when an exception to raise is not specified and the inner code does catch Exception
def test_2
s = ErrorLifeCycleTester.new
s.subject(nil, Exception)
core_assertions(s)

assert s.inner_rescue # true in 1.9, false in gem 0.2.0, true in gem 0.4.0

# UNDESIRED?
Copy link
Member

@eregon eregon Jul 3, 2023

Choose a reason for hiding this comment

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

All of these UNDESIRED? are expected and simply testing whether one can rescue an exception from Timeout.
If you rescue an exception, you are responsible for handling it.
If you don't do anything in the rescue and rescue Exception, it's a bug of that code.
As Jeremy said in #30 (comment)

assert !s.outer_rescue # false in 1.9 stdlib, true in gem 0.2.0, false in gem 0.4.0
end

# when an exception to raise is StandardError and the inner code does not catch Exception
def test_3
s = ErrorLifeCycleTester.new
s.subject(MyStandardError, StandardError)
core_assertions(s)

assert s.inner_rescue

# UNDESIRED?
assert !s.outer_rescue
end

# when an exception to raise is StandardError and the inner code does catch Exception
def test_4
s = ErrorLifeCycleTester.new
s.subject(MyStandardError, Exception)
core_assertions(s)

assert s.inner_rescue

# UNDESIRED?
assert !s.outer_rescue
end

# when an exception to raise is Exception and the inner code does not catch Exception
def test_5
s = ErrorLifeCycleTester.new
s.subject(MyException, StandardError)
core_assertions(s)

assert !s.inner_rescue
assert s.outer_rescue
end

# when an exception to raise is Exception and the inner code does catch Exception
def test_6
s = ErrorLifeCycleTester.new
s.subject(MyException, Exception)
core_assertions(s)

assert s.inner_rescue

# UNDESIRED?
assert !s.outer_rescue
end

end