fluentd 0.10.53 → 0.10.54
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 -1
- data/ChangeLog +10 -0
- data/Rakefile +3 -12
- data/example/v1_literal_example.conf +36 -0
- data/fluentd.gemspec +1 -1
- data/lib/fluent/command/fluentd.rb +4 -0
- data/lib/fluent/config/basic_parser.rb +5 -0
- data/lib/fluent/config/element.rb +2 -5
- data/lib/fluent/config/literal_parser.rb +26 -7
- data/lib/fluent/config/v1_parser.rb +1 -1
- data/lib/fluent/parser.rb +14 -3
- data/lib/fluent/plugin/in_http.rb +9 -1
- data/lib/fluent/plugin/in_stream.rb +10 -1
- data/lib/fluent/plugin/in_syslog.rb +10 -1
- data/lib/fluent/plugin/socket_util.rb +9 -1
- data/lib/fluent/version.rb +1 -1
- data/test/config/assertions.rb +42 -0
- data/test/config/test_config_parser.rb +354 -0
- data/test/config/test_configurable.rb +530 -0
- data/test/config/test_configure_proxy.rb +99 -0
- data/test/config/test_dsl.rb +237 -0
- data/test/config/test_literal_parser.rb +293 -0
- data/test/config/test_section.rb +102 -0
- data/test/config/test_system_config.rb +49 -0
- data/test/helper.rb +25 -0
- data/test/plugin/test_in_http.rb +1 -1
- data/test/test_parser.rb +7 -1
- metadata +33 -33
- data/lib/fluent/plugin/buf_zfile.rb +0 -84
- data/spec/config/config_parser_spec.rb +0 -314
- data/spec/config/configurable_spec.rb +0 -524
- data/spec/config/configure_proxy_spec.rb +0 -96
- data/spec/config/dsl_spec.rb +0 -239
- data/spec/config/helper.rb +0 -49
- data/spec/config/literal_parser_spec.rb +0 -222
- data/spec/config/section_spec.rb +0 -97
- data/spec/config/system_config_spec.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a362e48a2716bb0892c742fd3e1a3ba78c943c80
|
4
|
+
data.tar.gz: 4bebdc013320b9e06be09fa93b52e83e95ee814f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a67253d1567eb3f934d2740271c3e1355a1df08b0638809e5add6b9e739f1f99aee1fa27896f9aac798c659f5dc7ce38f943654c5299a8af63ca679c3158f260
|
7
|
+
data.tar.gz: f32dd1c438f4a6f0598182d0a26d712f05cd1782a3eeebad4d32026529668e8212b4cf0269361646c440b1b3cae8e0e5b46b89cff8e2ba4e7334e42a819953af
|
data/.gitignore
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
Release 0.10.54 - 2014/10/17
|
2
|
+
|
3
|
+
* config: Change v1's non-quoted string literal behaviour to v0's behaviour
|
4
|
+
* config: Add --use-v0-config for keeping old configuration
|
5
|
+
* config: Add single quoted literal in v1 configuration
|
6
|
+
* config: Improve error message of Array / Hash parse error
|
7
|
+
* input: Reduce shutdown time in some network related plugins when use Cool.io 1.2
|
8
|
+
* parser: Use ParserError instead of general exception classes
|
9
|
+
* Remove unused zfile buffer plugin
|
10
|
+
|
1
11
|
Release 0.10.53 - 2014/08/21
|
2
12
|
|
3
13
|
* in_tail: Fix forget to detach Closer timer object
|
data/Rakefile
CHANGED
@@ -4,10 +4,8 @@ require "bundler/gem_tasks"
|
|
4
4
|
require 'fileutils'
|
5
5
|
require 'rake/testtask'
|
6
6
|
require 'rake/clean'
|
7
|
-
require 'rspec/core'
|
8
|
-
require 'rspec/core/rake_task'
|
9
7
|
|
10
|
-
task :test => [:base_test
|
8
|
+
task :test => [:base_test]
|
11
9
|
|
12
10
|
desc 'Run test_unit based test'
|
13
11
|
Rake::TestTask.new(:base_test) do |t|
|
@@ -15,7 +13,7 @@ Rake::TestTask.new(:base_test) do |t|
|
|
15
13
|
# $ bundle exec rake base_test TEST=test/test_specified_path.rb
|
16
14
|
# $ bundle exec rake base_test TEST=test/test_*.rb
|
17
15
|
t.libs << "test"
|
18
|
-
t.test_files =
|
16
|
+
t.test_files = Dir["test/**/test_*.rb"].sort
|
19
17
|
t.verbose = true
|
20
18
|
#t.warning = true
|
21
19
|
end
|
@@ -26,14 +24,7 @@ task :parallel_test do
|
|
26
24
|
FileUtils.rm_rf('./test/tmp')
|
27
25
|
end
|
28
26
|
|
29
|
-
desc 'Run
|
30
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
31
|
-
t.rspec_opts = %w[-c -f progress -r ./spec/spec_helper.rb]
|
32
|
-
t.pattern = 'spec/**/*_spec.rb'
|
33
|
-
t.verbose = true
|
34
|
-
end
|
35
|
-
|
36
|
-
desc 'Run rspec with simplecov'
|
27
|
+
desc 'Run test with simplecov'
|
37
28
|
task :coverage do |t|
|
38
29
|
ENV['SIMPLE_COV'] = '1'
|
39
30
|
Rake::Task["spec"].invoke
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<section1>
|
2
|
+
key1 'text' # text
|
3
|
+
key2 '\'' # ' (1 char)
|
4
|
+
key3 '\\' # \ (1 char)
|
5
|
+
key4 '\t' # \t (2 char)
|
6
|
+
key5 '\[' # \[ (2 char)
|
7
|
+
key6 '\\[' # \[ (2 char)
|
8
|
+
key7 '#t' # #t (2 char)
|
9
|
+
key8 '\#{test}' # \#{test} (8 char)
|
10
|
+
key9 '#{test}' # #{test} (7 char)
|
11
|
+
key10 '\[(?<time>[^\]]*)\] (?<message>.*)' # \[(?<time>[^\]]*\] (?<message>.*)
|
12
|
+
</section1>
|
13
|
+
<section2>
|
14
|
+
key1 "text" # text
|
15
|
+
key2 "\"" # " (1 char)
|
16
|
+
key3 "\\" # \ (1 char)
|
17
|
+
key4 "\t" # TAB (1 char)
|
18
|
+
key5 "\[" # [ (1 char)
|
19
|
+
key6 "\\[" # \[ (2 char)
|
20
|
+
key7 "#t" # #t (2 char)
|
21
|
+
key8 "\#{test}" # #{test} (7 char)
|
22
|
+
key9 "#{test}" # replaced by eval('test')
|
23
|
+
key10 "\\[(?<time>[^\\]]*)\\] (?<message>.*)" # \[(?<time>[^\]]*\] (?<message>.*)
|
24
|
+
</section2>
|
25
|
+
<section3>
|
26
|
+
key1 text # text
|
27
|
+
key2 \ # \ (1 char)
|
28
|
+
key3 \\ # \\ (2 char)
|
29
|
+
key4 \t # \t (2 char)
|
30
|
+
key5 \[ # \[ (2 char)
|
31
|
+
key6 \\[ # \\[ (3 char)
|
32
|
+
key7 #t # #t (2 char)
|
33
|
+
key8 \#{test} # \#{test} (8 char)
|
34
|
+
key9 #{test} # #{test} (7 char)
|
35
|
+
key10 \[(?<time>[^\]]*)\] (?<message>.*) # \[(?<time>[^\]]*\] (?<message>.*)
|
36
|
+
</section3>
|
data/fluentd.gemspec
CHANGED
@@ -28,8 +28,8 @@ Gem::Specification.new do |gem|
|
|
28
28
|
gem.add_development_dependency("rake", [">= 0.9.2"])
|
29
29
|
gem.add_development_dependency("flexmock")
|
30
30
|
gem.add_development_dependency("parallel_tests", [">= 0.15.3"])
|
31
|
-
gem.add_development_dependency("rspec", ["~> 3.0.0"])
|
32
31
|
gem.add_development_dependency("simplecov", ["~> 0.6.4"])
|
33
32
|
gem.add_development_dependency("rr", [">= 1.0.0"])
|
34
33
|
gem.add_development_dependency("timecop", [">= 0.3.0"])
|
34
|
+
gem.add_development_dependency("test-unit", ["~> 3.0.2"])
|
35
35
|
end
|
@@ -84,6 +84,10 @@ op.on('--use-v1-config', "Use v1 configuration format", TrueClass) {|b|
|
|
84
84
|
opts[:use_v1_config] = b
|
85
85
|
}
|
86
86
|
|
87
|
+
op.on('--use-v0-config', "Use v0 configuration format (default)", TrueClass) {|b|
|
88
|
+
opts[:use_v1_config] = !b
|
89
|
+
}
|
90
|
+
|
87
91
|
op.on('-v', '--verbose', "increase verbose level (-v: debug, -vv: trace)", TrueClass) {|b|
|
88
92
|
if b
|
89
93
|
opts[:log_level] = [opts[:log_level] - 1, Fluent::Log::LEVEL_TRACE].max
|
@@ -26,6 +26,7 @@ module Fluent
|
|
26
26
|
|
27
27
|
LINE_END = /(?:[ \t]*(?:\#.*)?(?:\z|[\r\n]))+/
|
28
28
|
SPACING = /(?:[ \t\r\n]|\z|\#.*?(?:\z|[\r\n]))+/
|
29
|
+
SPACING_WITHOUT_COMMENT = /(?:[ \t\r\n]|\z)+/
|
29
30
|
|
30
31
|
module ClassMethods
|
31
32
|
def symbol(string)
|
@@ -77,6 +78,10 @@ module Fluent
|
|
77
78
|
skip(SPACING)
|
78
79
|
end
|
79
80
|
|
81
|
+
def spacing_without_comment
|
82
|
+
skip(SPACING_WITHOUT_COMMENT)
|
83
|
+
end
|
84
|
+
|
80
85
|
def parse_error!(message)
|
81
86
|
raise ConfigParseError, "#{message} at #{error_sample}"
|
82
87
|
end
|
@@ -29,6 +29,7 @@ module Fluent
|
|
29
29
|
"name:#{@name}, arg:#{@arg}, " + attrs + ", " + @elements.inspect
|
30
30
|
end
|
31
31
|
|
32
|
+
# This method assumes _o_ is an Element object. Should return false for nil or other object
|
32
33
|
def ==(o)
|
33
34
|
self.name == o.name && self.arg == o.arg &&
|
34
35
|
self.keys.size == o.keys.size &&
|
@@ -86,11 +87,7 @@ module Fluent
|
|
86
87
|
out << "#{indent}<#{@name} #{@arg}>\n"
|
87
88
|
end
|
88
89
|
each_pair { |k, v|
|
89
|
-
|
90
|
-
out << "#{nindent}#{k} #{Element.unescape_parameter(v)}\n"
|
91
|
-
else
|
92
|
-
out << "#{nindent}#{k} #{v}\n"
|
93
|
-
end
|
90
|
+
out << "#{nindent}#{k} #{v}\n"
|
94
91
|
}
|
95
92
|
@elements.each { |e|
|
96
93
|
out << e.to_s(nest + 1)
|
@@ -50,7 +50,7 @@ module Fluent
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def parse_literal(string_boundary_charset = LINE_END)
|
53
|
-
|
53
|
+
spacing_without_comment
|
54
54
|
|
55
55
|
value = if skip(/\[/)
|
56
56
|
scan_json(true)
|
@@ -64,13 +64,15 @@ module Fluent
|
|
64
64
|
|
65
65
|
def scan_string(string_boundary_charset = LINE_END)
|
66
66
|
if skip(/\"/)
|
67
|
-
return
|
67
|
+
return scan_double_quoted_string
|
68
|
+
elsif skip(/\'/)
|
69
|
+
return scan_single_quoted_string
|
68
70
|
else
|
69
71
|
return scan_nonquoted_string(string_boundary_charset)
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
def
|
75
|
+
def scan_double_quoted_string
|
74
76
|
string = []
|
75
77
|
while true
|
76
78
|
if skip(/\"/)
|
@@ -83,7 +85,24 @@ module Fluent
|
|
83
85
|
elsif s = scan(/./)
|
84
86
|
string << s
|
85
87
|
else
|
86
|
-
parse_error! "unexpected end of file in a quoted string"
|
88
|
+
parse_error! "unexpected end of file in a double quoted string"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def scan_single_quoted_string
|
94
|
+
string = []
|
95
|
+
while true
|
96
|
+
if skip(/\'/)
|
97
|
+
return string.join
|
98
|
+
elsif s = scan(/\\'/)
|
99
|
+
string << "'"
|
100
|
+
elsif s = scan(/\\\\/)
|
101
|
+
string << "\\"
|
102
|
+
elsif s = scan(/./)
|
103
|
+
string << s
|
104
|
+
else
|
105
|
+
parse_error! "unexpected end of file in a signle quoted string"
|
87
106
|
end
|
88
107
|
end
|
89
108
|
end
|
@@ -93,8 +112,8 @@ module Fluent
|
|
93
112
|
|
94
113
|
string = []
|
95
114
|
while true
|
96
|
-
if s = scan(
|
97
|
-
string <<
|
115
|
+
if s = scan(/\#/)
|
116
|
+
string << '#'
|
98
117
|
elsif s = scan(charset)
|
99
118
|
string << s
|
100
119
|
else
|
@@ -213,7 +232,7 @@ module Fluent
|
|
213
232
|
end
|
214
233
|
|
215
234
|
unless result
|
216
|
-
parse_error! "got incomplete #{is_array ? 'array' : 'hash'} configuration"
|
235
|
+
parse_error! "got incomplete JSON #{is_array ? 'array' : 'hash'} configuration"
|
217
236
|
end
|
218
237
|
|
219
238
|
JSON.dump(result)
|
data/lib/fluent/parser.rb
CHANGED
@@ -19,6 +19,9 @@ module Fluent
|
|
19
19
|
require 'fluent/registry'
|
20
20
|
|
21
21
|
class TextParser
|
22
|
+
class ParserError < StandardError
|
23
|
+
end
|
24
|
+
|
22
25
|
class TimeParser
|
23
26
|
def initialize(time_format)
|
24
27
|
@cache1_key = nil
|
@@ -35,7 +38,7 @@ module Fluent
|
|
35
38
|
|
36
39
|
def parse(value)
|
37
40
|
unless value.is_a?(String)
|
38
|
-
raise
|
41
|
+
raise ParserError, "value must be string: #{value}"
|
39
42
|
end
|
40
43
|
|
41
44
|
if @cache1_key == value
|
@@ -43,7 +46,11 @@ module Fluent
|
|
43
46
|
elsif @cache2_key == value
|
44
47
|
return @cache2_time
|
45
48
|
else
|
46
|
-
|
49
|
+
begin
|
50
|
+
time = @parser.call(value).to_i
|
51
|
+
rescue => e
|
52
|
+
raise ParserError, "invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
|
53
|
+
end
|
47
54
|
@cache1_key = @cache2_key
|
48
55
|
@cache1_time = @cache2_time
|
49
56
|
@cache2_key = value
|
@@ -225,7 +232,11 @@ module Fluent
|
|
225
232
|
if @time_format
|
226
233
|
time = @mutex.synchronize { @time_parser.parse(value) }
|
227
234
|
else
|
228
|
-
|
235
|
+
begin
|
236
|
+
time = value.to_i
|
237
|
+
rescue => e
|
238
|
+
raise ParserError, "invalid time value: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
|
239
|
+
end
|
229
240
|
end
|
230
241
|
else
|
231
242
|
if @estimate_current_event
|
@@ -104,12 +104,20 @@ module Fluent
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def run
|
107
|
-
|
107
|
+
if support_blocking_timeout?
|
108
|
+
@loop.run(0.5)
|
109
|
+
else
|
110
|
+
@loop.run
|
111
|
+
end
|
108
112
|
rescue
|
109
113
|
log.error "unexpected error", :error=>$!.to_s
|
110
114
|
log.error_backtrace
|
111
115
|
end
|
112
116
|
|
117
|
+
def support_blocking_timeout?
|
118
|
+
@loop.method(:run).arity.nonzero?
|
119
|
+
end
|
120
|
+
|
113
121
|
def on_request(path_info, params)
|
114
122
|
begin
|
115
123
|
path = path_info[1..-1] # remove /
|
@@ -43,13 +43,22 @@ module Fluent
|
|
43
43
|
#end
|
44
44
|
|
45
45
|
def run
|
46
|
-
|
46
|
+
if support_blocking_timeout?
|
47
|
+
@loop.run(0.5)
|
48
|
+
else
|
49
|
+
@loop.run
|
50
|
+
end
|
47
51
|
rescue
|
48
52
|
log.error "unexpected error", :error=>$!.to_s
|
49
53
|
log.error_backtrace
|
50
54
|
end
|
51
55
|
|
52
56
|
protected
|
57
|
+
|
58
|
+
def support_blocking_timeout?
|
59
|
+
@loop.method(:run).arity.nonzero?
|
60
|
+
end
|
61
|
+
|
53
62
|
# message Entry {
|
54
63
|
# 1: long time
|
55
64
|
# 2: object record
|
@@ -117,13 +117,22 @@ module Fluent
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def run
|
120
|
-
|
120
|
+
if support_blocking_timeout?
|
121
|
+
@loop.run(0.5)
|
122
|
+
else
|
123
|
+
@loop.run
|
124
|
+
end
|
121
125
|
rescue
|
122
126
|
log.error "unexpected error", :error=>$!.to_s
|
123
127
|
log.error_backtrace
|
124
128
|
end
|
125
129
|
|
126
130
|
protected
|
131
|
+
|
132
|
+
def support_blocking_timeout?
|
133
|
+
@loop.method(:run).arity.nonzero?
|
134
|
+
end
|
135
|
+
|
127
136
|
def receive_data_parser(data, addr)
|
128
137
|
m = SYSLOG_REGEXP.match(data)
|
129
138
|
unless m
|
@@ -106,7 +106,11 @@ module Fluent
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def run
|
109
|
-
|
109
|
+
if support_blocking_timeout?
|
110
|
+
@loop.run(0.5)
|
111
|
+
else
|
112
|
+
@loop.run
|
113
|
+
end
|
110
114
|
rescue => e
|
111
115
|
log.error "unexpected error", :error => e, :error_class => e.class
|
112
116
|
log.error_backtrace
|
@@ -114,6 +118,10 @@ module Fluent
|
|
114
118
|
|
115
119
|
private
|
116
120
|
|
121
|
+
def support_blocking_timeout?
|
122
|
+
@loop.method(:run).arity.nonzero?
|
123
|
+
end
|
124
|
+
|
117
125
|
def on_message(msg, addr)
|
118
126
|
@parser.parse(msg) { |time, record|
|
119
127
|
unless time && record
|
data/lib/fluent/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
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
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require "json"
|
3
|
+
require "config/assertions"
|
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
|
+
test "rejects @ prefix in parameter name" do
|
158
|
+
assert_parse_error(' @k v')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
sub_test_case 'element parsing' do
|
163
|
+
data(
|
164
|
+
'root' => [root, ""],
|
165
|
+
"accepts empty element" => [root(e("test")), %[
|
166
|
+
<test>
|
167
|
+
</test>
|
168
|
+
]],
|
169
|
+
"accepts argument and attributes" => [root(e("test", 'var', {'key'=>"val"})), %[
|
170
|
+
<test var>
|
171
|
+
key val
|
172
|
+
</test>
|
173
|
+
]],
|
174
|
+
"accepts nested elements" => [root(
|
175
|
+
e("test", 'var', {'key'=>'1'}, [
|
176
|
+
e('nested1'),
|
177
|
+
e('nested2')
|
178
|
+
])), %[
|
179
|
+
<test var>
|
180
|
+
key 1
|
181
|
+
<nested1>
|
182
|
+
</nested1>
|
183
|
+
<nested2>
|
184
|
+
</nested2>
|
185
|
+
</test>
|
186
|
+
]],
|
187
|
+
"accepts multiline json values" => [root(e("test", 'var', {'key'=>"[\"a\",\"b\",\"c\",\"d\"]"})), %[
|
188
|
+
<test var>
|
189
|
+
key ["a",
|
190
|
+
"b", "c",
|
191
|
+
"d"]
|
192
|
+
</test>
|
193
|
+
]],
|
194
|
+
"parses empty element argument to nil" => [root(e("test", '')), %[
|
195
|
+
<test >
|
196
|
+
</test>
|
197
|
+
]],
|
198
|
+
"ignores spacing around element argument" => [root(e("test", "a")), %[
|
199
|
+
<test a >
|
200
|
+
</test>
|
201
|
+
]])
|
202
|
+
def test_parse_element(data)
|
203
|
+
expected, target = data
|
204
|
+
assert_text_parsed_as(expected, target)
|
205
|
+
end
|
206
|
+
|
207
|
+
[
|
208
|
+
"**",
|
209
|
+
"*.*",
|
210
|
+
"1",
|
211
|
+
"_.%$!",
|
212
|
+
"/",
|
213
|
+
"()*{}.[]",
|
214
|
+
].each do |arg|
|
215
|
+
test "parses symbol element argument:#{arg}" do
|
216
|
+
assert_text_parsed_as(root(e("test", arg)), %[
|
217
|
+
<test #{arg}>
|
218
|
+
</test>
|
219
|
+
])
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
data(
|
224
|
+
"considers comments in element argument" => %[
|
225
|
+
<test #a>
|
226
|
+
</test>
|
227
|
+
],
|
228
|
+
"requires line_end after begin tag" => %[
|
229
|
+
<test></test>
|
230
|
+
],
|
231
|
+
"requires line_end after end tag" => %[
|
232
|
+
<test>
|
233
|
+
</test><test>
|
234
|
+
</test>
|
235
|
+
])
|
236
|
+
def test_parse_error(data)
|
237
|
+
assert_parse_error(data)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# port from test_config.rb
|
242
|
+
sub_test_case '@include parsing' do
|
243
|
+
TMP_DIR = File.dirname(__FILE__) + "/tmp/v1_config#{ENV['TEST_ENV_NUMBER']}"
|
244
|
+
|
245
|
+
def write_config(path, data)
|
246
|
+
FileUtils.mkdir_p(File.dirname(path))
|
247
|
+
File.open(path, "w") { |f| f.write data }
|
248
|
+
end
|
249
|
+
|
250
|
+
def prepare_config
|
251
|
+
write_config "#{TMP_DIR}/config_test_1.conf", %[
|
252
|
+
k1 root_config
|
253
|
+
include dir/config_test_2.conf #
|
254
|
+
@include #{TMP_DIR}/config_test_4.conf
|
255
|
+
include file://#{TMP_DIR}/config_test_5.conf
|
256
|
+
@include config.d/*.conf
|
257
|
+
]
|
258
|
+
write_config "#{TMP_DIR}/dir/config_test_2.conf", %[
|
259
|
+
k2 relative_path_include
|
260
|
+
@include ../config_test_3.conf
|
261
|
+
]
|
262
|
+
write_config "#{TMP_DIR}/config_test_3.conf", %[
|
263
|
+
k3 relative_include_in_included_file
|
264
|
+
]
|
265
|
+
write_config "#{TMP_DIR}/config_test_4.conf", %[
|
266
|
+
k4 absolute_path_include
|
267
|
+
]
|
268
|
+
write_config "#{TMP_DIR}/config_test_5.conf", %[
|
269
|
+
k5 uri_include
|
270
|
+
]
|
271
|
+
write_config "#{TMP_DIR}/config.d/config_test_6.conf", %[
|
272
|
+
k6 wildcard_include_1
|
273
|
+
<elem1 name>
|
274
|
+
include normal_parameter
|
275
|
+
</elem1>
|
276
|
+
]
|
277
|
+
write_config "#{TMP_DIR}/config.d/config_test_7.conf", %[
|
278
|
+
k7 wildcard_include_2
|
279
|
+
]
|
280
|
+
write_config "#{TMP_DIR}/config.d/config_test_8.conf", %[
|
281
|
+
<elem2 name>
|
282
|
+
@include ../dir/config_test_9.conf
|
283
|
+
</elem2>
|
284
|
+
]
|
285
|
+
write_config "#{TMP_DIR}/dir/config_test_9.conf", %[
|
286
|
+
k9 embeded
|
287
|
+
<elem3 name>
|
288
|
+
nested nested_value
|
289
|
+
include hoge
|
290
|
+
</elem3>
|
291
|
+
]
|
292
|
+
write_config "#{TMP_DIR}/config.d/00_config_test_8.conf", %[
|
293
|
+
k8 wildcard_include_3
|
294
|
+
<elem4 name>
|
295
|
+
include normal_parameter
|
296
|
+
</elem4>
|
297
|
+
]
|
298
|
+
end
|
299
|
+
|
300
|
+
test 'parses @include / include correctly' do
|
301
|
+
prepare_config
|
302
|
+
c = read_config("#{TMP_DIR}/config_test_1.conf")
|
303
|
+
assert_equal('root_config', c['k1'])
|
304
|
+
assert_equal('relative_path_include', c['k2'])
|
305
|
+
assert_equal('relative_include_in_included_file', c['k3'])
|
306
|
+
assert_equal('absolute_path_include', c['k4'])
|
307
|
+
assert_equal('uri_include', c['k5'])
|
308
|
+
assert_equal('wildcard_include_1', c['k6'])
|
309
|
+
assert_equal('wildcard_include_2', c['k7'])
|
310
|
+
assert_equal('wildcard_include_3', c['k8'])
|
311
|
+
assert_equal([
|
312
|
+
'k1',
|
313
|
+
'k2',
|
314
|
+
'k3',
|
315
|
+
'k4',
|
316
|
+
'k5',
|
317
|
+
'k8', # Because of the file name this comes first.
|
318
|
+
'k6',
|
319
|
+
'k7',
|
320
|
+
], c.keys)
|
321
|
+
|
322
|
+
elem1 = c.elements.find { |e| e.name == 'elem1' }
|
323
|
+
assert(elem1)
|
324
|
+
assert_equal('name', elem1.arg)
|
325
|
+
assert_equal('normal_parameter', elem1['include'])
|
326
|
+
|
327
|
+
elem2 = c.elements.find { |e| e.name == 'elem2' }
|
328
|
+
assert(elem2)
|
329
|
+
assert_equal('name', elem2.arg)
|
330
|
+
assert_equal('embeded', elem2['k9'])
|
331
|
+
assert_not_include(elem2, 'include')
|
332
|
+
|
333
|
+
elem3 = elem2.elements.find { |e| e.name == 'elem3' }
|
334
|
+
assert(elem3)
|
335
|
+
assert_equal('nested_value', elem3['nested'])
|
336
|
+
assert_equal('hoge', elem3['include'])
|
337
|
+
end
|
338
|
+
|
339
|
+
# TODO: Add uri based include spec
|
340
|
+
end
|
341
|
+
|
342
|
+
sub_test_case '#to_s' do
|
343
|
+
test 'parses dumpped configuration' do
|
344
|
+
original = %q!a\\\n\r\f\b'"z!
|
345
|
+
expected = %q!a\\\n\r\f\b'"z!
|
346
|
+
|
347
|
+
conf = parse_text(%[k1 #{original}])
|
348
|
+
assert_equal(expected, conf['k1']) # escape check
|
349
|
+
conf2 = parse_text(conf.to_s) # use dumpped configuration to check unescape
|
350
|
+
assert_equal(expected, conf2.elements.first['k1'])
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|