Skip to content

Commit

Permalink
Introduce a Duration object in Cucumber::Core::Test::Result
Browse files Browse the repository at this point in the history
For results which do not have an actual duration (skipped, undefined and
pending) instead hold an null object. Client can either use the "null
duration" (0), or check if it is an actual duration or an null duration.
  • Loading branch information
brasmusson committed Oct 9, 2014
1 parent 6ed926d commit 3e39c8d
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 16 deletions.
36 changes: 29 additions & 7 deletions lib/cucumber/core/test/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Passed

def initialize(duration)
raise ArgumentError unless duration
super
super(Duration.new(duration))
end

def describe_to(visitor, *args)
Expand All @@ -56,7 +56,7 @@ class Failed
def initialize(duration, exception)
raise ArgumentError unless duration
raise ArgumentError unless exception
super
super(Duration.new(duration), exception)
end

def describe_to(visitor, *args)
Expand All @@ -81,8 +81,8 @@ def with_duration(new_duration)
class Raisable < StandardError
attr_reader :message, :duration

def initialize(message = "", duration = :unknown, backtrace = nil)
@message, @duration = message, duration
def initialize(message = "", duration = nil, backtrace = nil)
@message, @duration = message, duration ? Duration.new(duration) : UnknownDuration.new
super(message)
set_backtrace(backtrace) if backtrace
end
Expand All @@ -101,7 +101,7 @@ class Undefined < Raisable

def describe_to(visitor, *args)
visitor.undefined(*args)
visitor.duration(duration, *args) unless duration == :unknown
visitor.duration(duration, *args)
self
end

Expand All @@ -116,7 +116,7 @@ class Skipped < Raisable

def describe_to(visitor, *args)
visitor.skipped(*args)
visitor.duration(duration, *args) unless duration == :unknown
visitor.duration(duration, *args)
self
end

Expand All @@ -131,7 +131,7 @@ class Pending < Raisable

def describe_to(visitor, *args)
visitor.pending(self, *args)
visitor.duration(duration, *args) unless duration == :unknown
visitor.duration(duration, *args)
self
end

Expand Down Expand Up @@ -201,6 +201,28 @@ def total
total_passed + total_failed + total_skipped + total_undefined
end
end

class Duration
attr_reader :duration

def initialize(duration)
@duration = duration
end

def exist?
true
end
end

class UnknownDuration
def exist?
false
end

def duration
0
end
end
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions spec/cucumber/core/test/action_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'cucumber/core/test/action'
require 'cucumber/core/test/duration_matcher'

module Cucumber
module Core
Expand Down Expand Up @@ -76,13 +77,13 @@ module Test
it "records the nanoseconds duration of the execution on the result" do
mapping = Action.new { }
duration = mapping.execute(last_result).duration
expect( duration ).to eq 1
expect( duration ).to be_duration 1
end

it "records the duration of a failed execution" do
mapping = Action.new { raise StandardError }
duration = mapping.execute(last_result).duration
expect( duration ).to eq 1
expect( duration ).to be_duration 1
end
end

Expand Down
19 changes: 19 additions & 0 deletions spec/cucumber/core/test/duration_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- encoding: utf-8 -*-
require 'cucumber/core/test/result'
require 'rspec/expectations'

module Cucumber::Core::Test
RSpec::Matchers.define :be_duration do |expected|
match do |actual|
actual.exist? and actual.duration == expected
end
end

RSpec::Matchers.alias_matcher :a_duration_of, :be_duration

RSpec::Matchers.define :an_unknown_duration do
match do |actual|
not actual.exist? and expect(actual).to respond_to(:duration)
end
end
end
38 changes: 33 additions & 5 deletions spec/cucumber/core/test/result_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- encoding: utf-8 -*-
require 'cucumber/core/test/result'
require 'cucumber/core/test/duration_matcher'

module Cucumber::Core::Test
describe Result do
Expand All @@ -13,7 +14,7 @@ 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(:duration).with(a_duration_of(duration), args)
result.describe_to(visitor, args)
end

Expand All @@ -22,7 +23,7 @@ module Cucumber::Core::Test
end

it "has a duration" do
expect( result.duration ).to eq duration
expect( result.duration ).to be_duration duration
end

it "requires the constructor argument" do
Expand All @@ -43,13 +44,13 @@ module Cucumber::Core::Test

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(:duration).with(a_duration_of(duration), args)
expect( visitor ).to receive(:exception).with(exception, args)
result.describe_to(visitor, args)
end

it "has a duration" do
expect( result.duration ).to eq duration
expect( result.duration ).to be_duration duration
end

it "requires both constructor arguments" do
Expand Down Expand Up @@ -84,6 +85,7 @@ module Cucumber::Core::Test

it "describes itself to a visitor" do
expect( visitor ).to receive(:undefined).with(args)
expect( visitor ).to receive(:duration).with(an_unknown_duration, args)
result.describe_to(visitor, args)
end

Expand All @@ -99,6 +101,7 @@ module Cucumber::Core::Test

it "describes itself to a visitor" do
expect( visitor ).to receive(:skipped).with(args)
expect( visitor ).to receive(:duration).with(an_unknown_duration, args)
result.describe_to(visitor, args)
end

Expand Down Expand Up @@ -158,13 +161,38 @@ module Cucumber::Core::Test

it "records durations" do
[passed, failed].each { |r| r.describe_to summary }
expect( summary.durations ).to eq [11, 10]
expect( summary.durations[0] ).to be_duration 11
expect( summary.durations[1] ).to be_duration 10
end

it "records exceptions" do
[passed, failed].each { |r| r.describe_to summary }
expect( summary.exceptions ).to eq [exception]
end
end

describe Result::Duration do
subject(:duration) { Result::Duration.new(10) }

it "exist? returns true" do
expect( duration.exist? ).to be_truthy
end

it "has a duration" do
expect( duration.duration ).to eq 10
end
end

describe Result::UnknownDuration do
subject(:duration) { Result::UnknownDuration.new }

it "exist? returns false" do
expect( duration.exist? ).to be_falsy
end

it "return duration 0" do
expect( duration.duration ).to eq 0
end
end
end
end
5 changes: 3 additions & 2 deletions spec/cucumber/core/test/runner_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'cucumber/core/test/runner'
require 'cucumber/core/test/case'
require 'cucumber/core/test/step'
require 'cucumber/core/test/duration_matcher'

module Cucumber::Core::Test
describe Runner do
Expand Down Expand Up @@ -33,7 +34,7 @@ module Cucumber::Core::Test

it "records the nanoseconds duration of the execution on the result" do
expect( report ).to receive(:after_test_case) do |reported_test_case, result|
expect( result.duration ).to eq 1
expect( result.duration ).to be_duration 1
end
test_case.describe_to runner
end
Expand All @@ -44,7 +45,7 @@ module Cucumber::Core::Test

it "records the duration" do
expect( report ).to receive(:after_test_case) do |reported_test_case, result|
expect( result.duration ).to eq 1
expect( result.duration ).to be_duration 1
end
test_case.describe_to runner
end
Expand Down

1 comment on commit 3e39c8d

@tooky
Copy link
Member

@tooky tooky commented on 3e39c8d Oct 10, 2014

Choose a reason for hiding this comment

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

@brasmusson looks good.

I wonder if we can have Core::Test::Timer return a Duration object?

We could then set the default Duration on line 84 to UnknownDuration, and pass in an actual Duration where we have one.

Please sign in to comment.