fluentd 1.15.3 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linux-test.yaml +2 -2
  3. data/.github/workflows/macos-test.yaml +2 -2
  4. data/.github/workflows/windows-test.yaml +2 -2
  5. data/CHANGELOG.md +77 -0
  6. data/MAINTAINERS.md +2 -0
  7. data/README.md +0 -1
  8. data/fluentd.gemspec +2 -2
  9. data/lib/fluent/command/fluentd.rb +55 -53
  10. data/lib/fluent/daemon.rb +2 -4
  11. data/lib/fluent/log/console_adapter.rb +66 -0
  12. data/lib/fluent/log.rb +35 -5
  13. data/lib/fluent/plugin/base.rb +5 -7
  14. data/lib/fluent/plugin/buf_file.rb +32 -3
  15. data/lib/fluent/plugin/buf_file_single.rb +32 -3
  16. data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
  17. data/lib/fluent/plugin/buffer.rb +21 -0
  18. data/lib/fluent/plugin/in_tcp.rb +4 -2
  19. data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
  20. data/lib/fluent/plugin/out_forward.rb +2 -2
  21. data/lib/fluent/plugin/out_secondary_file.rb +39 -22
  22. data/lib/fluent/plugin/output.rb +49 -12
  23. data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
  24. data/lib/fluent/supervisor.rb +157 -251
  25. data/lib/fluent/test/driver/base.rb +11 -5
  26. data/lib/fluent/test/driver/filter.rb +4 -0
  27. data/lib/fluent/test/startup_shutdown.rb +6 -8
  28. data/lib/fluent/version.rb +1 -1
  29. data/test/command/test_ctl.rb +1 -1
  30. data/test/command/test_fluentd.rb +137 -6
  31. data/test/command/test_plugin_config_formatter.rb +0 -1
  32. data/test/compat/test_parser.rb +5 -5
  33. data/test/config/test_system_config.rb +0 -8
  34. data/test/log/test_console_adapter.rb +110 -0
  35. data/test/plugin/out_forward/test_ack_handler.rb +39 -0
  36. data/test/plugin/test_base.rb +98 -0
  37. data/test/plugin/test_buf_file.rb +62 -23
  38. data/test/plugin/test_buf_file_single.rb +65 -0
  39. data/test/plugin/test_in_http.rb +2 -3
  40. data/test/plugin/test_in_monitor_agent.rb +2 -3
  41. data/test/plugin/test_in_tcp.rb +15 -0
  42. data/test/plugin/test_out_forward.rb +14 -18
  43. data/test/plugin/test_out_http.rb +1 -0
  44. data/test/plugin/test_output.rb +269 -0
  45. data/test/plugin/test_parser_regexp.rb +1 -6
  46. data/test/plugin_helper/test_http_server_helper.rb +1 -1
  47. data/test/plugin_helper/test_server.rb +10 -5
  48. data/test/test_config.rb +0 -21
  49. data/test/test_formatter.rb +23 -20
  50. data/test/test_log.rb +71 -36
  51. data/test/test_supervisor.rb +277 -282
  52. metadata +12 -19
  53. data/.drone.yml +0 -35
  54. data/.gitlab-ci.yml +0 -103
  55. data/test/test_logger_initializer.rb +0 -46
@@ -21,9 +21,8 @@ module Fluent
21
21
  module Test
22
22
  module StartupShutdown
23
23
  def startup
24
- socket_manager_path = ServerEngine::SocketManager::Server.generate_path
25
- @server = ServerEngine::SocketManager::Server.open(socket_manager_path)
26
- ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
24
+ @server = ServerEngine::SocketManager::Server.open
25
+ ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = @server.path.to_s
27
26
  end
28
27
 
29
28
  def shutdown
@@ -31,15 +30,14 @@ module Fluent
31
30
  end
32
31
 
33
32
  def self.setup
34
- @socket_manager_path = ServerEngine::SocketManager::Server.generate_path
35
- @server = ServerEngine::SocketManager::Server.open(@socket_manager_path)
36
- ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = @socket_manager_path.to_s
33
+ @server = ServerEngine::SocketManager::Server.open
34
+ ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = @server.path.to_s
37
35
  end
38
36
 
39
37
  def self.teardown
40
38
  @server.close
41
- # on Windows, socket_manager_path is a TCP port number
42
- FileUtils.rm_f @socket_manager_path unless Fluent.windows?
39
+ # on Windows, the path is a TCP port number
40
+ FileUtils.rm_f @server.path unless Fluent.windows?
43
41
  end
44
42
  end
45
43
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.15.3'
19
+ VERSION = '1.16.0'
20
20
 
21
21
  end
@@ -35,7 +35,7 @@ class TestFluentdCtl < ::Test::Unit::TestCase
35
35
  Signal.trap(signal) do
36
36
  got_signal = true
37
37
  end
38
- Fluent::Ctl.new([command, "#{$$}"]).call
38
+ Fluent::Ctl.new([command, Process.pid.to_s]).call
39
39
  assert_true(got_signal)
40
40
  end
41
41
  end
@@ -71,6 +71,20 @@ class TestFluentdCommand < ::Test::Unit::TestCase
71
71
  end
72
72
  end
73
73
 
74
+ def process_kill(pid)
75
+ if Fluent.windows?
76
+ Process.kill(:KILL, pid) rescue nil
77
+ return
78
+ end
79
+
80
+ begin
81
+ Process.kill(:TERM, pid) rescue nil
82
+ Timeout.timeout(10){ sleep 0.1 while process_exist?(pid) }
83
+ rescue Timeout::Error
84
+ Process.kill(:KILL, pid) rescue nil
85
+ end
86
+ end
87
+
74
88
  def execute_command(cmdline, chdir=@tmp_dir, env = {})
75
89
  null_stream = Fluent::FileWrapper.open(File::NULL, 'w')
76
90
  gemfile_path = File.expand_path(File.dirname(__FILE__) + "../../../Gemfile")
@@ -85,12 +99,12 @@ class TestFluentdCommand < ::Test::Unit::TestCase
85
99
  yield pid, io
86
100
  # p(here: "execute command", pid: pid, worker_pids: @worker_pids)
87
101
  ensure
88
- Process.kill(:KILL, pid) rescue nil
102
+ process_kill(pid)
89
103
  if @supervisor_pid
90
- Process.kill(:KILL, @supervisor_pid) rescue nil
104
+ process_kill(@supervisor_pid)
91
105
  end
92
106
  @worker_pids.each do |cpid|
93
- Process.kill(:KILL, cpid) rescue nil
107
+ process_kill(cpid)
94
108
  end
95
109
  # p(here: "execute command", pid: pid, exist: process_exist?(pid), worker_pids: @worker_pids, exists: @worker_pids.map{|i| process_exist?(i) })
96
110
  Timeout.timeout(10){ sleep 0.1 while process_exist?(pid) }
@@ -112,7 +126,9 @@ class TestFluentdCommand < ::Test::Unit::TestCase
112
126
  end
113
127
  end
114
128
 
115
- def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 10, env: {})
129
+ # ATTENTION: This stops taking logs when all `pattern_list` match or timeout,
130
+ # so `patterns_not_match` can test only logs up to that point.
131
+ def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 20, env: {})
116
132
  matched = false
117
133
  matched_wrongly = false
118
134
  assert_error_msg = ""
@@ -121,7 +137,7 @@ class TestFluentdCommand < ::Test::Unit::TestCase
121
137
  execute_command(cmdline, @tmp_dir, env) do |pid, stdout|
122
138
  begin
123
139
  waiting(timeout) do
124
- while process_exist?(pid) && !matched
140
+ while process_exist?(pid)
125
141
  readables, _, _ = IO.select([stdout], nil, nil, 1)
126
142
  next unless readables
127
143
  break if readables.first.eof?
@@ -133,6 +149,18 @@ class TestFluentdCommand < ::Test::Unit::TestCase
133
149
  if pattern_list.all?{|ptn| lines.any?{|line| ptn.is_a?(Regexp) ? ptn.match(line) : line.include?(ptn) } }
134
150
  matched = true
135
151
  end
152
+
153
+ if Fluent.windows?
154
+ # https://github.com/fluent/fluentd/issues/4095
155
+ # On Windows, the initial process is different from the supervisor process,
156
+ # so we need to wait until `SUPERVISOR_PID_PATTERN` appears in the logs to get the pid.
157
+ # (Worker processes will be killed by the supervisor process, so we don't need it-)
158
+ break if matched && SUPERVISOR_PID_PATTERN =~ stdio_buf
159
+ else
160
+ # On Non-Windows, the initial process is the supervisor process,
161
+ # so we don't need to wait `SUPERVISOR_PID_PATTERN`.
162
+ break if matched
163
+ end
136
164
  end
137
165
  end
138
166
  ensure
@@ -146,6 +174,10 @@ class TestFluentdCommand < ::Test::Unit::TestCase
146
174
  end
147
175
  rescue Timeout::Error
148
176
  assert_error_msg = "execution timeout"
177
+ # https://github.com/fluent/fluentd/issues/4095
178
+ # On Windows, timeout without `@supervisor_pid` means that the test is invalid,
179
+ # since the supervisor process will survive without being killed correctly.
180
+ flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
149
181
  rescue => e
150
182
  assert_error_msg = "unexpected error in launching fluentd: #{e.inspect}"
151
183
  else
@@ -177,7 +209,7 @@ class TestFluentdCommand < ::Test::Unit::TestCase
177
209
  assert matched && !matched_wrongly, assert_error_msg
178
210
  end
179
211
 
180
- def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout: 10)
212
+ def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout: 20)
181
213
  # empty_list.all?{ ... } is always true
182
214
  matched = false
183
215
  running = false
@@ -213,6 +245,10 @@ class TestFluentdCommand < ::Test::Unit::TestCase
213
245
  end
214
246
  rescue Timeout::Error
215
247
  assert_error_msg = "execution timeout with command out:\n" + stdio_buf
248
+ # https://github.com/fluent/fluentd/issues/4095
249
+ # On Windows, timeout without `@supervisor_pid` means that the test is invalid,
250
+ # since the supervisor process will survive without being killed correctly.
251
+ flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
216
252
  rescue => e
217
253
  assert_error_msg = "unexpected error in launching fluentd: #{e.inspect}\n" + stdio_buf
218
254
  assert false, assert_error_msg
@@ -1151,4 +1187,99 @@ CONF
1151
1187
  "shared socket for multiple workers is disabled",)
1152
1188
  end
1153
1189
  end
1190
+
1191
+ # TODO: `patterns_not_match` can test only logs up to `pattern_list`,
1192
+ # so we need to fix some meaningless `patterns_not_match` conditions.
1193
+ sub_test_case 'log_level by command line option' do
1194
+ test 'info' do
1195
+ conf = ""
1196
+ conf_path = create_conf_file('empty.conf', conf)
1197
+ assert File.exist?(conf_path)
1198
+ assert_log_matches(create_cmdline(conf_path),
1199
+ "[info]",
1200
+ patterns_not_match: ["[debug]"])
1201
+ end
1202
+
1203
+ test 'debug' do
1204
+ conf = ""
1205
+ conf_path = create_conf_file('empty.conf', conf)
1206
+ assert File.exist?(conf_path)
1207
+ assert_log_matches(create_cmdline(conf_path, "-v"),
1208
+ "[debug]",
1209
+ patterns_not_match: ["[trace]"])
1210
+ end
1211
+
1212
+ data("Trace" => "-vv")
1213
+ data("Invalid low level should be treated as Trace level": "-vvv")
1214
+ test 'trace' do |option|
1215
+ conf = <<CONF
1216
+ <source>
1217
+ @type sample
1218
+ tag test
1219
+ </source>
1220
+ CONF
1221
+ conf_path = create_conf_file('sample.conf', conf)
1222
+ assert File.exist?(conf_path)
1223
+ assert_log_matches(create_cmdline(conf_path, option),
1224
+ "[trace]",)
1225
+ end
1226
+
1227
+ test 'warn' do
1228
+ omit "Can't run on Windows since there is no way to take pid of the supervisor." if Fluent.windows?
1229
+ conf = <<CONF
1230
+ <source>
1231
+ @type sample
1232
+ tag test
1233
+ </source>
1234
+ CONF
1235
+ conf_path = create_conf_file('sample.conf', conf)
1236
+ assert File.exist?(conf_path)
1237
+ assert_log_matches(create_cmdline(conf_path, "-q"),
1238
+ "[warn]",
1239
+ patterns_not_match: ["[info]"])
1240
+ end
1241
+
1242
+ data("Error" => "-qq")
1243
+ data("Fatal should be treated as Error level" => "-qqq")
1244
+ data("Invalid high level should be treated as Error level": "-qqqq")
1245
+ test 'error' do |option|
1246
+ # This test can run on Windows correctly,
1247
+ # since the process will stop automatically with an error.
1248
+ conf = <<CONF
1249
+ <source>
1250
+ @type plugin_not_found
1251
+ tag test
1252
+ </source>
1253
+ CONF
1254
+ conf_path = create_conf_file('plugin_not_found.conf', conf)
1255
+ assert File.exist?(conf_path)
1256
+ assert_log_matches(create_cmdline(conf_path, option),
1257
+ "[error]",
1258
+ patterns_not_match: ["[warn]"])
1259
+ end
1260
+
1261
+ test 'system config one should not be overwritten when cmd line one is not specified' do
1262
+ conf = <<CONF
1263
+ <system>
1264
+ log_level debug
1265
+ </system>
1266
+ CONF
1267
+ conf_path = create_conf_file('debug.conf', conf)
1268
+ assert File.exist?(conf_path)
1269
+ assert_log_matches(create_cmdline(conf_path),
1270
+ "[debug]")
1271
+ end
1272
+ end
1273
+
1274
+ sub_test_case "inline_config" do
1275
+ test "can change log_level by --inline-config" do
1276
+ # Since we can't define multiple `<system>` directives, this use-case is not recommended.
1277
+ # This is just for this test.
1278
+ inline_conf = '<system>\nlog_level debug\n</system>'
1279
+ conf_path = create_conf_file('test.conf', "")
1280
+ assert File.exist?(conf_path)
1281
+ assert_log_matches(create_cmdline(conf_path, "--inline-config", inline_conf),
1282
+ "[debug]")
1283
+ end
1284
+ end
1154
1285
  end
@@ -188,7 +188,6 @@ slow_flush_log_threshold: float: (20.0)
188
188
  retry_exponential_backoff_base: float: (2)
189
189
  retry_max_interval: time: (nil)
190
190
  retry_randomize: bool: (true)
191
- disable_chunk_backup: bool: (false)
192
191
  <secondary>: optional, single
193
192
  @type: string: (nil)
194
193
  <buffer>: optional, single
@@ -38,14 +38,14 @@ class TextParserTest < ::Test::Unit::TestCase
38
38
 
39
39
  def test_parse_with_return
40
40
  parser = Fluent::TextParser.new
41
- parser.configure('format' => 'none')
41
+ parser.configure(config_element('test', '', 'format' => 'none'))
42
42
  _time, record = parser.parse('log message!')
43
43
  assert_equal({'message' => 'log message!'}, record)
44
44
  end
45
45
 
46
46
  def test_parse_with_block
47
47
  parser = Fluent::TextParser.new
48
- parser.configure('format' => 'none')
48
+ parser.configure(config_element('test', '', 'format' => 'none'))
49
49
  parser.parse('log message!') { |time, record|
50
50
  assert_equal({'message' => 'log message!'}, record)
51
51
  }
@@ -53,7 +53,7 @@ class TextParserTest < ::Test::Unit::TestCase
53
53
 
54
54
  def test_multi_event_parser
55
55
  parser = Fluent::TextParser.new
56
- parser.configure('format' => 'multi_event_test')
56
+ parser.configure(config_element('test', '', 'format' => 'multi_event_test'))
57
57
  i = 0
58
58
  parser.parse('log message!') { |time, record|
59
59
  assert_equal('log message!', record['message'])
@@ -67,7 +67,7 @@ class TextParserTest < ::Test::Unit::TestCase
67
67
  assert_nil p1.estimate_current_event
68
68
  assert_nil p1.parser
69
69
 
70
- p1.configure('format' => 'none')
70
+ p1.configure(config_element('test', '', 'format' => 'none'))
71
71
  assert_equal true, p1.parser.estimate_current_event
72
72
 
73
73
  p2 = Fluent::TextParser.new
@@ -76,7 +76,7 @@ class TextParserTest < ::Test::Unit::TestCase
76
76
 
77
77
  p2.estimate_current_event = false
78
78
 
79
- p2.configure('format' => 'none')
79
+ p2.configure(config_element('test', '', 'format' => 'none'))
80
80
  assert_equal false, p2.parser.estimate_current_event
81
81
  end
82
82
 
@@ -5,13 +5,6 @@ require 'fluent/config/section'
5
5
  require 'fluent/system_config'
6
6
 
7
7
  module Fluent::Config
8
- class FakeLoggerInitializer
9
- attr_accessor :level
10
- def initialize
11
- @level = nil
12
- end
13
- end
14
-
15
8
  class FakeSupervisor
16
9
  attr_writer :log_level
17
10
 
@@ -21,7 +14,6 @@ module Fluent::Config
21
14
  wokers: nil,
22
15
  restart_worker_interval: nil,
23
16
  root_dir: nil,
24
- log: FakeLoggerInitializer.new,
25
17
  log_level: Fluent::Log::LEVEL_INFO,
26
18
  suppress_interval: nil,
27
19
  suppress_config_dump: nil,
@@ -0,0 +1,110 @@
1
+ require_relative '../helper'
2
+
3
+ require 'fluent/log'
4
+ require 'fluent/log/console_adapter'
5
+
6
+ class ConsoleAdapterTest < Test::Unit::TestCase
7
+ def setup
8
+ @timestamp = Time.parse("2023-01-01 15:32:41 +0000")
9
+ @timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
10
+ Timecop.freeze(@timestamp)
11
+
12
+ @logdev = Fluent::Test::DummyLogDevice.new
13
+ @logger = ServerEngine::DaemonLogger.new(@logdev)
14
+ @fluent_log = Fluent::Log.new(@logger)
15
+ @console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
16
+ end
17
+
18
+ def teardown
19
+ Timecop.return
20
+ end
21
+
22
+ def test_expected_log_levels
23
+ assert_equal({debug: 0, info: 1, warn: 2, error: 3, fatal: 4},
24
+ Console::Logger::LEVELS)
25
+ end
26
+
27
+ data(trace: [Fluent::Log::LEVEL_TRACE, :debug],
28
+ debug: [Fluent::Log::LEVEL_DEBUG, :debug],
29
+ info: [Fluent::Log::LEVEL_INFO, :info],
30
+ warn: [Fluent::Log::LEVEL_WARN, :warn],
31
+ error: [Fluent::Log::LEVEL_ERROR, :error],
32
+ fatal: [Fluent::Log::LEVEL_FATAL, :fatal])
33
+ def test_reflect_log_level(data)
34
+ level, expected = data
35
+ @fluent_log.level = level
36
+ console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
37
+ assert_equal(Console::Logger::LEVELS[expected],
38
+ console_logger.level)
39
+ end
40
+
41
+ data(debug: :debug,
42
+ info: :info,
43
+ warn: :warn,
44
+ error: :error,
45
+ fatal: :fatal)
46
+ def test_string_subject(level)
47
+ @console_logger.send(level, "subject")
48
+ assert_equal(["#{@timestamp_str} [#{level}]: 0.0s: subject\n"],
49
+ @logdev.logs)
50
+ end
51
+
52
+ data(debug: :debug,
53
+ info: :info,
54
+ warn: :warn,
55
+ error: :error,
56
+ fatal: :fatal)
57
+ def test_args(level)
58
+ @console_logger.send(level, "subject", 1, 2, 3)
59
+ assert_equal([
60
+ "#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
61
+ " | 1\n" +
62
+ " | 2\n" +
63
+ " | 3\n"
64
+ ],
65
+ @logdev.logs)
66
+ end
67
+
68
+ data(debug: :debug,
69
+ info: :info,
70
+ warn: :warn,
71
+ error: :error,
72
+ fatal: :fatal)
73
+ def test_options(level)
74
+ @console_logger.send(level, "subject", kwarg1: "opt1", kwarg2: "opt2")
75
+ assert_equal([
76
+ "#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
77
+ " | {\"kwarg1\":\"opt1\",\"kwarg2\":\"opt2\"}\n"
78
+ ],
79
+ @logdev.logs)
80
+ end
81
+
82
+ data(debug: :debug,
83
+ info: :info,
84
+ warn: :warn,
85
+ error: :error,
86
+ fatal: :fatal)
87
+ def test_block(level)
88
+ @console_logger.send(level, "subject") { "block message" }
89
+ assert_equal([
90
+ "#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
91
+ " | block message\n"
92
+ ],
93
+ @logdev.logs)
94
+ end
95
+
96
+ data(debug: :debug,
97
+ info: :info,
98
+ warn: :warn,
99
+ error: :error,
100
+ fatal: :fatal)
101
+ def test_multiple_entries(level)
102
+ @console_logger.send(level, "subject1")
103
+ @console_logger.send(level, "line2")
104
+ assert_equal([
105
+ "#{@timestamp_str} [#{level}]: 0.0s: subject1\n",
106
+ "#{@timestamp_str} [#{level}]: 0.0s: line2\n"
107
+ ],
108
+ @logdev.logs)
109
+ end
110
+ end
@@ -98,4 +98,43 @@ class AckHandlerTest < Test::Unit::TestCase
98
98
  w.close rescue nil
99
99
  end
100
100
  end
101
+
102
+ # ForwardOutput uses AckHandler in multiple threads, so we need to assume this case.
103
+ # If exclusive control for this case is implemented, this test may not be necessary.
104
+ test 'raises no error when another thread closes a socket' do
105
+ ack_handler = Fluent::Plugin::ForwardOutput::AckHandler.new(timeout: 10, log: $log, read_length: 100)
106
+
107
+ node = flexmock('node', host: '127.0.0.1', port: '1000') # for log
108
+ chunk_id = 'chunk_id 111'
109
+ ack = ack_handler.create_ack(chunk_id, node)
110
+
111
+ r, w = IO.pipe
112
+ begin
113
+ w.write(chunk_id)
114
+ stub(r).recv { |_|
115
+ sleep(1) # To ensure that multiple threads select the socket before closing.
116
+ raise IOError, 'stream closed in another thread' if r.closed?
117
+ MessagePack.pack({ 'ack' => Base64.encode64('chunk_id 111') })
118
+ }
119
+ ack.enqueue(r)
120
+
121
+ threads = []
122
+ 2.times do
123
+ threads << Thread.new do
124
+ ack_handler.collect_response(1) do |cid, n, s, ret|
125
+ s&.close
126
+ end
127
+ end
128
+ end
129
+
130
+ assert_true threads.map{ |t| t.join(10) }.all?
131
+ assert_false(
132
+ $log.out.logs.any? { |log| log.include?('[error]') },
133
+ $log.out.logs.select{ |log| log.include?('[error]') }.join('\n')
134
+ )
135
+ ensure
136
+ r.close rescue nil
137
+ w.close rescue nil
138
+ end
139
+ end
101
140
  end
@@ -146,4 +146,102 @@ class BaseTest < Test::Unit::TestCase
146
146
  end
147
147
  end
148
148
  end
149
+
150
+ test '`ArgumentError` when `conf` is not `Fluent::Config::Element`' do
151
+ assert_raise ArgumentError.new('BUG: type of conf must be Fluent::Config::Element, but Hash is passed.') do
152
+ @p.configure({})
153
+ end
154
+ end
155
+
156
+ sub_test_case 'system_config.workers value after configure' do
157
+ def assert_system_config_workers_value(data)
158
+ conf = config_element()
159
+ conf.set_target_worker_ids(data[:target_worker_ids])
160
+ @p.configure(conf)
161
+ assert{ @p.system_config.workers == data[:expected] }
162
+ end
163
+
164
+ def stub_supervisor_mode
165
+ stub(Fluent::Engine).supervisor_mode { true }
166
+ stub(Fluent::Engine).worker_id { -1 }
167
+ end
168
+
169
+ sub_test_case 'with <system> workers 3 </system>' do
170
+ setup do
171
+ system_config = Fluent::SystemConfig.new
172
+ system_config.workers = 3
173
+ stub(Fluent::Engine).system_config { system_config }
174
+ end
175
+
176
+ data(
177
+ 'without <worker> directive',
178
+ {
179
+ target_worker_ids: [],
180
+ expected: 3
181
+ },
182
+ keep: true
183
+ )
184
+ data(
185
+ 'with <worker 0>',
186
+ {
187
+ target_worker_ids: [0],
188
+ expected: 1
189
+ },
190
+ keep: true
191
+ )
192
+ data(
193
+ 'with <worker 0-1>',
194
+ {
195
+ target_worker_ids: [0, 1],
196
+ expected: 2
197
+ },
198
+ keep: true
199
+ )
200
+ data(
201
+ 'with <worker 0-2>',
202
+ {
203
+ target_worker_ids: [0, 1, 2],
204
+ expected: 3
205
+ },
206
+ keep: true
207
+ )
208
+
209
+ test 'system_config.workers value after configure' do
210
+ assert_system_config_workers_value(data)
211
+ end
212
+
213
+ test 'system_config.workers value after configure with supervisor_mode' do
214
+ stub_supervisor_mode
215
+ assert_system_config_workers_value(data)
216
+ end
217
+ end
218
+
219
+ sub_test_case 'without <system> directive' do
220
+ data(
221
+ 'without <worker> directive',
222
+ {
223
+ target_worker_ids: [],
224
+ expected: 1
225
+ },
226
+ keep: true
227
+ )
228
+ data(
229
+ 'with <worker 0>',
230
+ {
231
+ target_worker_ids: [0],
232
+ expected: 1
233
+ },
234
+ keep: true
235
+ )
236
+
237
+ test 'system_config.workers value after configure' do
238
+ assert_system_config_workers_value(data)
239
+ end
240
+
241
+ test 'system_config.workers value after configure with supervisor_mode' do
242
+ stub_supervisor_mode
243
+ assert_system_config_workers_value(data)
244
+ end
245
+ end
246
+ end
149
247
  end