From 0a31e95bab91346f3a4864767455fc0f57ca1817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sat, 25 Apr 2015 20:30:30 +0200 Subject: [PATCH 1/2] Make the result objects usable in the new formatter api Set the message on undefined results and append the backtrace with the backtrace line of the step on results that either have an exception (with backtrace) or have a backtrace themselves. Support applying filters on the backtrace. --- lib/cucumber/core/test/result.rb | 33 +++++++++++ lib/cucumber/core/test/runner.rb | 2 + spec/cucumber/core/test/result_spec.rb | 81 ++++++++++++++++++++++++++ spec/cucumber/core/test/runner_spec.rb | 42 +++++++++++++ 4 files changed, 158 insertions(+) diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb index 2d66b3d5..bc699bd4 100644 --- a/lib/cucumber/core/test/result.rb +++ b/lib/cucumber/core/test/result.rb @@ -44,6 +44,14 @@ def describe_to(visitor, *args) def to_s "✓" end + + def with_appended_backtrace(step) + self + end + + def with_filtered_backtrace(filter) + self + end end class Failed @@ -72,6 +80,18 @@ def with_duration(new_duration) self.class.new(new_duration, exception) end + def with_appended_backtrace(step) + return self unless step.respond_to?(:backtrace_line) + new_exception = exception.dup + new_exception.set_backtrace([]) + exception.backtrace.each { |line| new_exception.backtrace << line } + new_exception.backtrace << step.backtrace_line + self.class.new(duration, new_exception) + end + + def with_filtered_backtrace(filter) + self.class.new(duration, filter.new(exception.dup).exception) + end end # Base class for exceptions that can be raised in a step defintion causing @@ -92,6 +112,19 @@ def with_message(new_message) def with_duration(new_duration) self.class.new(message, new_duration, backtrace) end + + def with_appended_backtrace(step) + return self unless step.respond_to?(:backtrace_line) + new_backtrace = [] + backtrace.each { |line| new_backtrace << line } if backtrace + new_backtrace << step.backtrace_line + self.class.new(message, duration, new_backtrace) + end + + def with_filtered_backtrace(filter) + return self unless backtrace + filter.new(dup).exception + end end class Undefined < Raisable diff --git a/lib/cucumber/core/test/runner.rb b/lib/cucumber/core/test/runner.rb index cd617639..99435584 100644 --- a/lib/cucumber/core/test/runner.rb +++ b/lib/cucumber/core/test/runner.rb @@ -97,6 +97,8 @@ def initialize(step_result) def execute(test_step, monitor, &continue) result = test_step.execute(monitor.result, &continue) + result = result.with_message(%(Undefined step: "#{test_step.name}")) if result.undefined? + result = result.with_appended_backtrace(test_step.source.last) if test_step.respond_to?(:source) result.describe_to(monitor, result) end diff --git a/spec/cucumber/core/test/result_spec.rb b/spec/cucumber/core/test/result_spec.rb index 5add4fc9..f6432f72 100644 --- a/spec/cucumber/core/test/result_spec.rb +++ b/spec/cucumber/core/test/result_spec.rb @@ -30,6 +30,14 @@ module Cucumber::Core::Test expect { Result::Passed.new }.to raise_error(ArgumentError) end + it "does nothing when appending the backtrace" do + expect( result.with_appended_backtrace(double) ).to equal result + end + + it "does nothing when filtering the backtrace" do + expect( result.with_filtered_backtrace(double) ).to equal result + end + it { expect( result ).to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).not_to be_undefined } @@ -58,6 +66,31 @@ module Cucumber::Core::Test expect { Result::Failed.new(duration) }.to raise_error(ArgumentError) end + it "does nothing if step has no backtrace line" do + result.exception.set_backtrace("exception backtrace") + step = "does not respond_to?(:backtrace_line)" + + expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace"]) + end + + it "appends the backtrace line of the step" do + result.exception.set_backtrace("exception backtrace") + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace", "step_line"]) + end + + it "apply filters to the exception" do + filter_class = double + filter = double + filtered_exception = double + expect( filter_class ).to receive(:new).with(result.exception).and_return(filter) + expect( filter ).to receive(:exception).and_return(filtered_exception) + + expect( result.with_filtered_backtrace(filter_class).exception ).to equal filtered_exception + end + it { expect( result ).not_to be_passed } it { expect( result ).to be_failed } it { expect( result ).not_to be_undefined } @@ -80,6 +113,54 @@ module Cucumber::Core::Test it { expect( result ).not_to be_skipped } end + describe Result::Raisable do + context "with or without backtrace" do + subject(:result) { Result::Raisable.new } + + it "does nothing if step has no backtrace line" do + step = "does not respond_to?(:backtrace_line)" + + expect( result.with_appended_backtrace(step).backtrace ).to eq(nil) + end + end + + context "without backtrace" do + subject(:result) { Result::Raisable.new } + + it "set the backtrace to the backtrace line of the step" do + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).backtrace ).to eq(["step_line"]) + end + + it "does nothing when filtering the backtrace" do + expect( result.with_filtered_backtrace(double) ).to equal result + end + end + + context "with backtrace" do + subject(:result) { Result::Raisable.new("message", 0, "backtrace") } + + it "appends the backtrace line of the step" do + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).backtrace ).to eq(["backtrace", "step_line"]) + end + + it "apply filters to the backtrace" do + filter_class = double + filter = double + filtered_result = double + expect( filter_class ).to receive(:new).with(result.exception).and_return(filter) + expect( filter ).to receive(:exception).and_return(filtered_result) + + expect( result.with_filtered_backtrace(filter_class) ).to equal filtered_result + end + end + end + describe Result::Undefined do subject(:result) { Result::Undefined.new } diff --git a/spec/cucumber/core/test/runner_spec.rb b/spec/cucumber/core/test/runner_spec.rb index dae21d4b..03df9cb2 100644 --- a/spec/cucumber/core/test/runner_spec.rb +++ b/spec/cucumber/core/test/runner_spec.rb @@ -100,6 +100,24 @@ module Cucumber::Core::Test expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_undefined end + allow( undefined.source.last ).to receive(:name) + test_case.describe_to runner + end + + it 'sets the message on the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.message ).to eq("Undefined step: \"step name\"") + end + expect( undefined.source.last ).to receive(:name).and_return("step name") + test_case.describe_to runner + end + + it 'gets backtrace appended' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace ).to eq(["step line"]) + end + expect( undefined.source.last ).to receive(:backtrace_line).and_return("step line") + allow( undefined.source.last ).to receive(:name) test_case.describe_to runner end end @@ -113,6 +131,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'gets its backtrace appended' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace.last ).to eq("step line") + end + expect( pending.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context "a skipping step" do @@ -124,6 +150,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'gets its backtrace appended' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace.last ).to eq("step line") + end + expect( skipping.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context 'that fail' do @@ -135,6 +169,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'gets its exception backtrace appended' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.exception.backtrace.last ).to eq("step line") + end + expect( failing.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context 'where the first step fails' do From 0e391ee48c801cbe02a84e5652a2a36ff16bd5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sat, 25 Apr 2015 20:35:19 +0200 Subject: [PATCH 2/2] Modify the backtrace in place instead of trying to copy it. Somehow a scenario in a wire feature file in Cucumber failed with a timeout error when copying the object and its backtrace. --- lib/cucumber/core/test/result.rb | 15 +++++---------- spec/cucumber/core/test/runner_spec.rb | 8 ++++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb index bc699bd4..06adb6b5 100644 --- a/lib/cucumber/core/test/result.rb +++ b/lib/cucumber/core/test/result.rb @@ -81,12 +81,8 @@ def with_duration(new_duration) end def with_appended_backtrace(step) - return self unless step.respond_to?(:backtrace_line) - new_exception = exception.dup - new_exception.set_backtrace([]) - exception.backtrace.each { |line| new_exception.backtrace << line } - new_exception.backtrace << step.backtrace_line - self.class.new(duration, new_exception) + exception.backtrace << step.backtrace_line if step.respond_to?(:backtrace_line) + self end def with_filtered_backtrace(filter) @@ -115,10 +111,9 @@ def with_duration(new_duration) def with_appended_backtrace(step) return self unless step.respond_to?(:backtrace_line) - new_backtrace = [] - backtrace.each { |line| new_backtrace << line } if backtrace - new_backtrace << step.backtrace_line - self.class.new(message, duration, new_backtrace) + set_backtrace([]) unless backtrace + backtrace << step.backtrace_line + self end def with_filtered_backtrace(filter) diff --git a/spec/cucumber/core/test/runner_spec.rb b/spec/cucumber/core/test/runner_spec.rb index 03df9cb2..8d718761 100644 --- a/spec/cucumber/core/test/runner_spec.rb +++ b/spec/cucumber/core/test/runner_spec.rb @@ -112,7 +112,7 @@ module Cucumber::Core::Test test_case.describe_to runner end - it 'gets backtrace appended' do + it 'appends the backtrace of the result' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result.backtrace ).to eq(["step line"]) end @@ -132,7 +132,7 @@ module Cucumber::Core::Test test_case.describe_to runner end - it 'gets its backtrace appended' do + it 'appends the backtrace of the result' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result.backtrace.last ).to eq("step line") end @@ -151,7 +151,7 @@ module Cucumber::Core::Test test_case.describe_to runner end - it 'gets its backtrace appended' do + it 'appends the backtrace of the result' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result.backtrace.last ).to eq("step line") end @@ -170,7 +170,7 @@ module Cucumber::Core::Test test_case.describe_to runner end - it 'gets its exception backtrace appended' do + it 'appends the backtrace of the exception of the result' do expect( report ).to receive(:after_test_case) do |test_case, result| expect( result.exception.backtrace.last ).to eq("step line") end