Back to Repositories

Testing Log Event Router Implementation in Fluentd

This test suite validates the functionality of Fluentd’s log event routing system, focusing on event handling, router configuration, and graceful shutdown behaviors. The tests ensure proper event emission and routing across different configuration scenarios.

Test Coverage Overview

The test suite provides comprehensive coverage of Fluentd’s log event routing system, examining both null router and standard router implementations.

  • Tests null router behavior and method responses
  • Validates router building based on different configurations
  • Verifies event handling during graceful and immediate stops
  • Covers configuration parsing and root agent integration

Implementation Analysis

The testing approach utilizes Ruby’s Test::Unit framework with sub_test_case blocks for logical test organization. Mock objects and stubs are employed to isolate router behavior and verify event emission patterns.

  • Uses modular test case structure
  • Implements stub methods for event routing verification
  • Tests configuration parsing with different syntax versions

Technical Details

  • Test::Unit as primary testing framework
  • Fluent::Config for configuration parsing
  • RootAgent and SystemConfig integration
  • Mock objects for event router isolation
  • Time-based event emission testing

Best Practices Demonstrated

The test suite exemplifies strong testing practices through clear separation of concerns and comprehensive edge case coverage.

  • Isolated test cases with clear purpose
  • Thorough configuration testing
  • Proper cleanup and resource management
  • Clear test naming conventions
  • Effective use of assertions and validations

fluent/fluentd

test/test_fluent_log_event_router.rb

            
require_relative 'helper'
require 'fluent/fluent_log_event_router'
require 'fluent/root_agent'
require 'fluent/system_config'

class FluentLogEventRouterTest < ::Test::Unit::TestCase
  # @param config [String]
  def build_config(config)
    Fluent::Config.parse(config, 'fluent_log_event', '', syntax: :v1)
  end

  sub_test_case 'NullFluentLogEventRouter does nothing' do
    test 'emittable? returns false but others does nothing' do
      null_event_router = Fluent::NullFluentLogEventRouter.new
      null_event_router.start
      null_event_router.stop
      null_event_router.graceful_stop
      null_event_router.emit_event(nil)
      assert_false null_event_router.emittable?
    end
  end

  sub_test_case '#build' do
    test 'NullFluentLogEventRouter if root_agent have not internal logger' do
      root_agent = Fluent::RootAgent.new(log: $log, system_config: Fluent::SystemConfig.new)
      root_agent.configure(build_config(''))

      d = Fluent::FluentLogEventRouter.build(root_agent)
      assert_equal Fluent::NullFluentLogEventRouter, d.class
    end

    test 'FluentLogEventRouter if <match fluent.*> exists in config' do
      root_agent = Fluent::RootAgent.new(log: $log, system_config: Fluent::SystemConfig.new)
      root_agent.configure(build_config(<<-CONFIG))
        <match fluent.*>
          @type null
        </match>
      CONFIG

      d = Fluent::FluentLogEventRouter.build(root_agent)
      assert_equal Fluent::FluentLogEventRouter, d.class
    end

    test 'FluentLogEventRouter if <label @FLUENT_LOG> exists in config' do
      root_agent = Fluent::RootAgent.new(log: $log, system_config: Fluent::SystemConfig.new)
      root_agent.configure(build_config(<<-CONFIG))
       <label @FLUENT_LOG>
         <match *>
           @type null
         </match>
        </label>
      CONFIG

      d = Fluent::FluentLogEventRouter.build(root_agent)
      assert_equal Fluent::FluentLogEventRouter, d.class
    end
  end

  test 'when calling graceful_stop, it flushes all events' do
    event_router = []
    stub(event_router).emit do |tag, time, record|
      event_router.push([tag, time, record])
    end

    d = Fluent::FluentLogEventRouter.new(event_router)

    t = Time.now
    msg = ['tag', t, { 'key' => 'value' }]
    d.emit_event(msg)
    d.graceful_stop
    d.emit_event(msg)
    d.start

    d.graceful_stop # to call join
    assert_equal 2, event_router.size
    assert_equal msg, event_router[0]
    assert_equal msg, event_router[1]
  end

  test 'when calling stop, it ignores existing events' do
    event_router = []
    stub(event_router).emit do |tag, time, record|
      event_router.push([tag, time, record])
    end

    d = Fluent::FluentLogEventRouter.new(event_router)

    t = Time.now
    msg = ['tag', t, { 'key' => 'value' }]
    d.emit_event(msg)
    d.stop
    d.emit_event(msg)
    d.start

    d.stop # to call join
    assert_equal 1, event_router.size
    assert_equal msg, event_router[0]
  end
end