From d156f6f4d0e278571f355be2796545f3d35dea9b Mon Sep 17 00:00:00 2001 From: Cyrille Faucheux Date: Sun, 19 May 2019 22:59:32 +0200 Subject: [PATCH 1/3] Read embeddings from the invoke result and store them in the Passed/Failed result --- lib/cucumber/core/test/action.rb | 18 ++++++++++------ lib/cucumber/core/test/invoke_result.rb | 23 ++++++++++++++++++++ lib/cucumber/core/test/result.rb | 12 +++++++---- spec/cucumber/core/test/action_spec.rb | 28 ++++++++++++++++++++++--- spec/cucumber/core/test/result_spec.rb | 20 +++++++++++++++--- spec/cucumber/core/test/step_spec.rb | 21 +++++++++++++++---- 6 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 lib/cucumber/core/test/invoke_result.rb diff --git a/lib/cucumber/core/test/action.rb b/lib/cucumber/core/test/action.rb index 65b2694a..1b40ac93 100644 --- a/lib/cucumber/core/test/action.rb +++ b/lib/cucumber/core/test/action.rb @@ -3,6 +3,7 @@ require 'cucumber/core/test/timer' require 'cucumber/core/test/result' require 'cucumber/core/test/location' +require 'cucumber/core/test/invoke_result' module Cucumber module Core @@ -21,8 +22,13 @@ def skip(*) def execute(*args) @timer.start - @block.call(*args) - passed + invoke_result = @block.call(*args) + + case invoke_result + when PassedInvokeResult; then passed(invoke_result.embeddings) + when FailedInvokeResult; then failed(invoke_result.exception, invoke_result.embeddings) + else passed + end rescue Result::Raisable => exception exception.with_duration(@timer.duration) rescue Exception => exception @@ -39,12 +45,12 @@ def inspect private - def passed - Result::Passed.new(@timer.duration) + def passed(embeddings = []) + Result::Passed.new(@timer.duration, embeddings) end - def failed(exception) - Result::Failed.new(@timer.duration, exception) + def failed(exception, embeddings = []) + Result::Failed.new(@timer.duration, exception, embeddings) end def skipped diff --git a/lib/cucumber/core/test/invoke_result.rb b/lib/cucumber/core/test/invoke_result.rb new file mode 100644 index 00000000..37c9e098 --- /dev/null +++ b/lib/cucumber/core/test/invoke_result.rb @@ -0,0 +1,23 @@ +module Cucumber + module Core + module Test + class InvokeResult + attr_reader :embeddings + def initialize(embeddings = []) + @embeddings = embeddings + end + end + + class PassedInvokeResult < InvokeResult + end + + class FailedInvokeResult < InvokeResult + attr_reader :exception + def initialize(exception, embeddings = []) + super(embeddings) + @exception = exception + end + end + end + end +end diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb index 77ba3d7c..f146b0c2 100644 --- a/lib/cucumber/core/test/result.rb +++ b/lib/cucumber/core/test/result.rb @@ -54,14 +54,16 @@ def to_message class Passed include Result.query_methods :passed attr_accessor :duration + attr_reader :embeddings def self.ok?(be_strict = false) true end - def initialize(duration) + def initialize(duration, embeddings = []) raise ArgumentError unless duration @duration = duration + @embeddings = embeddings end def describe_to(visitor, *args) @@ -98,16 +100,18 @@ class Failed include Result.query_methods :failed attr_reader :duration, :exception + attr_reader :embeddings def self.ok?(be_strict = false) false end - def initialize(duration, exception) + def initialize(duration, exception, embeddings = []) raise ArgumentError unless duration raise ArgumentError unless exception @duration = duration @exception = exception + @embeddings = embeddings end def describe_to(visitor, *args) @@ -140,7 +144,7 @@ def ok?(be_strict = nil) end def with_duration(new_duration) - self.class.new(new_duration, exception) + self.class.new(new_duration, exception, embeddings) end def with_appended_backtrace(step) @@ -149,7 +153,7 @@ def with_appended_backtrace(step) end def with_filtered_backtrace(filter) - self.class.new(duration, filter.new(exception.dup).exception) + self.class.new(duration, filter.new(exception.dup).exception, embeddings) end end diff --git a/spec/cucumber/core/test/action_spec.rb b/spec/cucumber/core/test/action_spec.rb index f75e5eb5..3dff4e87 100644 --- a/spec/cucumber/core/test/action_spec.rb +++ b/spec/cucumber/core/test/action_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'cucumber/core/test/action' require 'cucumber/core/test/duration_matcher' +require 'cucumber/core/test/invoke_result' module Cucumber module Core @@ -46,15 +47,18 @@ module Test it "returns a passed result if the block doesn't fail" do action = Action.new {} - expect( action.execute ).to be_passed + result = action.execute + expect( result ).to be_passed + expect( result.embeddings ).to be_empty end it "returns a failed result when the block raises an error" do exception = StandardError.new action = Action.new { raise exception } result = action.execute - expect( result ).to be_failed - expect( result.exception ).to eq exception + expect( result ).to be_failed + expect( result.exception ).to eq exception + expect( result.embeddings ).to be_empty end it "yields the args passed to #execute to the block" do @@ -89,6 +93,24 @@ module Test expect( result.message ).to eq "new step" end + it "returns a passed result if the block returns a PassedInvokeResult" do + embeddings = [double] + action = Action.new { PassedInvokeResult.new(embeddings) } + result = action.execute + expect( result ).to be_passed + expect( result.embeddings ).to eq embeddings + end + + it "returns a failed result when the block returns a FailedInvokeResult" do + exception = StandardError.new + embeddings = [double] + action = Action.new { FailedInvokeResult.new(exception, embeddings) } + result = action.execute + expect( result ).to be_failed + expect( result.exception ).to eq exception + expect( result.embeddings ).to eq embeddings + end + context "recording the duration" do before do allow( Timer::MonotonicTime ).to receive(:time_in_nanoseconds).and_return(525702744080000, 525702744080001) diff --git a/spec/cucumber/core/test/result_spec.rb b/spec/cucumber/core/test/result_spec.rb index d7418a22..32b01c1f 100644 --- a/spec/cucumber/core/test/result_spec.rb +++ b/spec/cucumber/core/test/result_spec.rb @@ -10,8 +10,11 @@ module Cucumber::Core::Test let(:args) { double('args') } describe Result::Passed do - subject(:result) { Result::Passed.new(duration) } + subject(:result) { Result::Passed.new(duration, embeddings) } let(:duration) { Result::Duration.new(1 * 1000 * 1000) } + let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } + let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } + let(:embeddings) { [embedding1, embedding2] } it "describes itself to a visitor" do expect( visitor ).to receive(:passed).with(args) @@ -32,7 +35,11 @@ module Cucumber::Core::Test expect( result.duration ).to eq duration end - it "requires the constructor argument" do + it "has embeddings" do + expect( result.embeddings ).to eq embeddings + end + + it "requires the first constructor argument" do expect { Result::Passed.new }.to raise_error(ArgumentError) end @@ -58,9 +65,12 @@ module Cucumber::Core::Test end describe Result::Failed do - subject(:result) { Result::Failed.new(duration, exception) } + subject(:result) { Result::Failed.new(duration, exception, embeddings) } let(:duration) { Result::Duration.new(1 * 1000 * 1000) } let(:exception) { StandardError.new("error message") } + let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } + let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } + let(:embeddings) { [embedding1, embedding2] } it "describes itself to a visitor" do expect( visitor ).to receive(:failed).with(args) @@ -78,6 +88,10 @@ module Cucumber::Core::Test expect(message.status).to eq(Cucumber::Messages::TestStepResult::Status::FAILED) end + it "has embeddings" do + expect( result.embeddings ).to eq embeddings + end + it "requires both constructor arguments" do expect { Result::Failed.new }.to raise_error(ArgumentError) expect { Result::Failed.new(duration) }.to raise_error(ArgumentError) diff --git a/spec/cucumber/core/test/step_spec.rb b/spec/cucumber/core/test/step_spec.rb index 64ef8dd4..d24ad053 100644 --- a/spec/cucumber/core/test/step_spec.rb +++ b/spec/cucumber/core/test/step_spec.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require 'cucumber/core/test/step' +require 'cucumber/core/test/invoke_result' module Cucumber::Core::Test describe Step do @@ -38,22 +39,34 @@ module Cucumber::Core::Test expect(args_spy).to eq expected_args end - context "when a passing action exists" do - it "returns a passing result" do + context "passing action" do + it "returns a passing result when the step invocation doesn't fail" do test_step = Step.new(id, text, location).with_action {} expect( test_step.execute ).to be_passed end + + it "returns a passing result when the step invocation returns a PassedInvokeResult" do + test_step = Step.new(id, text, location).with_action { PassedInvokeResult.new } + expect( test_step.execute ).to be_passed + end end - context "when a failing action exists" do + context "failing action" do let(:exception) { StandardError.new('oops') } - it "returns a failing result" do + it "returns a failing result when the step invocation raises an error" do test_step = Step.new(id, text, location).with_action { raise exception } result = test_step.execute expect( result ).to be_failed expect( result.exception ).to eq exception end + + it "returns a failing result when the step invocation returns a FailedInvokeResult" do + test_step = Step.new(id, text, location).with_action { FailedInvokeResult.new(exception) } + result = test_step.execute + expect( result ).to be_failed + expect( result.exception ).to eq exception + end end context "with no action" do From 6a16e69eed6d41ac0f2232bdbb34382758ea10e8 Mon Sep 17 00:00:00 2001 From: Cyrille Faucheux Date: Mon, 10 Jun 2019 12:02:55 +0200 Subject: [PATCH 2/3] Make Result describe its embeddings --- lib/cucumber/core/test/result.rb | 6 ++++++ lib/cucumber/core/test/runner.rb | 4 ++++ spec/cucumber/core/test/result_spec.rb | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb index f146b0c2..b060007c 100644 --- a/lib/cucumber/core/test/result.rb +++ b/lib/cucumber/core/test/result.rb @@ -69,6 +69,7 @@ def initialize(duration, embeddings = []) def describe_to(visitor, *args) visitor.passed(*args) visitor.duration(duration, *args) + @embeddings.each { |e| visitor.embed(e['src'], e['mime_type'], e['label']) } self end @@ -118,6 +119,7 @@ def describe_to(visitor, *args) visitor.failed(*args) visitor.duration(duration, *args) visitor.exception(exception, *args) if exception + @embeddings.each { |e| visitor.embed(e['src'], e['mime_type'], e['label']) } self end @@ -371,6 +373,10 @@ def duration(duration) self end + def embed(*) + self + end + def total(for_status = nil) if for_status @totals.fetch(for_status) { 0 } diff --git a/lib/cucumber/core/test/runner.rb b/lib/cucumber/core/test/runner.rb index 1e5f31fe..d1fd71aa 100644 --- a/lib/cucumber/core/test/runner.rb +++ b/lib/cucumber/core/test/runner.rb @@ -90,6 +90,10 @@ def duration(step_duration, step_result) self end + def embed(*) + self + end + attr_reader :status private :status diff --git a/spec/cucumber/core/test/result_spec.rb b/spec/cucumber/core/test/result_spec.rb index 32b01c1f..fee74199 100644 --- a/spec/cucumber/core/test/result_spec.rb +++ b/spec/cucumber/core/test/result_spec.rb @@ -19,6 +19,8 @@ module Cucumber::Core::Test it "describes itself to a visitor" do expect( visitor ).to receive(:passed).with(args) expect( visitor ).to receive(:duration).with(duration, args) + expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered + expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered result.describe_to(visitor, args) end @@ -76,6 +78,8 @@ module Cucumber::Core::Test expect( visitor ).to receive(:failed).with(args) expect( visitor ).to receive(:duration).with(duration, args) expect( visitor ).to receive(:exception).with(exception, args) + expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered + expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered result.describe_to(visitor, args) end From 62ddf161ea337aeb66e4b5f5b2ea04564bce3b88 Mon Sep 17 00:00:00 2001 From: Cyrille Faucheux Date: Sun, 25 Aug 2019 17:48:30 +0200 Subject: [PATCH 3/3] Add tests for Result::Passed/Failed with & without embeddings --- spec/cucumber/core/test/result_spec.rb | 259 ++++++++++++++++--------- 1 file changed, 166 insertions(+), 93 deletions(-) diff --git a/spec/cucumber/core/test/result_spec.rb b/spec/cucumber/core/test/result_spec.rb index fee74199..b49a0896 100644 --- a/spec/cucumber/core/test/result_spec.rb +++ b/spec/cucumber/core/test/result_spec.rb @@ -10,133 +10,206 @@ module Cucumber::Core::Test let(:args) { double('args') } describe Result::Passed do - subject(:result) { Result::Passed.new(duration, embeddings) } - let(:duration) { Result::Duration.new(1 * 1000 * 1000) } - let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } - let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } - let(:embeddings) { [embedding1, embedding2] } - - it "describes itself to a visitor" do - expect( visitor ).to receive(:passed).with(args) - expect( visitor ).to receive(:duration).with(duration, args) - expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered - expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered - result.describe_to(visitor, args) + it "requires the first constructor argument" do + expect { Result::Passed.new }.to raise_error(ArgumentError) end - it "converts to a string" do - expect( result.to_s ).to eq "✓" - end + shared_examples "a Result::Passed object" do + it "describes itself to a visitor" do + expect( visitor ).to receive(:passed).with(args) + expect( visitor ).to receive(:duration).with(duration, args) + allow( visitor ).to receive(:embed) # Will be verified later + result.describe_to(visitor, args) + end - it "converts to a Cucumber::Message::TestResult" do - message = result.to_message - expect(message.status).to eq(Cucumber::Messages::TestStepResult::Status::PASSED) - end + it "converts to a string" do + expect( result.to_s ).to eq "✓" + end - it "has a duration" do - expect( result.duration ).to eq duration - end + it "converts to a Cucumber::Message::TestResult" do + message = result.to_message + expect(message.status).to eq(Cucumber::Messages::TestStepResult::Status::PASSED) + end - it "has embeddings" do - expect( result.embeddings ).to eq embeddings - end + it "has a duration" do + expect( result.duration ).to eq duration + end - it "requires the first constructor argument" do - 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 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 + + specify { expect( result.to_sym ).to eq :passed } + + specify { expect( result ).to be_passed } + specify { expect( result ).not_to be_failed } + specify { expect( result ).not_to be_undefined } + specify { expect( result ).not_to be_unknown } + specify { expect( result ).not_to be_skipped } + specify { expect( result ).not_to be_flaky } - it "does nothing when filtering the backtrace" do - expect( result.with_filtered_backtrace(double) ).to equal result + specify { expect( result ).to be_ok } + specify { expect( result.ok? ).to be_truthy } end - specify { expect( result.to_sym ).to eq :passed } + context "initialized without embeddings" do + let(:duration) { Result::Duration.new(1 * 1000 * 1000) } + subject(:result) { Result::Passed.new(duration) } - specify { expect( result ).to be_passed } - specify { expect( result ).not_to be_failed } - specify { expect( result ).not_to be_undefined } - specify { expect( result ).not_to be_unknown } - specify { expect( result ).not_to be_skipped } - specify { expect( result ).not_to be_flaky } + include_examples "a Result::Passed object" - specify { expect( result ).to be_ok } - specify { expect( result.ok? ).to be_truthy } - end + it "does not contain embeddings" do + expect( result.embeddings ).to be_empty + end - describe Result::Failed do - subject(:result) { Result::Failed.new(duration, exception, embeddings) } - let(:duration) { Result::Duration.new(1 * 1000 * 1000) } - let(:exception) { StandardError.new("error message") } - let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } - let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } - let(:embeddings) { [embedding1, embedding2] } + it "does not describe any embeddings to a visitor" do + # Already verified + allow( visitor ).to receive(:passed) + allow( visitor ).to receive(:duration) - it "describes itself to a visitor" do - expect( visitor ).to receive(:failed).with(args) - expect( visitor ).to receive(:duration).with(duration, args) - expect( visitor ).to receive(:exception).with(exception, args) - expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered - expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered - result.describe_to(visitor, args) + expect( visitor ).not_to receive(:embed) + result.describe_to(visitor, args) + end end - it "has a duration" do - expect( result.duration ).to eq duration - end + context "initialized with embeddings" do + let(:duration) { Result::Duration.new(1 * 1000 * 1000) } + let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } + let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } + let(:embeddings) { [embedding1, embedding2] } + subject(:result) { Result::Passed.new(duration, embeddings) } - it "converts to a Cucumber::Message::TestResult" do - message = result.to_message - expect(message.status).to eq(Cucumber::Messages::TestStepResult::Status::FAILED) - end + include_examples "a Result::Passed object" + + it "contains embeddings" do + expect( result.embeddings ).to eq embeddings + end + + it "describes its embeddings to a visitor" do + # Already verified + allow( visitor ).to receive(:passed) + allow( visitor ).to receive(:duration) - it "has embeddings" do - expect( result.embeddings ).to eq embeddings + expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered + expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered + result.describe_to(visitor, args) + end end + end + describe Result::Failed do it "requires both constructor arguments" do expect { Result::Failed.new }.to raise_error(ArgumentError) - expect { Result::Failed.new(duration) }.to raise_error(ArgumentError) + expect { Result::Failed.new(Result::Duration.new(1)) }.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)" + shared_examples "a Result::Failed object" do + it "describes itself to a visitor" do + expect( visitor ).to receive(:failed).with(args) + expect( visitor ).to receive(:duration).with(duration, args) + expect( visitor ).to receive(:exception).with(exception, args) + allow( visitor ).to receive(:embed) # Will be verified later + result.describe_to(visitor, args) + end - expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace"]) - end + it "has a duration" do + expect( result.duration ).to eq duration + end + + it "converts to a Cucumber::Message::TestResult" do + message = result.to_message + expect(message.status).to eq(Cucumber::Messages::TestStepResult::Status::FAILED) + end + + it "does nothing if step has no backtrace line" do + result.exception.set_backtrace("exception backtrace") + step = "does not respond_to?(:backtrace_line)" - 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"]) + end - expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace", "step_line"]) + 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 + + specify { expect( result.to_sym ).to eq :failed } + + specify { expect( result ).not_to be_passed } + specify { expect( result ).to be_failed } + specify { expect( result ).not_to be_undefined } + specify { expect( result ).not_to be_unknown } + specify { expect( result ).not_to be_skipped } + specify { expect( result ).not_to be_flaky } + + specify { expect( result ).to_not be_ok } + specify { expect( result.ok? ).to be_falsey } 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) + context "initialized without embeddings" do + let(:duration) { Result::Duration.new(1 * 1000 * 1000) } + let(:exception) { StandardError.new("error message") } + subject(:result) { Result::Failed.new(duration, exception) } - expect( result.with_filtered_backtrace(filter_class).exception ).to equal filtered_exception + include_examples "a Result::Failed object" + + it "does not contain embeddings" do + expect( result.embeddings ).to be_empty + end + + it "does not describe any embeddings to a visitor" do + # Already verified + allow( visitor ).to receive(:failed) + allow( visitor ).to receive(:duration) + allow( visitor ).to receive(:exception) + + expect( visitor ).not_to receive(:embed) + result.describe_to(visitor, args) + end end - specify { expect( result.to_sym ).to eq :failed } + context "initialized with embeddings" do + let(:duration) { Result::Duration.new(1 * 1000 * 1000) } + let(:exception) { StandardError.new("error message") } + let(:embedding1) { {'src' => 'src1', 'mime_type' => 'mime_type1', 'label' => 'label1'} } + let(:embedding2) { {'src' => 'src2', 'mime_type' => 'mime_type2', 'label' => 'label2'} } + let(:embeddings) { [embedding1, embedding2] } + subject(:result) { Result::Failed.new(duration, exception, embeddings) } - specify { expect( result ).not_to be_passed } - specify { expect( result ).to be_failed } - specify { expect( result ).not_to be_undefined } - specify { expect( result ).not_to be_unknown } - specify { expect( result ).not_to be_skipped } - specify { expect( result ).not_to be_flaky } + include_examples "a Result::Failed object" - specify { expect( result ).to_not be_ok } - specify { expect( result.ok? ).to be_falsey } + it "contains embeddings" do + expect( result.embeddings ).to eq embeddings + end + + it "describes its embeddings to a visitor" do + # Already verified + allow( visitor ).to receive(:failed) + allow( visitor ).to receive(:duration) + allow( visitor ).to receive(:exception) + + expect( visitor ).to receive(:embed).with(embedding1['src'], embedding1['mime_type'], embedding1['label']).ordered + expect( visitor ).to receive(:embed).with(embedding2['src'], embedding2['mime_type'], embedding2['label']).ordered + result.describe_to(visitor, args) + end + end end describe Result::Unknown do