diff --git a/features/fixtures/plain/app/stack_frame_modification/initiators/handled_before_notify.rb b/features/fixtures/plain/app/stack_frame_modification/initiators/handled_before_notify.rb index 544c42827..6e54300f9 100644 --- a/features/fixtures/plain/app/stack_frame_modification/initiators/handled_before_notify.rb +++ b/features/fixtures/plain/app/stack_frame_modification/initiators/handled_before_notify.rb @@ -21,5 +21,9 @@ def step_three end def crash - Bugsnag.notify(RuntimeError.new("oh no")) + begin + "Test".insrt(-1, "!") + rescue Exception => e + Bugsnag.notify(e) + end end \ No newline at end of file diff --git a/features/plain_features/handled_errors.feature b/features/plain_features/handled_errors.feature index 0c183206c..2c5a0b360 100644 --- a/features/plain_features/handled_errors.feature +++ b/features/plain_features/handled_errors.feature @@ -4,10 +4,10 @@ Background: Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa" And I configure the bugsnag endpoint -Scenario Outline: A handled error sends a report +Scenario Outline: A rescued exception sends a report And I set environment variable "RUBY_VERSION" to "" And I have built the service "plain-ruby" - And I run the service "plain-ruby" with the command "bundle exec ruby handled/.rb" + And I run the service "plain-ruby" with the command "bundle exec ruby handled/notify_exception.rb" And I wait for 1 second Then I should receive a request And the request used the "Ruby Bugsnag Notifier" notifier @@ -17,25 +17,45 @@ Scenario Outline: A handled error sends a report And the event "severity" equals "warning" And the event "severityReason.type" equals "handledException" And the exception "errorClass" equals "RuntimeError" - And the "file" of stack frame 0 equals "/usr/src/app/handled/.rb" - And the "lineNumber" of stack frame 0 equals + And the "file" of stack frame 0 equals "/usr/src/app/handled/notify_exception.rb" + And the "lineNumber" of stack frame 0 equals 6 + + Examples: + | ruby version | + | 1.9.3 | + | 2.0 | + | 2.1 | + | 2.2 | + | 2.3 | + | 2.4 | + | 2.5 | + +Scenario Outline: A notified string sends a report + And I set environment variable "RUBY_VERSION" to "" + And I have built the service "plain-ruby" + And I run the service "plain-ruby" with the command "bundle exec ruby handled/notify_string.rb" + And I wait for 1 second + Then I should receive a request + And the request used the "Ruby Bugsnag Notifier" notifier + And the request used payload v4 headers + And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the exception "errorClass" equals "RuntimeError" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/handled/notify_string.rb" + And the "lineNumber" of the top non-bugsnag stackframe equals 8 Examples: - | ruby version | file | lineNumber | - | 1.9.3 | notify_exception | 6 | - | 1.9.3 | notify_string | 8 | - | 2.0 | notify_exception | 6 | - | 2.0 | notify_string | 8 | - | 2.1 | notify_exception | 6 | - | 2.1 | notify_string | 8 | - | 2.2 | notify_exception | 6 | - | 2.2 | notify_string | 8 | - | 2.3 | notify_exception | 6 | - | 2.3 | notify_string | 8 | - | 2.4 | notify_exception | 6 | - | 2.4 | notify_string | 8 | - | 2.5 | notify_exception | 6 | - | 2.5 | notify_string | 8 | + | ruby version | + | 1.9.3 | + | 2.0 | + | 2.1 | + | 2.2 | + | 2.3 | + | 2.4 | + | 2.5 | + Scenario Outline: A handled error doesn't send a report when the :skip_bugsnag flag is set And I set environment variable "RUBY_VERSION" to "" diff --git a/features/plain_features/report_stack_frames.feature b/features/plain_features/report_stack_frames.feature index f37dddd5a..71c7497c1 100644 --- a/features/plain_features/report_stack_frames.feature +++ b/features/plain_features/report_stack_frames.feature @@ -14,33 +14,49 @@ Scenario Outline: Stack frames can be removed And the request used the "Ruby Bugsnag Notifier" notifier And the request used payload v4 headers And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa" - And the "file" of stack frame 0 equals "/usr/src/app/stack_frame_modification/initiators/.rb" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/stack_frame_modification/initiators/.rb" And the "lineNumber" of stack frame 0 equals Examples: | ruby version | initiator | lineNumber | | 1.9.3 | handled_before_notify | 20 | - | 1.9.3 | handled_block | 19 | | 1.9.3 | unhandled_before_notify | 21 | | 2.0 | handled_before_notify | 20 | - | 2.0 | handled_block | 19 | | 2.0 | unhandled_before_notify | 21 | | 2.1 | handled_before_notify | 20 | - | 2.1 | handled_block | 19 | | 2.1 | unhandled_before_notify | 21 | | 2.2 | handled_before_notify | 20 | - | 2.2 | handled_block | 19 | | 2.2 | unhandled_before_notify | 21 | | 2.3 | handled_before_notify | 20 | - | 2.3 | handled_block | 19 | | 2.3 | unhandled_before_notify | 21 | | 2.4 | handled_before_notify | 20 | - | 2.4 | handled_block | 19 | | 2.4 | unhandled_before_notify | 21 | | 2.5 | handled_before_notify | 20 | - | 2.5 | handled_block | 19 | | 2.5 | unhandled_before_notify | 21 | +Scenario Outline: Stack frames can be removed from a notified string + Given I set environment variable "RUBY_VERSION" to "" + And I set environment variable "CALLBACK_INITIATOR" to "handled_block" + And I have built the service "plain-ruby" + And I run the service "plain-ruby" with the command "bundle exec ruby stack_frame_modification/remove_stack_frame.rb" + And I wait for 1 second + Then I should receive a request + And the request used the "Ruby Bugsnag Notifier" notifier + And the request used payload v4 headers + And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/stack_frame_modification/initiators/handled_block.rb" + And the "lineNumber" of the top non-bugsnag stackframe equals 19 + + Examples: + | ruby version | + | 1.9.3 | + | 2.0 | + | 2.1 | + | 2.2 | + | 2.3 | + | 2.4 | + | 2.5 | + Scenario Outline: Stack frames can be marked as in project Given I set environment variable "RUBY_VERSION" to "" And I set environment variable "CALLBACK_INITIATOR" to "" @@ -60,23 +76,42 @@ Scenario Outline: Stack frames can be marked as in project Examples: | ruby version | initiator | | 1.9.3 | handled_before_notify | - | 1.9.3 | handled_block | | 1.9.3 | unhandled_before_notify | | 2.0 | handled_before_notify | - | 2.0 | handled_block | | 2.0 | unhandled_before_notify | | 2.1 | handled_before_notify | - | 2.1 | handled_block | | 2.1 | unhandled_before_notify | | 2.2 | handled_before_notify | - | 2.2 | handled_block | | 2.2 | unhandled_before_notify | | 2.3 | handled_before_notify | - | 2.3 | handled_block | | 2.3 | unhandled_before_notify | | 2.4 | handled_before_notify | - | 2.4 | handled_block | | 2.4 | unhandled_before_notify | | 2.5 | handled_before_notify | - | 2.5 | handled_block | - | 2.5 | unhandled_before_notify | \ No newline at end of file + | 2.5 | unhandled_before_notify | + +Scenario Outline: Stack frames can be marked as in project with a handled string + Given I set environment variable "RUBY_VERSION" to "" + And I set environment variable "CALLBACK_INITIATOR" to "handled_block" + And I have built the service "plain-ruby" + And I run the service "plain-ruby" with the command "bundle exec ruby stack_frame_modification/mark_frames_in_project.rb" + And I wait for 1 second + Then I should receive a request + And the request used the "Ruby Bugsnag Notifier" notifier + And the request used payload v4 headers + And the request contained the api key "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/stack_frame_modification/initiators/handled_block.rb" + And the event "exceptions.0.stacktrace.0.inProject" is null + And the event "exceptions.0.stacktrace.1.inProject" is true + And the event "exceptions.0.stacktrace.2.inProject" is true + And the event "exceptions.0.stacktrace.3.inProject" is true + + Examples: + | ruby version | + | 1.9.3 | + | 2.0 | + | 2.1 | + | 2.2 | + | 2.3 | + | 2.4 | + | 2.5 | \ No newline at end of file diff --git a/features/rails_features/project_root.feature b/features/rails_features/project_root.feature index c98df2696..f906ec5ba 100644 --- a/features/rails_features/project_root.feature +++ b/features/rails_features/project_root.feature @@ -18,7 +18,7 @@ Scenario Outline: Project_root should default to Rails.root And the exception "errorClass" equals "RuntimeError" And the exception "message" starts with "handled string" And the event "metaData.request.url" ends with "/project_root/default" - And the "file" of stack frame 0 equals "app/controllers/project_root_controller.rb" + And the "file" of the top non-bugsnag stackframe equals "app/controllers/project_root_controller.rb" Examples: | ruby_version | rails_version | @@ -49,7 +49,7 @@ Scenario Outline: Project_root can be set in an initializer And the exception "errorClass" equals "RuntimeError" And the exception "message" starts with "handled string" And the event "metaData.request.url" ends with "/project_root/initializer" - And the "file" of stack frame 0 equals "/usr/src/app/controllers/project_root_controller.rb" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/controllers/project_root_controller.rb" Examples: | ruby_version | rails_version | @@ -79,7 +79,7 @@ Scenario Outline: Project_root can be set after an initializer And the exception "errorClass" equals "RuntimeError" And the exception "message" starts with "handled string" And the event "metaData.request.url" ends with "/project_root/after" - And the "file" of stack frame 0 equals "/usr/src/app/controllers/project_root_controller.rb" + And the "file" of the top non-bugsnag stackframe equals "/usr/src/app/controllers/project_root_controller.rb" Examples: | ruby_version | rails_version | diff --git a/features/steps/ruby_notifier_steps.rb b/features/steps/ruby_notifier_steps.rb index b14c167a8..2160ae36d 100644 --- a/features/steps/ruby_notifier_steps.rb +++ b/features/steps/ruby_notifier_steps.rb @@ -3,13 +3,23 @@ When I set environment variable "#{env_var}" to "#{current_ip}" } end + When("I set environment variable {string} to the mock API port") do |env_var| steps %Q{ When I set environment variable "#{env_var}" to "#{MOCK_API_PORT}" } end + When("I set environment variable {string} to the proxy settings with credentials {string}") do |env_var, credentials| steps %Q{ When I set environment variable "#{env_var}" to "#{credentials}@#{current_ip}:#{MOCK_API_PORT}" } +end + +Then(/^the "(.+)" of the top non-bugsnag stackframe equals (\d+|".+")(?: for request (\d+))?$/) do |element, value, request_index| + stacktrace = read_key_path(find_request(request_index)[:body], 'events.0.exceptions.0.stacktrace') + frame_index = stacktrace.find_index { |frame| ! /.*lib\/bugsnag.*\.rb/.match(frame["file"]) } + steps %Q{ + the "#{element}" of stack frame #{frame_index} equals #{value} + } end \ No newline at end of file diff --git a/lib/bugsnag/stacktrace.rb b/lib/bugsnag/stacktrace.rb index b3b257815..42a82461d 100644 --- a/lib/bugsnag/stacktrace.rb +++ b/lib/bugsnag/stacktrace.rb @@ -26,8 +26,7 @@ def initialize(backtrace, configuration) # Parse the stacktrace line - # Skip stacktrace lines inside lib/bugsnag - next(nil) if file.nil? || file =~ %r{lib/bugsnag(/|\.rb)} + next(nil) if file.nil? # Expand relative paths p = Pathname.new(file) diff --git a/spec/report_spec.rb b/spec/report_spec.rb index 2dfe43ad5..32bcd4b39 100644 --- a/spec/report_spec.rb +++ b/spec/report_spec.rb @@ -503,7 +503,12 @@ def gloops it "does not mark the top-most stacktrace line as inProject if out of project" do Bugsnag.configuration.project_root = "/Random/location/here" - Bugsnag.notify(BugsnagTestException.new("It crashed")) + + begin + "Test".prepnd "T" + rescue Exception => e + Bugsnag.notify(e) + end expect(Bugsnag).to have_sent_notification{ |payload, headers| exception = get_exception_from_payload(payload) @@ -514,12 +519,17 @@ def gloops it "marks the top-most stacktrace line as inProject if necessary" do Bugsnag.configuration.project_root = File.expand_path File.dirname(__FILE__) - Bugsnag.notify(BugsnagTestException.new("It crashed")) + + begin + "Test".prepnd "T" + rescue Exception => e + Bugsnag.notify(e) + end expect(Bugsnag).to have_sent_notification{ |payload, headers| exception = get_exception_from_payload(payload) expect(exception["stacktrace"].size).to be >= 1 - expect(exception["stacktrace"].first["inProject"]).to eq(true) + expect(exception["stacktrace"][0]["inProject"]).to eq(true) } end @@ -1050,12 +1060,35 @@ def gloops exception = event["exceptions"][0] expect(exception["errorClass"]).to eq("RuntimeError") expect(exception["message"]).to eq("'nil' was notified as an exception") + } + end - stacktrace = exception["stacktrace"][0] - expect(stacktrace["lineNumber"]).to eq(1047) - expect(stacktrace["file"]).to end_with("spec/report_spec.rb") - expect(stacktrace["code"]["1046"]).to eq(" it 'uses an appropriate message if nil is notified' do") - expect(stacktrace["code"]["1047"]).to eq(" Bugsnag.notify(nil)") + it "includes bugsnag lines marked out of project" do + notify_test_exception + expect(Bugsnag).to have_sent_notification{ |payload, headers| + exception = get_exception_from_payload(payload) + bugsnag_count = 0 + exception["stacktrace"].each do |frame| + if /.*lib\/bugsnag.*\.rb/.match(frame["file"]) + bugsnag_count += 1 + expect(frame["inProject"]).to be_nil + end + end + # 7 is used here as the called bugsnag frames for a `notify` call should be: + # - Bugsnag.notify + # - Report.new + # - Report.initialize + # - Report.generate_exceptions_list + # - Report.generate_exceptions_list | raw_exceptions.map + # - Report.generate_exceptions_list | raw_exceptions.map | block + # - Report.generate_exceptions_list | raw_exceptions.map | block | Stacktrace.new + # However, JRUBY does not include the two `new` frames, resulting in 5 bugsnag frames + if defined?(JRUBY_VERSION) + frame_count = 5 + else + frame_count = 7 + end + expect(bugsnag_count).to equal frame_count } end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2f6061dab..6584fd1ea 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -83,4 +83,4 @@ def have_sent_notification(&matcher) raise "no matcher provided to have_sent_notification (did you use { })" end end -end +end \ No newline at end of file diff --git a/spec/stacktrace_spec.rb b/spec/stacktrace_spec.rb index 749477fbd..57895f2b2 100644 --- a/spec/stacktrace_spec.rb +++ b/spec/stacktrace_spec.rb @@ -2,25 +2,29 @@ describe Bugsnag::Stacktrace do it "includes code in the stack trace" do - _a = 1 - _b = 2 - _c = 3 - notify_test_exception - _d = 4 - _e = 5 - _f = 6 + begin + _a = 1 + _b = 2 + _c = 3 + "Test".prepnd "T" + _d = 4 + _e = 5 + _f = 6 + rescue Exception => e + Bugsnag.notify(e) + end expect(Bugsnag).to have_sent_notification{ |payload, headers| exception = get_exception_from_payload(payload) - starting_line = __LINE__ - 10 - expect(exception["stacktrace"][1]["code"]).to eq({ - (starting_line + 0).to_s => " _a = 1", - (starting_line + 1).to_s => " _b = 2", - (starting_line + 2).to_s => " _c = 3", - (starting_line + 3).to_s => " notify_test_exception", - (starting_line + 4).to_s => " _d = 4", - (starting_line + 5).to_s => " _e = 5", - (starting_line + 6).to_s => " _f = 6" + starting_line = __LINE__ - 13 + expect(exception["stacktrace"][0]["code"]).to eq({ + (starting_line + 0).to_s => ' _a = 1', + (starting_line + 1).to_s => ' _b = 2', + (starting_line + 2).to_s => ' _c = 3', + (starting_line + 3).to_s => ' "Test".prepnd "T"', + (starting_line + 4).to_s => ' _d = 4', + (starting_line + 5).to_s => ' _e = 5', + (starting_line + 6).to_s => ' _f = 6' }) } end