Skip to content

Refactored TimerTask specs. #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 105 additions & 115 deletions spec/concurrent/timer_task_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require 'spec_helper'
require_relative 'dereferenceable_shared'
require_relative 'observable_shared'
require_relative 'runnable_shared'

module Concurrent

Expand All @@ -13,30 +12,24 @@ module Concurrent
Concurrent::TimerTask.stub(:warn)
end

after(:each) do
@subject = @subject.runner if @subject.respond_to?(:runner)
@subject.kill unless @subject.nil?
@thread.kill unless @thread.nil?
sleep(0.1)
end

context :runnable do

subject { TimerTask.new{ nil } }

it_should_behave_like :runnable
end

context :dereferenceable do

after(:each) do
begin
@subject.kill if @subject
rescue Exception => ex
# prevent exceptions with mocks in tests
end
end

def dereferenceable_subject(value, opts = {})
opts = opts.merge(execution_interval: 0.1, run_now: true)
TimerTask.new(opts){ value }.execute.tap{ sleep(0.1) }
@subject = TimerTask.new(opts){ value }.execute.tap{ sleep(0.1) }
end

def dereferenceable_observable(opts = {})
opts = opts.merge(execution_interval: 0.1, run_now: true)
TimerTask.new(opts){ 'value' }
@subject = TimerTask.new(opts){ 'value' }
end

def execute_dereferenceable(subject)
Expand All @@ -48,9 +41,11 @@ def execute_dereferenceable(subject)
end

context :observable do

subject{ TimerTask.new(execution_interval: 0.1){ nil } }


after(:each){ subject.kill }

def trigger_observable(observable)
observable.execute
sleep(0.2)
Expand All @@ -65,156 +60,148 @@ def trigger_observable(observable)

it 'raises an exception if no block given' do
lambda {
@subject = Concurrent::TimerTask.new
Concurrent::TimerTask.new
}.should raise_error(ArgumentError)
end

it 'raises an exception if :execution_interval is not greater than zero' do
lambda {
@subject = Concurrent::TimerTask.new(execution_interval: 0){ nil }
Concurrent::TimerTask.new(execution_interval: 0){ nil }
}.should raise_error(ArgumentError)
end

it 'raises an exception if :execution_interval is not an integer' do
lambda {
@subject = Concurrent::TimerTask.new(execution_interval: 'one'){ nil }
Concurrent::TimerTask.new(execution_interval: 'one'){ nil }
}.should raise_error(ArgumentError)
end

it 'raises an exception if :timeout_interval is not greater than zero' do
lambda {
@subject = Concurrent::TimerTask.new(timeout_interval: 0){ nil }
Concurrent::TimerTask.new(timeout_interval: 0){ nil }
}.should raise_error(ArgumentError)
end

it 'raises an exception if :timeout_interval is not an integer' do
lambda {
@subject = Concurrent::TimerTask.new(timeout_interval: 'one'){ nil }
Concurrent::TimerTask.new(timeout_interval: 'one'){ nil }
}.should raise_error(ArgumentError)
end

it 'uses the default execution interval when no interval is given' do
@subject = TimerTask.new{ nil }
@subject.execution_interval.should eq TimerTask::EXECUTION_INTERVAL
subject = TimerTask.new{ nil }
subject.execution_interval.should eq TimerTask::EXECUTION_INTERVAL
end

it 'uses the default timeout interval when no interval is given' do
@subject = TimerTask.new{ nil }
@subject.timeout_interval.should eq TimerTask::TIMEOUT_INTERVAL
subject = TimerTask.new{ nil }
subject.timeout_interval.should eq TimerTask::TIMEOUT_INTERVAL
end

it 'uses the given execution interval' do
@subject = TimerTask.new(execution_interval: 5){ nil }
@subject.execution_interval.should eq 5
subject = TimerTask.new(execution_interval: 5){ nil }
subject.execution_interval.should eq 5
end

it 'uses the given timeout interval' do
@subject = TimerTask.new(timeout_interval: 5){ nil }
@subject.timeout_interval.should eq 5
subject = TimerTask.new(timeout_interval: 5){ nil }
subject.timeout_interval.should eq 5
end
end

context '#kill' do

it 'returns true on success' do
task = TimerTask.new(run_now: false){ nil }
task.run!
task = TimerTask.execute(run_now: false){ nil }
sleep(0.1)
task.kill.should be_true
end
end
end

context 'created with TimerTask.run!' do
context 'arguments' do

context 'arguments' do
it 'raises an exception if no block given' do
lambda {
Concurrent::TimerTask.execute
}.should raise_error
end

it 'raises an exception if no block given' do
lambda {
@subject = Concurrent::TimerTask.run
}.should raise_error
end
specify '#execution_interval is writeable' do

it 'passes the block to the new TimerTask' do
@expected = false
block = proc{ @expected = true }
@subject = TimerTask.run!(run_now: true, &block)
sleep(0.1)
@expected.should be_true
latch = CountDownLatch.new(1)
subject = TimerTask.new(execution_interval: 1) do |task|
task.execution_interval = 3
latch.count_down
end

it 'creates a new thread' do
thread = Thread.new{ sleep(1) }
Thread.should_receive(:new).with(any_args()).and_return(thread)
@subject = TimerTask.run!{ nil }
end
subject.execution_interval.should == 1
subject.execution_interval = 0.1
subject.execution_interval.should == 0.1

specify '#execution_interval is writeable' do
@subject = TimerTask.new(execution_interval: 1) do |task|
task.execution_interval = 3
end
@subject.execution_interval.should == 1
@subject.execution_interval = 0.1
@subject.execution_interval.should == 0.1
@thread = Thread.new { @subject.run }
sleep(0.2)
@subject.execution_interval.should == 3
end
subject.execute
latch.wait(0.2)

specify '#execution_interval is writeable' do
@subject = TimerTask.new(timeout_interval: 1, execution_interval: 0.1) do |task|
task.timeout_interval = 3
end
@subject.timeout_interval.should == 1
@subject.timeout_interval = 2
@subject.timeout_interval.should == 2
@thread = Thread.new { @subject.run }
sleep(0.2)
@subject.timeout_interval.should == 3
subject.execution_interval.should == 3
subject.kill
end

specify '#execution_interval is writeable' do

latch = CountDownLatch.new(1)
subject = TimerTask.new(timeout_interval: 1, execution_interval: 0.1) do |task|
task.timeout_interval = 3
latch.count_down
end

subject.timeout_interval.should == 1
subject.timeout_interval = 2
subject.timeout_interval.should == 2

subject.execute
latch.wait(0.2)

subject.timeout_interval.should == 3
subject.kill
end
end

context 'execution' do

it 'runs the block immediately when the :run_now option is true' do
@expected = false
@subject = TimerTask.run!(execution: 500, now: true){ @expected = true }
sleep(0.1)
@expected.should be_true
latch = CountDownLatch.new(1)
subject = TimerTask.execute(execution: 500, now: true){ latch.count_down }
latch.wait(0.1).should be_true
subject.kill
end

it 'waits for :execution_interval seconds when the :run_now option is false' do
@expected = false
@subject = TimerTask.run!(execution: 0.5, now: false){ @expected = true }
@expected.should be_false
sleep(1)
@expected.should be_true
latch = CountDownLatch.new(1)
subject = TimerTask.execute(execution: 0.1, now: false){ latch.count_down }
latch.count.should eq 1
latch.wait(0.2).should be_true
subject.kill
end

it 'waits for :execution_interval seconds when the :run_now option is not given' do
@expected = false
@subject = TimerTask.run!(execution: 0.5){ @expected = true }
@expected.should be_false
sleep(1)
@expected.should be_true
end

it 'yields to the execution block' do
@expected = false
@subject = TimerTask.run!(execution: 1){ @expected = true }
sleep(2)
@expected.should be_true
latch = CountDownLatch.new(1)
subject = TimerTask.execute(execution: 0.1, now: false){ latch.count_down }
latch.count.should eq 1
latch.wait(0.2).should be_true
subject.kill
end

it 'passes a "self" reference to the block as the sole argument' do
@expected = nil
@subject = TimerTask.new(execution_interval: 1, run_now: true) do |task|
@expected = task
expected = nil
latch = CountDownLatch.new(1)
subject = TimerTask.new(execution_interval: 1, run_now: true) do |task|
expected = task
latch.sount_down
end
@thread = Thread.new { @subject.run }
sleep(0.2)
@expected.should eq @subject
subject.execute
latch.wait(0.2)
expected.should eq subject
subject.kill
end
end

Expand All @@ -225,42 +212,45 @@ def trigger_observable(observable)
attr_reader :time
attr_reader :value
attr_reader :ex
attr_reader :latch
define_method(:initialize){ @latch = CountDownLatch.new(1) }
define_method(:update) do |time, value, ex|
@time = time
@value = value
@ex = ex
@latch.count_down
end
end.new
end

it 'notifies all observers on success' do
task = TimerTask.new(run_now: true){ sleep(0.1); 42 }
task.add_observer(observer)
Thread.new{ task.run }
sleep(1)
subject = TimerTask.new(execution: 0.1){ 42 }
subject.add_observer(observer)
subject.execute
observer.latch.wait(1)
observer.value.should == 42
observer.ex.should be_nil
task.kill
subject.kill
end

it 'notifies all observers on timeout' do
task = TimerTask.new(run_now: true, timeout: 1){ sleep }
task.add_observer(observer)
Thread.new{ task.run }
sleep(2)
subject = TimerTask.new(execution: 0.1, timeout: 0.1){ sleep }
subject.add_observer(observer)
subject.execute
observer.latch.wait(1)
observer.value.should be_nil
observer.ex.should be_a(Concurrent::TimeoutError)
task.kill
subject.kill
end

it 'notifies all observers on error' do
task = TimerTask.new(run_now: true){ sleep(0.1); raise ArgumentError }
task.add_observer(observer)
Thread.new{ task.run }
sleep(1)
subject = TimerTask.new(execution: 0.1){ raise ArgumentError }
subject.add_observer(observer)
subject.execute
observer.latch.wait(1)
observer.value.should be_nil
observer.ex.should be_a(ArgumentError)
task.kill
subject.kill
end
end
end
Expand Down