diff --git a/test/error_lifecycle.rb b/test/error_lifecycle.rb new file mode 100644 index 0000000..db89cd5 --- /dev/null +++ b/test/error_lifecycle.rb @@ -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 diff --git a/test/test_error_lifecycle.rb b/test/test_error_lifecycle.rb new file mode 100644 index 0000000..87aaa42 --- /dev/null +++ b/test/test_error_lifecycle.rb @@ -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? + 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