fluentd 0.10.62 → 0.12.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/.travis.yml +0 -4
- data/ChangeLog +0 -72
- data/Gemfile +0 -6
- data/Rakefile +12 -3
- data/example/in_http.conf +14 -0
- data/example/in_syslog.conf +15 -0
- data/example/in_tail.conf +14 -0
- data/example/in_tcp.conf +13 -0
- data/example/in_udp.conf +13 -0
- data/example/out_copy.conf +20 -0
- data/example/out_file.conf +13 -0
- data/example/out_forward.conf +30 -0
- data/fluent.conf +2 -12
- data/fluentd.gemspec +8 -11
- data/lib/fluent/agent.rb +180 -0
- data/lib/fluent/buffer.rb +6 -12
- data/lib/fluent/command/cat.rb +1 -3
- data/lib/fluent/command/debug.rb +1 -3
- data/lib/fluent/command/fluentd.rb +0 -10
- data/lib/fluent/config.rb +9 -3
- data/lib/fluent/config/basic_parser.rb +1 -6
- data/lib/fluent/config/configure_proxy.rb +25 -61
- data/lib/fluent/config/dsl.rb +16 -0
- data/lib/fluent/config/element.rb +21 -2
- data/lib/fluent/config/error.rb +16 -0
- data/lib/fluent/config/literal_parser.rb +9 -27
- data/lib/fluent/config/parser.rb +16 -0
- data/lib/fluent/config/section.rb +16 -2
- data/lib/fluent/config/types.rb +16 -1
- data/lib/fluent/config/v1_parser.rb +4 -12
- data/lib/fluent/configurable.rb +16 -0
- data/lib/fluent/engine.rb +43 -163
- data/lib/fluent/env.rb +16 -1
- data/lib/fluent/event.rb +20 -48
- data/lib/fluent/event_router.rb +187 -0
- data/lib/fluent/filter.rb +32 -0
- data/lib/fluent/formatter.rb +29 -101
- data/lib/fluent/input.rb +6 -4
- data/lib/fluent/label.rb +18 -0
- data/lib/fluent/load.rb +1 -3
- data/lib/fluent/log.rb +1 -3
- data/lib/fluent/match.rb +12 -19
- data/lib/fluent/mixin.rb +9 -25
- data/lib/fluent/output.rb +27 -45
- data/lib/fluent/parser.rb +93 -99
- data/lib/fluent/plugin.rb +22 -48
- data/lib/fluent/plugin/buf_file.rb +10 -7
- data/lib/fluent/plugin/buf_memory.rb +2 -3
- data/lib/fluent/plugin/buf_zfile.rb +75 -0
- data/lib/fluent/plugin/exec_util.rb +16 -0
- data/lib/fluent/plugin/in_debug_agent.rb +2 -3
- data/lib/fluent/plugin/in_exec.rb +2 -9
- data/lib/fluent/plugin/in_forward.rb +4 -22
- data/lib/fluent/plugin/in_gc_stat.rb +2 -3
- data/lib/fluent/plugin/in_http.rb +19 -59
- data/lib/fluent/plugin/in_monitor_agent.rb +21 -47
- data/lib/fluent/plugin/in_object_space.rb +2 -3
- data/lib/fluent/plugin/in_status.rb +2 -3
- data/lib/fluent/plugin/in_stream.rb +6 -16
- data/lib/fluent/plugin/in_syslog.rb +8 -17
- data/lib/fluent/plugin/in_tail.rb +17 -24
- data/lib/fluent/plugin/in_tcp.rb +16 -0
- data/lib/fluent/plugin/in_udp.rb +16 -0
- data/lib/fluent/plugin/out_copy.rb +3 -4
- data/lib/fluent/plugin/out_exec.rb +2 -4
- data/lib/fluent/plugin/out_exec_filter.rb +2 -13
- data/lib/fluent/plugin/out_file.rb +5 -6
- data/lib/fluent/plugin/out_forward.rb +4 -5
- data/lib/fluent/plugin/out_null.rb +2 -3
- data/lib/fluent/plugin/out_relabel.rb +26 -0
- data/lib/fluent/plugin/out_roundrobin.rb +3 -4
- data/lib/fluent/plugin/out_stdout.rb +2 -3
- data/lib/fluent/plugin/out_stream.rb +2 -3
- data/{test/scripts → lib}/fluent/plugin/out_test.rb +2 -3
- data/lib/fluent/plugin/socket_util.rb +19 -10
- data/lib/fluent/process.rb +4 -6
- data/lib/fluent/registry.rb +16 -0
- data/lib/fluent/root_agent.rb +212 -0
- data/lib/fluent/status.rb +2 -3
- data/lib/fluent/supervisor.rb +33 -54
- data/lib/fluent/test.rb +16 -0
- data/lib/fluent/test/base.rb +3 -17
- data/lib/fluent/test/input_test.rb +52 -7
- data/lib/fluent/test/output_test.rb +4 -20
- data/lib/fluent/version.rb +17 -1
- data/spec/config/config_parser_spec.rb +314 -0
- data/spec/config/configurable_spec.rb +524 -0
- data/spec/config/configure_proxy_spec.rb +96 -0
- data/spec/config/dsl_spec.rb +239 -0
- data/spec/config/helper.rb +49 -0
- data/spec/config/literal_parser_spec.rb +222 -0
- data/spec/config/section_spec.rb +97 -0
- data/spec/config/system_config_spec.rb +49 -0
- data/test/helper.rb +0 -25
- data/test/plugin/test_in_exec.rb +1 -1
- data/test/plugin/test_in_forward.rb +2 -1
- data/test/plugin/test_in_gc_stat.rb +1 -1
- data/test/plugin/test_in_http.rb +3 -78
- data/test/plugin/test_in_object_space.rb +1 -1
- data/test/plugin/test_in_status.rb +1 -1
- data/test/plugin/test_in_stream.rb +2 -1
- data/test/plugin/test_in_syslog.rb +2 -1
- data/test/plugin/test_in_tail.rb +6 -11
- data/test/plugin/test_in_tcp.rb +2 -1
- data/test/plugin/test_in_udp.rb +2 -1
- data/test/plugin/test_out_copy.rb +1 -12
- data/test/plugin/test_out_exec.rb +1 -1
- data/test/plugin/test_out_exec_filter.rb +1 -1
- data/test/plugin/test_out_file.rb +7 -96
- data/test/plugin/test_out_forward.rb +2 -1
- data/test/plugin/test_out_roundrobin.rb +1 -12
- data/test/plugin/test_out_stdout.rb +1 -1
- data/test/plugin/test_out_stream.rb +2 -1
- data/test/scripts/fluent/plugin/formatter_known.rb +1 -4
- data/test/scripts/fluent/plugin/parser_known.rb +1 -2
- data/test/test_config.rb +1 -1
- data/test/test_configdsl.rb +2 -1
- data/test/test_formatter.rb +3 -395
- data/test/test_match.rb +2 -1
- data/test/test_mixin.rb +3 -75
- data/test/test_output.rb +1 -112
- data/test/test_parser.rb +85 -152
- metadata +58 -167
- data/example/v1_literal_example.conf +0 -36
- data/lib/fluent/plugin/in_dummy.rb +0 -103
- data/lib/fluent/timezone.rb +0 -131
- data/test/config/assertions.rb +0 -42
- data/test/config/test_config_parser.rb +0 -389
- data/test/config/test_configurable.rb +0 -652
- data/test/config/test_configure_proxy.rb +0 -99
- data/test/config/test_dsl.rb +0 -237
- data/test/config/test_literal_parser.rb +0 -295
- data/test/config/test_section.rb +0 -112
- data/test/config/test_system_config.rb +0 -99
- data/test/config/test_types.rb +0 -63
- data/test/plugin/test_in_dummy.rb +0 -95
- data/test/test_event.rb +0 -168
- data/test/test_input.rb +0 -21
@@ -1,103 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Fluentd
|
3
|
-
#
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
#
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
#
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
#
|
16
|
-
|
17
|
-
module Fluent
|
18
|
-
class DummyInput < Input
|
19
|
-
Fluent::Plugin.register_input('dummy', self)
|
20
|
-
|
21
|
-
BIN_NUM = 10
|
22
|
-
|
23
|
-
config_param :tag, :string
|
24
|
-
config_param :rate, :integer, :default => 1
|
25
|
-
config_param :auto_increment_key, :string, :default => nil
|
26
|
-
config_param :dummy, :default => [{"message"=>"dummy"}] do |val|
|
27
|
-
begin
|
28
|
-
parsed = JSON.parse(val)
|
29
|
-
rescue JSON::ParserError => e
|
30
|
-
# Fluent::ConfigParseError, "got incomplete JSON" will be raised
|
31
|
-
# at literal_parser.rb with --use-v1-config, but I had to
|
32
|
-
# take care at here for the case of --use-v0-config.
|
33
|
-
raise Fluent::ConfigError, "#{e.class}: #{e.message}"
|
34
|
-
end
|
35
|
-
dummy = parsed.is_a?(Array) ? parsed : [parsed]
|
36
|
-
dummy.each_with_index do |e, i|
|
37
|
-
raise Fluent::ConfigError, "#{i}th element of dummy, #{e}, is not a hash" unless e.is_a?(Hash)
|
38
|
-
end
|
39
|
-
dummy
|
40
|
-
end
|
41
|
-
|
42
|
-
def configure(conf)
|
43
|
-
super
|
44
|
-
|
45
|
-
@increment_value = 0
|
46
|
-
@dummy_index = 0
|
47
|
-
end
|
48
|
-
|
49
|
-
def start
|
50
|
-
super
|
51
|
-
@running = true
|
52
|
-
@thread = Thread.new(&method(:run))
|
53
|
-
end
|
54
|
-
|
55
|
-
def shutdown
|
56
|
-
@running = false
|
57
|
-
@thread.join
|
58
|
-
end
|
59
|
-
|
60
|
-
def run
|
61
|
-
batch_num = (@rate / BIN_NUM).to_i
|
62
|
-
residual_num = (@rate % BIN_NUM)
|
63
|
-
while @running
|
64
|
-
current_time = Time.now.to_i
|
65
|
-
BIN_NUM.times do
|
66
|
-
break unless (@running && Time.now.to_i <= current_time)
|
67
|
-
wait(0.1) { emit(batch_num) }
|
68
|
-
end
|
69
|
-
emit(residual_num)
|
70
|
-
# wait for next second
|
71
|
-
while @running && Time.now.to_i <= current_time
|
72
|
-
sleep 0.01
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def emit(num)
|
78
|
-
num.times { router.emit(@tag, Fluent::Engine.now, generate()) }
|
79
|
-
end
|
80
|
-
|
81
|
-
def generate
|
82
|
-
d = @dummy[@dummy_index]
|
83
|
-
unless d
|
84
|
-
@dummy_index = 0
|
85
|
-
d = @dummy[0]
|
86
|
-
end
|
87
|
-
@dummy_index += 1
|
88
|
-
if @auto_increment_key
|
89
|
-
d = d.dup
|
90
|
-
d[@auto_increment_key] = @increment_value
|
91
|
-
@increment_value += 1
|
92
|
-
end
|
93
|
-
d
|
94
|
-
end
|
95
|
-
|
96
|
-
def wait(time)
|
97
|
-
start_time = Time.now
|
98
|
-
yield
|
99
|
-
sleep_time = time - (Time.now - start_time)
|
100
|
-
sleep sleep_time if sleep_time > 0
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
data/lib/fluent/timezone.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Fluentd
|
3
|
-
#
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
#
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
#
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
#
|
16
|
-
|
17
|
-
require 'tzinfo'
|
18
|
-
|
19
|
-
module Fluent
|
20
|
-
class Timezone
|
21
|
-
# [+-]HH:MM, [+-]HHMM, [+-]HH
|
22
|
-
NUMERIC_PATTERN = %r{\A[+-]\d\d(:?\d\d)?\z}
|
23
|
-
|
24
|
-
# Region/Zone, Region/Zone/Zone
|
25
|
-
NAME_PATTERN = %r{\A[^/]+/[^/]+(/[^/]+)?\z}
|
26
|
-
|
27
|
-
# Validate the format of the specified timezone.
|
28
|
-
#
|
29
|
-
# Valid formats are as follows. Note that timezone abbreviations
|
30
|
-
# such as PST and JST are not supported intentionally.
|
31
|
-
#
|
32
|
-
# 1. [+-]HH:MM (e.g. "+09:00")
|
33
|
-
# 2. [+-]HHMM (e.g. "+0900")
|
34
|
-
# 3. [+-]HH (e.g. "+09")
|
35
|
-
# 4. Region/Zone (e.g. "Asia/Tokyo")
|
36
|
-
# 5. Region/Zone/Zone (e.g. "America/Argentina/Buenos_Aires")
|
37
|
-
#
|
38
|
-
# In the 4th and 5th cases, it is checked whether the specified
|
39
|
-
# timezone exists in the timezone database.
|
40
|
-
#
|
41
|
-
# When the given timezone is valid, true is returned. Otherwise,
|
42
|
-
# false is returned. When nil is given, false is returned.
|
43
|
-
def self.validate(timezone)
|
44
|
-
# If the specified timezone is nil.
|
45
|
-
if timezone.nil?
|
46
|
-
# Invalid.
|
47
|
-
return false
|
48
|
-
end
|
49
|
-
|
50
|
-
# [+-]HH:MM, [+-]HHMM, [+-]HH
|
51
|
-
if NUMERIC_PATTERN === timezone
|
52
|
-
# Valid. It can be parsed by Time.zone_offset method.
|
53
|
-
return true
|
54
|
-
end
|
55
|
-
|
56
|
-
# Region/Zone, Region/Zone/Zone
|
57
|
-
if NAME_PATTERN === timezone
|
58
|
-
begin
|
59
|
-
# Get a Timezone instance for the specified timezone.
|
60
|
-
TZInfo::Timezone.get(timezone)
|
61
|
-
rescue
|
62
|
-
# Invalid. The string does not exist in the timezone database.
|
63
|
-
return false
|
64
|
-
else
|
65
|
-
# Valid. The string was found in the timezone database.
|
66
|
-
return true
|
67
|
-
end
|
68
|
-
else
|
69
|
-
# Invalid. Timezone abbreviations are not supported.
|
70
|
-
return false
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Validate the format of the specified timezone.
|
75
|
-
#
|
76
|
-
# The implementation of this method calls validate(timezone) method
|
77
|
-
# to check whether the given timezone is valid. When invalid, this
|
78
|
-
# method raises a ConfigError.
|
79
|
-
def self.validate!(timezone)
|
80
|
-
unless validate(timezone)
|
81
|
-
raise ConfigError, "Unsupported timezone '#{timezone}'"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Create a formatter for a timezone and optionally a format.
|
86
|
-
#
|
87
|
-
# An Proc object is returned. If the given timezone is invalid,
|
88
|
-
# nil is returned.
|
89
|
-
def self.formatter(timezone, format = nil)
|
90
|
-
if timezone.nil?
|
91
|
-
return nil
|
92
|
-
end
|
93
|
-
|
94
|
-
# [+-]HH:MM, [+-]HHMM, [+-]HH
|
95
|
-
if NUMERIC_PATTERN === timezone
|
96
|
-
offset = Time.zone_offset(timezone)
|
97
|
-
|
98
|
-
if format
|
99
|
-
return Proc.new {|time|
|
100
|
-
Time.at(time).localtime(offset).strftime(format)
|
101
|
-
}
|
102
|
-
else
|
103
|
-
return Proc.new {|time|
|
104
|
-
Time.at(time).localtime(offset).iso8601
|
105
|
-
}
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# Region/Zone, Region/Zone/Zone
|
110
|
-
if NAME_PATTERN === timezone
|
111
|
-
begin
|
112
|
-
tz = TZInfo::Timezone.get(timezone)
|
113
|
-
rescue
|
114
|
-
return nil
|
115
|
-
end
|
116
|
-
|
117
|
-
if format
|
118
|
-
return Proc.new {|time|
|
119
|
-
Time.at(time).localtime(tz.period_for_utc(time).utc_total_offset).strftime(format)
|
120
|
-
}
|
121
|
-
else
|
122
|
-
return Proc.new {|time|
|
123
|
-
Time.at(time).localtime(tz.period_for_utc(time).utc_total_offset).iso8601
|
124
|
-
}
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
return nil
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
data/test/config/assertions.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'test/unit/assertions'
|
2
|
-
|
3
|
-
module Test::Unit::Assertions
|
4
|
-
def assert_text_parsed_as(expected, actual)
|
5
|
-
msg = parse_text(actual).inspect rescue 'failed'
|
6
|
-
msg = "expected that #{actual.inspect} would be a parsed as #{expected.inspect} but got #{msg}"
|
7
|
-
assert_block(msg) {
|
8
|
-
v = parse_text(actual)
|
9
|
-
if expected.is_a?(Float)
|
10
|
-
v.is_a?(Float) && (v == obj || (v.nan? && obj.nan?) || (v - obj).abs < 0.000001)
|
11
|
-
else
|
12
|
-
v == expected
|
13
|
-
end
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
def assert_text_parsed_as_json(expected, actual)
|
18
|
-
msg = parse_text(actual).inspect rescue 'failed'
|
19
|
-
msg = "expected that #{actual.inspect} would be a parsed as #{expected.inspect} but got #{msg}"
|
20
|
-
assert_block(msg) {
|
21
|
-
v = JSON.parse(parse_text(actual))
|
22
|
-
v == expected
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
def assert_parse_error(actual)
|
27
|
-
msg = begin
|
28
|
-
parse_text(actual).inspect
|
29
|
-
rescue => e
|
30
|
-
e.inspect
|
31
|
-
end
|
32
|
-
msg = "expected that #{actual.inspect} would cause a parse error but got #{msg}"
|
33
|
-
assert_block(msg) {
|
34
|
-
begin
|
35
|
-
parse_text(actual)
|
36
|
-
false
|
37
|
-
rescue Fluent::ConfigParseError
|
38
|
-
true
|
39
|
-
end
|
40
|
-
}
|
41
|
-
end
|
42
|
-
end
|
@@ -1,389 +0,0 @@
|
|
1
|
-
require_relative '../helper'
|
2
|
-
require_relative "assertions"
|
3
|
-
require "json"
|
4
|
-
require "fluent/config/error"
|
5
|
-
require "fluent/config/basic_parser"
|
6
|
-
require "fluent/config/literal_parser"
|
7
|
-
require "fluent/config/v1_parser"
|
8
|
-
|
9
|
-
module Fluent::Config
|
10
|
-
module V1TestHelper
|
11
|
-
def root(*elements)
|
12
|
-
if elements.first.is_a?(Fluent::Config::Element)
|
13
|
-
attrs = {}
|
14
|
-
else
|
15
|
-
attrs = elements.shift || {}
|
16
|
-
end
|
17
|
-
Fluent::Config::Element.new('ROOT', '', attrs, elements)
|
18
|
-
end
|
19
|
-
|
20
|
-
def e(name, arg='', attrs={}, elements=[])
|
21
|
-
Fluent::Config::Element.new(name, arg, attrs, elements)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class TestV1Parser < ::Test::Unit::TestCase
|
26
|
-
def read_config(path)
|
27
|
-
path = File.expand_path(path)
|
28
|
-
data = File.read(path)
|
29
|
-
Fluent::Config::V1Parser.parse(data, File.basename(path), File.dirname(path))
|
30
|
-
end
|
31
|
-
|
32
|
-
def parse_text(text)
|
33
|
-
basepath = File.expand_path(File.dirname(__FILE__) + '/../../')
|
34
|
-
Fluent::Config::V1Parser.parse(text, '(test)', basepath, nil)
|
35
|
-
end
|
36
|
-
|
37
|
-
include V1TestHelper
|
38
|
-
extend V1TestHelper
|
39
|
-
|
40
|
-
sub_test_case 'attribute parsing' do
|
41
|
-
test "parses attributes" do
|
42
|
-
assert_text_parsed_as(e('ROOT', '', {"k1"=>"v1", "k2"=>"v2"}), %[
|
43
|
-
k1 v1
|
44
|
-
k2 v2
|
45
|
-
])
|
46
|
-
end
|
47
|
-
|
48
|
-
test "allows attribute without value" do
|
49
|
-
assert_text_parsed_as(e('ROOT', '', {"k1"=>"", "k2"=>"v2"}), %[
|
50
|
-
k1
|
51
|
-
k2 v2
|
52
|
-
])
|
53
|
-
end
|
54
|
-
|
55
|
-
test "parses attribute key always string" do
|
56
|
-
assert_text_parsed_as(e('ROOT', '', {"1" => "1"}), "1 1")
|
57
|
-
end
|
58
|
-
|
59
|
-
data("_.%$!," => "_.%$!,",
|
60
|
-
"/=~-~@\`:?" => "/=~-~@\`:?",
|
61
|
-
"()*{}.[]" => "()*{}.[]")
|
62
|
-
test "parses a value with symbols" do |v|
|
63
|
-
assert_text_parsed_as(e('ROOT', '', {"k" => v}), "k #{v}")
|
64
|
-
end
|
65
|
-
|
66
|
-
test "ignores spacing around value" do
|
67
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a"}), " k1 a ")
|
68
|
-
end
|
69
|
-
|
70
|
-
test "allows spaces in value" do
|
71
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a b c"}), "k1 a b c")
|
72
|
-
end
|
73
|
-
|
74
|
-
sub_test_case 'non-quoted string' do
|
75
|
-
test "remains text starting with '#'" do
|
76
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "#not_comment"}), " k1 #not_comment")
|
77
|
-
end
|
78
|
-
|
79
|
-
test "remains text just after '#'" do
|
80
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a#not_comment"}), " k1 a#not_comment")
|
81
|
-
end
|
82
|
-
|
83
|
-
test "remove text after ` #` (comment)" do
|
84
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a"}), " k1 a #comment")
|
85
|
-
end
|
86
|
-
|
87
|
-
test "does not require escaping backslash" do
|
88
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\\\\"}), " k1 \\\\")
|
89
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\\"}), " k1 \\")
|
90
|
-
end
|
91
|
-
|
92
|
-
test "remains backslash in front of a normal character" do
|
93
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => '\['}), " k1 \\[")
|
94
|
-
end
|
95
|
-
|
96
|
-
test "does not accept escape characters" do
|
97
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => '\n'}), " k1 \\n")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
sub_test_case 'double quoted string' do
|
102
|
-
test "allows # in value" do
|
103
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a#comment"}), ' k1 "a#comment"')
|
104
|
-
end
|
105
|
-
|
106
|
-
test "rejects characters after double quoted string" do
|
107
|
-
assert_parse_error(' k1 "a" 1')
|
108
|
-
end
|
109
|
-
|
110
|
-
test "requires escaping backslash" do
|
111
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\\"}), ' k1 "\\\\"')
|
112
|
-
assert_parse_error(' k1 "\\"')
|
113
|
-
end
|
114
|
-
|
115
|
-
test "requires escaping double quote" do
|
116
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => '"'}), ' k1 "\\""')
|
117
|
-
assert_parse_error(' k1 """')
|
118
|
-
end
|
119
|
-
|
120
|
-
test "removes backslash in front of a normal character" do
|
121
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => '['}), ' k1 "\\["')
|
122
|
-
end
|
123
|
-
|
124
|
-
test "accepts escape characters" do
|
125
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\n"}), ' k1 "\\n"')
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
sub_test_case 'single quoted string' do
|
130
|
-
test "allows # in value" do
|
131
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "a#comment"}), " k1 'a#comment'")
|
132
|
-
end
|
133
|
-
|
134
|
-
test "rejects characters after single quoted string" do
|
135
|
-
assert_parse_error(" k1 'a' 1")
|
136
|
-
end
|
137
|
-
|
138
|
-
test "requires escaping backslash" do
|
139
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\\"}), " k1 '\\\\'")
|
140
|
-
assert_parse_error(" k1 '\\'")
|
141
|
-
end
|
142
|
-
|
143
|
-
test "requires escaping single quote" do
|
144
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "'"}), " k1 '\\''")
|
145
|
-
assert_parse_error(" k1 '''")
|
146
|
-
end
|
147
|
-
|
148
|
-
test "remains backslash in front of a normal character" do
|
149
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => '\\['}), " k1 '\\['")
|
150
|
-
end
|
151
|
-
|
152
|
-
test "does not accept escape characters" do
|
153
|
-
assert_text_parsed_as(e('ROOT', '', {"k1" => "\\n"}), " k1 '\\n'")
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
data(
|
158
|
-
"in match" => %[
|
159
|
-
<match>
|
160
|
-
@k v
|
161
|
-
</match>
|
162
|
-
],
|
163
|
-
"in source" => %[
|
164
|
-
<source>
|
165
|
-
@k v
|
166
|
-
</source>
|
167
|
-
],
|
168
|
-
"in filter" => %[
|
169
|
-
<filter>
|
170
|
-
@k v
|
171
|
-
</filter>
|
172
|
-
],
|
173
|
-
"in top-level" => ' @k v '
|
174
|
-
)
|
175
|
-
def test_rejects_at_prefix_in_the_parameter_name(data)
|
176
|
-
assert_parse_error(data)
|
177
|
-
end
|
178
|
-
|
179
|
-
data(
|
180
|
-
"in nested" => %[
|
181
|
-
<match>
|
182
|
-
<record>
|
183
|
-
@k v
|
184
|
-
</record>
|
185
|
-
</match>
|
186
|
-
]
|
187
|
-
)
|
188
|
-
def test_not_reject_at_prefix_in_the_parameter_name(data)
|
189
|
-
assert_nothing_raised { parse_text(data) }
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
sub_test_case 'element parsing' do
|
194
|
-
data(
|
195
|
-
'root' => [root, ""],
|
196
|
-
"accepts empty element" => [root(e("test")), %[
|
197
|
-
<test>
|
198
|
-
</test>
|
199
|
-
]],
|
200
|
-
"accepts argument and attributes" => [root(e("test", 'var', {'key'=>"val"})), %[
|
201
|
-
<test var>
|
202
|
-
key val
|
203
|
-
</test>
|
204
|
-
]],
|
205
|
-
"accepts nested elements" => [root(
|
206
|
-
e("test", 'var', {'key'=>'1'}, [
|
207
|
-
e('nested1'),
|
208
|
-
e('nested2')
|
209
|
-
])), %[
|
210
|
-
<test var>
|
211
|
-
key 1
|
212
|
-
<nested1>
|
213
|
-
</nested1>
|
214
|
-
<nested2>
|
215
|
-
</nested2>
|
216
|
-
</test>
|
217
|
-
]],
|
218
|
-
"accepts multiline json values" => [root(e("test", 'var', {'key'=>"[\"a\",\"b\",\"c\",\"d\"]"})), %[
|
219
|
-
<test var>
|
220
|
-
key ["a",
|
221
|
-
"b", "c",
|
222
|
-
"d"]
|
223
|
-
</test>
|
224
|
-
]],
|
225
|
-
"parses empty element argument to nil" => [root(e("test", '')), %[
|
226
|
-
<test >
|
227
|
-
</test>
|
228
|
-
]],
|
229
|
-
"ignores spacing around element argument" => [root(e("test", "a")), %[
|
230
|
-
<test a >
|
231
|
-
</test>
|
232
|
-
]],
|
233
|
-
"accepts spacing inside element argument (for multiple tags)" => [root(e("test", "a.** b.**")), %[
|
234
|
-
<test a.** b.** >
|
235
|
-
</test>
|
236
|
-
]])
|
237
|
-
def test_parse_element(data)
|
238
|
-
expected, target = data
|
239
|
-
assert_text_parsed_as(expected, target)
|
240
|
-
end
|
241
|
-
|
242
|
-
[
|
243
|
-
"**",
|
244
|
-
"*.*",
|
245
|
-
"1",
|
246
|
-
"_.%$!",
|
247
|
-
"/",
|
248
|
-
"()*{}.[]",
|
249
|
-
].each do |arg|
|
250
|
-
test "parses symbol element argument:#{arg}" do
|
251
|
-
assert_text_parsed_as(root(e("test", arg)), %[
|
252
|
-
<test #{arg}>
|
253
|
-
</test>
|
254
|
-
])
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
data(
|
259
|
-
"considers comments in element argument" => %[
|
260
|
-
<test #a>
|
261
|
-
</test>
|
262
|
-
],
|
263
|
-
"requires line_end after begin tag" => %[
|
264
|
-
<test></test>
|
265
|
-
],
|
266
|
-
"requires line_end after end tag" => %[
|
267
|
-
<test>
|
268
|
-
</test><test>
|
269
|
-
</test>
|
270
|
-
])
|
271
|
-
def test_parse_error(data)
|
272
|
-
assert_parse_error(data)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
# port from test_config.rb
|
277
|
-
sub_test_case '@include parsing' do
|
278
|
-
TMP_DIR = File.dirname(__FILE__) + "/tmp/v1_config#{ENV['TEST_ENV_NUMBER']}"
|
279
|
-
|
280
|
-
def write_config(path, data)
|
281
|
-
FileUtils.mkdir_p(File.dirname(path))
|
282
|
-
File.open(path, "w") { |f| f.write data }
|
283
|
-
end
|
284
|
-
|
285
|
-
def prepare_config
|
286
|
-
write_config "#{TMP_DIR}/config_test_1.conf", %[
|
287
|
-
k1 root_config
|
288
|
-
include dir/config_test_2.conf #
|
289
|
-
@include #{TMP_DIR}/config_test_4.conf
|
290
|
-
include file://#{TMP_DIR}/config_test_5.conf
|
291
|
-
@include config.d/*.conf
|
292
|
-
]
|
293
|
-
write_config "#{TMP_DIR}/dir/config_test_2.conf", %[
|
294
|
-
k2 relative_path_include
|
295
|
-
@include ../config_test_3.conf
|
296
|
-
]
|
297
|
-
write_config "#{TMP_DIR}/config_test_3.conf", %[
|
298
|
-
k3 relative_include_in_included_file
|
299
|
-
]
|
300
|
-
write_config "#{TMP_DIR}/config_test_4.conf", %[
|
301
|
-
k4 absolute_path_include
|
302
|
-
]
|
303
|
-
write_config "#{TMP_DIR}/config_test_5.conf", %[
|
304
|
-
k5 uri_include
|
305
|
-
]
|
306
|
-
write_config "#{TMP_DIR}/config.d/config_test_6.conf", %[
|
307
|
-
k6 wildcard_include_1
|
308
|
-
<elem1 name>
|
309
|
-
include normal_parameter
|
310
|
-
</elem1>
|
311
|
-
]
|
312
|
-
write_config "#{TMP_DIR}/config.d/config_test_7.conf", %[
|
313
|
-
k7 wildcard_include_2
|
314
|
-
]
|
315
|
-
write_config "#{TMP_DIR}/config.d/config_test_8.conf", %[
|
316
|
-
<elem2 name>
|
317
|
-
@include ../dir/config_test_9.conf
|
318
|
-
</elem2>
|
319
|
-
]
|
320
|
-
write_config "#{TMP_DIR}/dir/config_test_9.conf", %[
|
321
|
-
k9 embeded
|
322
|
-
<elem3 name>
|
323
|
-
nested nested_value
|
324
|
-
include hoge
|
325
|
-
</elem3>
|
326
|
-
]
|
327
|
-
write_config "#{TMP_DIR}/config.d/00_config_test_8.conf", %[
|
328
|
-
k8 wildcard_include_3
|
329
|
-
<elem4 name>
|
330
|
-
include normal_parameter
|
331
|
-
</elem4>
|
332
|
-
]
|
333
|
-
end
|
334
|
-
|
335
|
-
test 'parses @include / include correctly' do
|
336
|
-
prepare_config
|
337
|
-
c = read_config("#{TMP_DIR}/config_test_1.conf")
|
338
|
-
assert_equal('root_config', c['k1'])
|
339
|
-
assert_equal('relative_path_include', c['k2'])
|
340
|
-
assert_equal('relative_include_in_included_file', c['k3'])
|
341
|
-
assert_equal('absolute_path_include', c['k4'])
|
342
|
-
assert_equal('uri_include', c['k5'])
|
343
|
-
assert_equal('wildcard_include_1', c['k6'])
|
344
|
-
assert_equal('wildcard_include_2', c['k7'])
|
345
|
-
assert_equal('wildcard_include_3', c['k8'])
|
346
|
-
assert_equal([
|
347
|
-
'k1',
|
348
|
-
'k2',
|
349
|
-
'k3',
|
350
|
-
'k4',
|
351
|
-
'k5',
|
352
|
-
'k8', # Because of the file name this comes first.
|
353
|
-
'k6',
|
354
|
-
'k7',
|
355
|
-
], c.keys)
|
356
|
-
|
357
|
-
elem1 = c.elements.find { |e| e.name == 'elem1' }
|
358
|
-
assert(elem1)
|
359
|
-
assert_equal('name', elem1.arg)
|
360
|
-
assert_equal('normal_parameter', elem1['include'])
|
361
|
-
|
362
|
-
elem2 = c.elements.find { |e| e.name == 'elem2' }
|
363
|
-
assert(elem2)
|
364
|
-
assert_equal('name', elem2.arg)
|
365
|
-
assert_equal('embeded', elem2['k9'])
|
366
|
-
assert_not_include(elem2, 'include')
|
367
|
-
|
368
|
-
elem3 = elem2.elements.find { |e| e.name == 'elem3' }
|
369
|
-
assert(elem3)
|
370
|
-
assert_equal('nested_value', elem3['nested'])
|
371
|
-
assert_equal('hoge', elem3['include'])
|
372
|
-
end
|
373
|
-
|
374
|
-
# TODO: Add uri based include spec
|
375
|
-
end
|
376
|
-
|
377
|
-
sub_test_case '#to_s' do
|
378
|
-
test 'parses dumpped configuration' do
|
379
|
-
original = %q!a\\\n\r\f\b'"z!
|
380
|
-
expected = %q!a\\\n\r\f\b'"z!
|
381
|
-
|
382
|
-
conf = parse_text(%[k1 #{original}])
|
383
|
-
assert_equal(expected, conf['k1']) # escape check
|
384
|
-
conf2 = parse_text(conf.to_s) # use dumpped configuration to check unescape
|
385
|
-
assert_equal(expected, conf2.elements.first['k1'])
|
386
|
-
end
|
387
|
-
end
|
388
|
-
end
|
389
|
-
end
|