Back to Repositories

Testing Daemon Process Management Implementation in Fluentd

This test suite validates the daemonization functionality in Fluentd, focusing on process management and PID file handling. It ensures reliable daemon process creation and proper error handling for various file system scenarios.

Test Coverage Overview

The test suite provides comprehensive coverage of Fluentd’s daemonization capabilities.

Key areas tested include:
  • PID file creation and management
  • Process daemonization on supported platforms
  • Fallback behavior for platforms without fork support
  • Various file system permission scenarios

Implementation Analysis

The testing approach uses Test::Unit framework with extensive use of mocking to isolate daemon process behavior. The implementation validates both successful daemon creation and error conditions through systematic test cases that cover file system permissions and process management.

Technical patterns include:
  • Process manipulation using mock objects
  • File system operation verification
  • Platform-specific behavior handling

Technical Details

Testing tools and configuration:
  • Test::Unit as the primary testing framework
  • Process module for daemon management
  • FileUtils for temporary directory handling
  • Mock objects for process isolation
  • Temporary directory setup and teardown for each test

Best Practices Demonstrated

The test suite exemplifies strong testing practices through systematic validation of edge cases and proper test isolation.

Notable practices include:
  • Proper test setup and teardown
  • Comprehensive error condition testing
  • Platform-specific test considerations
  • Isolated test environments
  • Mock object usage for system calls

fluent/fluentd

test/test_daemonizer.rb

            
require_relative 'helper'
require 'fluent/daemonizer'

class DaemonizerTest < ::Test::Unit::TestCase
  TMP_DIR = File.join(File.dirname(__FILE__), 'tmp', 'daemonizer')

  setup do
    FileUtils.mkdir_p(TMP_DIR)
  end

  teardown do
    FileUtils.rm_rf(TMP_DIR) rescue nil
  end

  test 'makes pid file' do
    pid_path = File.join(TMP_DIR, 'file.pid')

    mock(Process).daemon(anything, anything).once
    r = Fluent::Daemonizer.daemonize(pid_path) { 'ret' }
    assert_equal 'ret', r
    assert File.exist?(pid_path)
    assert Process.pid.to_s, File.read(pid_path).to_s
  end

  test 'in platforms which do not support fork' do
    pid_path = File.join(TMP_DIR, 'file.pid')

    mock(Process).daemon(anything, anything) { raise NotImplementedError }
    args = ['-c', 'test.conf']
    mock(Process).spawn(anything, *args) { Process.pid }

    Fluent::Daemonizer.daemonize(pid_path, args) { 'ret' }
    assert File.exist?(pid_path)
    assert Process.pid.to_s, File.read(pid_path).to_s
  end

  sub_test_case 'when pid file already exists' do
    test 'raise an error when process is running' do
      omit 'chmod of file does not affetct root user' if Process.uid.zero?
      pid_path = File.join(TMP_DIR, 'file.pid')
      File.write(pid_path, '1')

      mock(Process).daemon(anything, anything).never
      mock(Process).kill(0, 1).once

      assert_raise(Fluent::ConfigError.new('pid(1) is running')) do
        Fluent::Daemonizer.daemonize(pid_path) { 'ret' }
      end
    end

    test 'raise an error when file is not redable' do
      omit 'chmod of file does not affetct root user' if Process.uid.zero?
      not_readable_path = File.join(TMP_DIR, 'not_readable.pid')

      File.write(not_readable_path, '1')
      FileUtils.chmod(0333, not_readable_path)

      mock(Process).daemon(anything, anything).never
      assert_raise(Fluent::ConfigError.new("Cannot access pid file: #{File.absolute_path(not_readable_path)}")) do
        Fluent::Daemonizer.daemonize(not_readable_path) { 'ret' }
      end
    end

    test 'raise an error when file is not writable' do
      omit 'chmod of file does not affetct root user' if Process.uid.zero?
      not_writable_path = File.join(TMP_DIR, 'not_writable.pid')

      File.write(not_writable_path, '1')
      FileUtils.chmod(0555, not_writable_path)

      mock(Process).daemon(anything, anything).never
      assert_raise(Fluent::ConfigError.new("Cannot access pid file: #{File.absolute_path(not_writable_path)}")) do
        Fluent::Daemonizer.daemonize(not_writable_path) { 'ret' }
      end
    end

    test 'raise an error when directory is not writable' do
      omit 'chmod of file does not affetct root user' if Process.uid.zero?
      not_writable_dir = File.join(TMP_DIR, 'not_writable')
      pid_path = File.join(not_writable_dir, 'file.pid')

      FileUtils.mkdir_p(not_writable_dir)
      FileUtils.chmod(0555, not_writable_dir)

      mock(Process).daemon(anything, anything).never
      assert_raise(Fluent::ConfigError.new("Cannot access directory for pid file: #{File.absolute_path(not_writable_dir)}")) do
        Fluent::Daemonizer.daemonize(pid_path) { 'ret' }
      end
    end
  end
end