Back to Repositories

Validating Configuration DSL Parsing in Fluentd

This test suite validates the Configuration DSL (Domain Specific Language) functionality in Fluentd, focusing on parsing and processing configuration files using Ruby-based DSL syntax. It ensures proper handling of source and match directives, server configurations, and Ruby integration within the DSL.

Test Coverage Overview

The test suite provides comprehensive coverage of Fluentd’s Configuration DSL parsing capabilities.

Key areas tested include:
  • Basic DSL configuration parsing for source and match directives
  • Server configuration block handling
  • Ruby expression evaluation within DSL
  • Error handling for invalid configurations
  • Integration with Ruby system commands and ERB templating

Implementation Analysis

The testing approach uses Ruby’s Test::Unit framework to validate DSL parsing functionality. Tests are structured to verify both successful parsing scenarios and error conditions.

Key patterns include:
  • DSL string parsing validation
  • Configuration hierarchy verification
  • Element attribute checking
  • Ruby code block integration testing

Technical Details

Testing infrastructure includes:
  • Test::Unit as the testing framework
  • Fluent::Config::DSL::Parser for configuration parsing
  • Ruby system command integration
  • ERB templating for dynamic configuration
  • Mock configuration strings for various test scenarios

Best Practices Demonstrated

The test suite exemplifies several testing best practices in configuration parsing validation.

Notable practices include:
  • Isolation of test cases for different configuration scenarios
  • Comprehensive error case coverage
  • Validation of nested configuration elements
  • Integration testing with Ruby features
  • Clear test case organization and naming

fluent/fluentd

test/test_configdsl.rb

            
require_relative 'helper'
require 'fluent/config/dsl'
require 'fluent/test'

class ConfigDSLTest < Test::Unit::TestCase
  # TEST_CONFIG1 = %[
  #   <source>
  #     type forward
  #     port 24224
  #   </source>
  #   <match test.**>
  #     type forward
  #     flush_interval 1s
  #     <server>
  #       host host0.example.com
  #       port 24224
  #     </server>
  #     <server>
  #       host host1.example.com
  #       port 24224
  #     </server>
  #   </match>
  # ]
  TEST_DSL_CONFIG1 = %q[
source {
  type "forward"
  port 24224
}
match('test.**') {
  type "forward"
  flush_interval "1s"
  (0..1).each do |i|
    server {
      host "host#{i}.example.com"
      port 24224
    }
  end
}
]

  TEST_DSL_CONFIG2 = %q[
v = [0, 1, 2]
]

  TEST_DSL_CONFIG3 = %q[
match
]

  TEST_DSL_CONFIG4 = %q[
match('aa', 'bb'){
  type :null
}
]

  TEST_DSL_CONFIG5 = %q[
match('aa')
]

  def test_parse
    root = Fluent::Config::DSL::Parser.parse(TEST_DSL_CONFIG1)

    assert_equal 0, root.keys.size
    assert_equal 2, root.elements.size

    e0 = root.elements[0]
    assert_equal 'source', e0.name
    assert_equal '', e0.arg
    assert_equal 'forward', e0['@type']
    assert_equal '24224', e0['port']

    e1 = root.elements[1]
    assert_equal 'match', e1.name
    assert_equal 'test.**', e1.arg
    assert_equal 'forward', e1['@type']
    assert_equal '1s', e1['flush_interval']
    assert_equal 2, e1.elements.size
    e1s0 = e1.elements[0]
    assert_equal 'server', e1s0.name
    assert_equal 'host0.example.com', e1s0['host']
    assert_equal '24224', e1s0['port']
    e1s1 = e1.elements[1]
    assert_equal 'server', e1s1.name
    assert_equal 'host1.example.com', e1s1['host']
    assert_equal '24224', e1s1['port']
  end

  def test_parse2
    root = Fluent::Config::DSL::Parser.parse(TEST_DSL_CONFIG2)

    assert_equal 0, root.keys.size
    assert_equal 0, root.elements.size
  end

  def test_config_error
    assert_raise(ArgumentError) {
      Fluent::Config::DSL::Parser.parse(TEST_DSL_CONFIG3)
    }

    assert_raise(ArgumentError) {
      Fluent::Config::DSL::Parser.parse(TEST_DSL_CONFIG4)
    }

    assert_raise(ArgumentError) {
      Fluent::Config::DSL::Parser.parse(TEST_DSL_CONFIG5)
    }
  end

  def test_with_ruby_keyword
    uname_string = `uname -a`
    root1 = Fluent::Config::DSL::Parser.parse(<<DSL)
uname_str = ruby.open('|uname -a'){|out| out.read}
source {
  uname uname_str
}
DSL
    source1 = root1.elements.first
    assert_equal 'source', source1.name
    assert_equal 1, source1.keys.size
    assert_equal uname_string, source1['uname']

    root2 = Fluent::Config::DSL::Parser.parse(<<DSL)
ruby_version = ruby {
  require 'erb'
  ERB.new('<%= RUBY_VERSION %> from erb').result
}
source {
  version ruby_version
}
DSL
    source2 = root2.elements.first
    assert_equal 'source', source2.name
    assert_equal 1, source2.keys.size
    assert_equal "#{RUBY_VERSION} from erb", source2['version']

    # Parser#parse raises NoMethodError when configuration dsl elements are written in ruby block
    conf3 = <<DSL
ruby {
  source {
    type "tail"
  }
}
source {
  uname uname_str
}
DSL
    assert_raise (NoMethodError) { Fluent::Config::DSL::Parser.parse(conf3) }
  end
end