Back to Repositories

Testing Timer Plugin Helper Implementation in Fluentd

This test suite validates the Timer plugin helper functionality in Fluentd, ensuring proper timer execution, management, and error handling. It verifies core timer operations, multiple timer handling, and one-time execution scenarios.

Test Coverage Overview

The test suite provides comprehensive coverage of the Timer plugin helper functionality:

  • Timer instantiation and initial state verification
  • Configuration validation
  • Timer start/stop operations
  • Multiple concurrent timer execution
  • Exception handling during timer execution
  • One-time timer execution scenarios

Implementation Analysis

The testing approach employs Test::Unit framework with systematic verification of timer behaviors:

  • Uses dummy plugin class for isolated testing
  • Implements sleep-based timing verification
  • Utilizes event loop watchers for execution tracking
  • Employs counter-based validation for execution frequency

Technical Details

Testing infrastructure and setup:

  • Test::Unit as the testing framework
  • Fluent::Plugin::TestBase for base functionality
  • Event loop integration for timer management
  • Custom assertion blocks for timing-sensitive tests
  • Helper methods for plugin lifecycle management

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Proper test isolation and cleanup
  • Comprehensive lifecycle testing
  • Error condition handling verification
  • Timing-sensitive test considerations
  • Clear test case organization and naming

fluent/fluentd

test/plugin_helper/test_timer.rb

            
require_relative '../helper'
require 'fluent/plugin_helper/timer'
require 'fluent/plugin/base'

class TimerTest < Test::Unit::TestCase
  class Dummy < Fluent::Plugin::TestBase
    helpers :timer
  end

  test 'can be instantiated under state that timer is not running' do
    d1 = Dummy.new
    assert d1.respond_to?(:timer_running?)
    assert !d1.timer_running?
  end

  test 'can be configured' do
    d1 = Dummy.new
    assert_nothing_raised do
      d1.configure(config_element())
    end
    assert d1.plugin_id
    assert d1.log
  end

  test 'can start timers by start' do
    d1 = Dummy.new
    d1.configure(config_element())
    assert !d1.timer_running?
    d1.start
    assert d1.timer_running?

    counter = 0
    d1.timer_execute(:test, 1) do
      counter += 1
    end

    sleep 2

    d1.stop
    assert !d1.timer_running?

    assert{ counter >= 1 && counter <= 2 }

    d1.shutdown; d1.close; d1.terminate
  end

  test 'can run many timers' do
    d1 = Dummy.new
    d1.configure(config_element())
    d1.start

    counter1 = 0
    counter2 = 0

    d1.timer_execute(:t1, 0.2) do
      counter1 += 1
    end
    d1.timer_execute(:t2, 0.2) do
      counter2 += 1
    end

    sleep 1
    d1.stop

    assert{ counter1 >= 4 && counter1 <= 5 }
    assert{ counter2 >= 4 && counter2 <= 5 }

    d1.shutdown; d1.close; d1.terminate
  end

  test 'aborts timer which raises exceptions' do
    d1 = Dummy.new
    d1.configure(config_element())
    d1.start

    counter1 = 0
    counter2 = 0

    d1.timer_execute(:t1, 0.2) do
      counter1 += 1
    end
    d1.timer_execute(:t2, 0.2) do
      raise "abort!!!!!!" if counter2 > 1
      counter2 += 1
    end

    sleep 1
    d1.stop

    assert{ counter1 >= 4 && counter1 <= 5 }
    assert{ counter2 == 2 }
    msg = "Unexpected error raised. Stopping the timer. title=:t2"
    assert(d1.log.out.logs.any?{|line| line.include?("[error]:") && line.include?(msg) && line.include?("abort!!!!!!") })
    assert(d1.log.out.logs.any?{|line| line.include?("[error]:") && line.include?("Timer detached. title=:t2") })

    d1.shutdown; d1.close; d1.terminate
  end

  test 'can run at once' do
    d1 = Dummy.new
    d1.configure(config_element())
    assert !d1.timer_running?
    d1.start
    assert d1.timer_running?

    waiting_assertion = true
    waiting_timer = true
    counter = 0
    d1.timer_execute(:test, 1, repeat: false) do
      sleep(0.1) while waiting_assertion
      counter += 1
      waiting_timer = false
    end

    watchers = d1._event_loop.watchers.reject {|w| w.is_a?(Fluent::PluginHelper::EventLoop::DefaultWatcher) }
    assert_equal(1, watchers.size)
    assert(watchers.first.attached?)

    waiting_assertion = false
    sleep(0.1) while waiting_timer

    assert_equal(1, counter)
    waiting(4){ sleep 0.1 while watchers.first.attached? }
    assert_false(watchers.first.attached?)
    watchers = d1._event_loop.watchers.reject {|w| w.is_a?(Fluent::PluginHelper::EventLoop::DefaultWatcher) }
    assert_equal(0, watchers.size)

    d1.shutdown; d1.close; d1.terminate
  end
end