Back to Repositories

Validating Input Plugin Lifecycle Management in Fluentd

This test suite validates the core functionality of Fluentd’s input plugin system, covering lifecycle management, plugin configuration, and event routing capabilities. The tests ensure reliable input plugin behavior and proper integration with Fluentd’s architecture.

Test Coverage Overview

The test suite provides comprehensive coverage of input plugin functionality in Fluentd.

Key areas tested include:
  • Plugin lifecycle states (configuration, start/stop, shutdown)
  • Plugin ID generation and configuration
  • Logger implementation and log level handling
  • Event routing and emission capabilities
  • Multi-worker compatibility checks

Implementation Analysis

The testing approach utilizes Test::Unit with FlexMock for robust unit testing.

Notable patterns include:
  • Systematic lifecycle state verification
  • Mock objects for router testing
  • Helper method integration testing
  • Structured setup and teardown

Technical Details

Testing infrastructure includes:
  • Test::Unit as the primary testing framework
  • FlexMock for mocking and stubbing
  • Fluent::Test setup utilities
  • Custom DummyPlugin implementation
  • Configuration element factories

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices with thorough state verification and edge case handling.

Notable practices include:
  • Isolated test cases with clear assertions
  • Comprehensive lifecycle testing
  • Proper mock object usage
  • Explicit error case handling
  • Clean test organization and readability

fluent/fluentd

test/plugin/test_input.rb

            
require_relative '../helper'
require 'fluent/plugin/input'
require 'flexmock/test_unit'

module FluentPluginInputTest
  class DummyPlugin < Fluent::Plugin::Input
  end
end

class InputTest < Test::Unit::TestCase
  setup do
    Fluent::Test.setup
    @p = FluentPluginInputTest::DummyPlugin.new
  end

  test 'has healthy lifecycle' do
    assert [email protected]?
    @p.configure(config_element())
    assert @p.configured?

    assert [email protected]?
    @p.start
    assert @p.start

    assert [email protected]?
    @p.stop
    assert @p.stopped?

    assert [email protected]_shutdown?
    @p.before_shutdown
    assert @p.before_shutdown?

    assert [email protected]?
    @p.shutdown
    assert @p.shutdown?

    assert [email protected]_shutdown?
    @p.after_shutdown
    assert @p.after_shutdown?

    assert [email protected]?
    @p.close
    assert @p.closed?

    assert [email protected]?
    @p.terminate
    assert @p.terminated?
  end

  test 'has plugin_id automatically generated' do
    assert @p.respond_to?(:plugin_id_configured?)
    assert @p.respond_to?(:plugin_id)

    @p.configure(config_element())

    assert [email protected]_id_configured?
    assert @p.plugin_id
    assert{ @p.plugin_id != 'mytest' }
  end

  test 'has plugin_id manually configured' do
    @p.configure(config_element('ROOT', '', {'@id' => 'mytest'}))
    assert @p.plugin_id_configured?
    assert_equal 'mytest', @p.plugin_id
  end

  test 'has plugin logger' do
    assert @p.respond_to?(:log)
    assert @p.log

    # default logger
    original_logger = @p.log

    @p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))

    assert(@p.log.object_id != original_logger.object_id)
    assert_equal Fluent::Log::LEVEL_DEBUG, @p.log.level
  end

  test 'can load plugin helpers' do
    assert_nothing_raised do
      class FluentPluginInputTest::DummyPlugin2 < Fluent::Plugin::Input
        helpers :storage
      end
    end
  end

  test 'can use metrics plugins and fallback methods' do
    @p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))

    %w[emit_size_metrics emit_records_metrics].each do |metric_name|
      assert_true @p.instance_variable_get(:"@#{metric_name}").is_a?(Fluent::Plugin::Metrics)
    end

    assert_equal 0, @p.emit_size
    assert_equal 0, @p.emit_records
  end

  test 'are not available with multi workers configuration in default' do
    assert_false @p.multi_workers_ready?
  end

  test 'has router and can emit into it' do
    assert @p.has_router?

    @p.configure(config_element())
    assert @p.router

    DummyRouter = Struct.new(:emits) do
      def emit(tag, es)
        self.emits << [tag, es]
      end
    end
    @p.router = DummyRouter.new([])
    @p.router.emit('mytag', [])
    @p.router.emit('mytag.testing', ['it is not es, but no problem for tests'])

    assert_equal ['mytag', []], @p.router.emits[0]
    assert_equal ['mytag.testing', ['it is not es, but no problem for tests']], @p.router.emits[1]
  end

  test 'has router for specified label if configured' do
    @p.configure(config_element())
    original_router = @p.router

    router_mock = flexmock('mytest')
    router_mock.should_receive(:emit).once.with('mytag.testing', ['for mock'])
    label_mock = flexmock('mylabel')
    label_mock.should_receive(:event_router).once.and_return(router_mock)
    Fluent::Engine.root_agent.labels['@mytest'] = label_mock

    @p.configure(config_element('ROOT', '', {'@label' => '@mytest'}))
    assert{ @p.router.object_id != original_router.object_id }

    @p.router.emit('mytag.testing', ['for mock'])
  end
end