# encoding: utf-8 require 'fluent_plugins_spec_helper' require 'idle_event_detector' require 'timecop' module Fluent describe IdleEventDetector do let(:time) { Time.local(2014, 12, 1, 5, 0, 0) } let(:initial_interval) { 0.1 } let(:continuous_interval) { 0.18 } let(:check_interval) { 0.1 } let(:idle_timeout) { 1.0 } subject { described_class.new(initial_interval, continuous_interval, check_interval, idle_timeout) } describe '#start' do context 'without block' do it 'errors out' do expect{ subject.start }.to raise_error end end context 'no event within initial_interval' do before do Timecop.freeze(time) @callback_called = false end let(:callback) { Proc.new { @callback_called = true } } it 'fires :event_not_coming callback' do subject.start(&callback) Timecop.freeze(time + initial_interval + continuous_interval * 0.2) sleep check_interval * 1.1 expect(@callback_called).to eq true end after do Timecop.return @callback_called = false end end context 'no event during test which completes before initial_interval' do before do Timecop.freeze(time) @callback_called = false end let(:callback) { Proc.new { @callback_called = true } } it 'does not fire :event_not_coming callback' do subject.start(&callback) Timecop.freeze(time + initial_interval * 0.2) sleep check_interval * 1.1 expect(@callback_called).to eq false subject.stop end after do Timecop.return @callback_called = false end end context 'an event during test which completes after initial_interval' do before do Timecop.freeze(time) @callback_called = false end let(:callback) { Proc.new { @callback_called = true } } it 'does not fire :event_not_coming callback' do subject.start(&callback) Timecop.freeze(time + initial_interval * 0.2) sleep check_interval * 1.1 subject.notify Timecop.freeze(time + initial_interval + initial_interval * 0.1) sleep check_interval * 1.1 expect(@callback_called).to eq false subject.stop end after do Timecop.return @callback_called = false end end context 'no event during test which completes after initial_interval + continuous_interval' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false end it 'fires an :event_not_coming callback and an :event_still_not_coming' do subject.start(&callback) expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false Timecop.freeze(time + initial_interval + continuous_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false Timecop.freeze(time + initial_interval + continuous_interval + check_interval) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq true subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end context 'an event during test after initial_interval' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true when :event_arrived_finally @arrived_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end it 'fires an :event_not_coming callback and an :event_arrived_finaly callback' do subject.start(&callback) expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false Timecop.freeze(time + initial_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false Timecop.freeze(time + initial_interval + initial_interval * 0.8) subject.notify sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq true subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end context 'an event during test after initial_interval + continuous_interval' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true when :event_arrived_finally @arrived_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end it 'fires all 3 callbacks' do subject.start(&callback) expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false Timecop.freeze(time + initial_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false @initial_callback_called = false Timecop.freeze(time + initial_interval + continuous_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq true expect(@arrived_callback_called).to eq false @continuous_callback_called = false Timecop.freeze(time + initial_interval + continuous_interval + initial_interval * 0.4) subject.notify sleep check_interval * 1.1 expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq true subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end context 'no event during test which is long enough for two event_still_not_coming callbacks' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true when :event_arrived_finally @arrived_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end it 'fires two event_still_not_coming callbacks after an event_not_coming callback' do subject.start(&callback) expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false Timecop.freeze(time + initial_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false @initial_callback_called = false Timecop.freeze(time + initial_interval + continuous_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq true expect(@arrived_callback_called).to eq false @continuous_callback_called = false Timecop.freeze(time + initial_interval + continuous_interval + continuous_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq true expect(@arrived_callback_called).to eq false subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end context 'an event after initial_interval and no event for another initial_interval time' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true when :event_arrived_finally @arrived_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end it 'fires two event_still_not_coming callbacks after an event_not_coming callback' do subject.start(&callback) expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false Timecop.freeze(time + initial_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false @initial_callback_called = false Timecop.freeze(time + initial_interval + initial_interval * 0.4) subject.notify sleep check_interval * 1.1 expect(@initial_callback_called).to eq false expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq true @arrived_callback_called = false Timecop.freeze(time + initial_interval + initial_interval * 0.4 + initial_interval + initial_interval * 0.2) sleep check_interval * 1.1 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq false expect(@arrived_callback_called).to eq false subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end context 'no event during test which is long enough for idle timeout callbacks' do let(:callback) { Proc.new do |reason| case reason when :event_not_coming @initial_callback_called = true when :event_still_not_coming @continuous_callback_called = true when :event_arrived_finally @arrived_callback_called = true when @timeout_callback_called = true end end } before do Timecop.freeze(time) @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false @timeout_callback_called = false end it 'fires idle_timeout callbacks after an event_not_coming callback' do subject.start(&callback) Timecop.freeze(time + initial_interval * 1.1) sleep check_interval * 1.2 Timecop.freeze(time + initial_interval + continuous_interval * 1.1) sleep check_interval * 1.2 Timecop.freeze(time + initial_interval + idle_timeout * 1.1) sleep check_interval * 1.2 expect(@initial_callback_called).to eq true expect(@continuous_callback_called).to eq true expect(@arrived_callback_called).to eq false expect(@timeout_callback_called).to eq true subject.stop end after do Timecop.return @initial_callback_called = false @continuous_callback_called = false @arrived_callback_called = false end end end describe '#stop' do context 'stop without corresponding start' do it 'behaves nicely' do subject.stop end end context 'stop with corresponding start' do it 'kills the thread' do subject.start {} thread = subject.instance_variable_get(:@thread) expect(thread.alive?).to eq true subject.stop sleep 0.05 expect(thread.alive?).to eq false end end end end end