hocon 0.9.5 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -2
- data/README.md +22 -10
- data/lib/hocon.rb +9 -3
- data/lib/hocon/config_factory.rb +4 -0
- data/lib/hocon/config_value_factory.rb +13 -2
- data/lib/hocon/impl/config_reference.rb +5 -2
- data/lib/hocon/impl/simple_config_origin.rb +1 -1
- data/spec/fixtures/parse_render/example1/input.conf +21 -0
- data/spec/fixtures/parse_render/example1/output.conf +26 -0
- data/spec/fixtures/parse_render/example1/output_nocomments.conf +17 -0
- data/spec/fixtures/parse_render/example2/input.conf +10 -0
- data/spec/fixtures/parse_render/example2/output.conf +17 -0
- data/spec/fixtures/parse_render/example2/output_nocomments.conf +17 -0
- data/spec/fixtures/parse_render/example3/input.conf +2 -0
- data/spec/fixtures/parse_render/example3/output.conf +2 -0
- data/spec/fixtures/parse_render/example4/input.json +6 -0
- data/spec/fixtures/parse_render/example4/output.conf +6 -0
- data/spec/fixtures/test_utils/resources/bom.conf +2 -0
- data/spec/fixtures/test_utils/resources/cycle.conf +1 -0
- data/spec/fixtures/test_utils/resources/file-include.conf +5 -0
- data/spec/fixtures/test_utils/resources/include-from-list.conf +4 -0
- data/spec/fixtures/test_utils/resources/subdir/bar.conf +1 -0
- data/spec/fixtures/test_utils/resources/subdir/baz.conf +1 -0
- data/spec/fixtures/test_utils/resources/subdir/foo.conf +5 -0
- data/spec/fixtures/test_utils/resources/test01.conf +80 -0
- data/spec/fixtures/test_utils/resources/test01.json +4 -0
- data/spec/fixtures/test_utils/resources/test03.conf +36 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/test_utils.rb +757 -0
- data/spec/unit/typesafe/config/concatenation_spec.rb +417 -0
- data/spec/unit/typesafe/config/conf_parser_spec.rb +822 -0
- data/spec/unit/typesafe/config/config_document_parser_spec.rb +494 -0
- data/spec/unit/typesafe/config/config_document_spec.rb +576 -0
- data/spec/unit/typesafe/config/config_factory_spec.rb +120 -0
- data/spec/unit/typesafe/config/config_node_spec.rb +552 -0
- data/spec/unit/typesafe/config/config_value_factory_spec.rb +85 -0
- data/spec/unit/typesafe/config/config_value_spec.rb +935 -0
- data/spec/unit/typesafe/config/hocon_spec.rb +54 -0
- data/spec/unit/typesafe/config/path_spec.rb +261 -0
- data/spec/unit/typesafe/config/public_api_spec.rb +520 -0
- data/spec/unit/typesafe/config/simple_config_spec.rb +112 -0
- data/spec/unit/typesafe/config/token_spec.rb +188 -0
- data/spec/unit/typesafe/config/tokenizer_spec.rb +801 -0
- metadata +39 -3
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'hocon/config_factory'
|
5
|
+
require 'hocon/config_render_options'
|
6
|
+
require 'hocon/config_error'
|
7
|
+
|
8
|
+
def get_comment_config_hash(config_string)
|
9
|
+
split_config_string = config_string.split("\n")
|
10
|
+
r = Regexp.new('^\s*#')
|
11
|
+
|
12
|
+
previous_string_comment = false
|
13
|
+
hash = {}
|
14
|
+
comment_list = []
|
15
|
+
|
16
|
+
split_config_string.each do |s|
|
17
|
+
if r.match(s)
|
18
|
+
comment_list << s
|
19
|
+
previous_string_comment = true
|
20
|
+
else
|
21
|
+
if previous_string_comment
|
22
|
+
hash[s] = comment_list
|
23
|
+
comment_list = []
|
24
|
+
end
|
25
|
+
previous_string_comment = false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return hash
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Hocon::ConfigFactory do
|
32
|
+
let(:render_options) { Hocon::ConfigRenderOptions.defaults }
|
33
|
+
|
34
|
+
before do
|
35
|
+
render_options.origin_comments = false
|
36
|
+
render_options.json = false
|
37
|
+
end
|
38
|
+
|
39
|
+
shared_examples_for "config_factory_parsing" do
|
40
|
+
let(:input_file) { "#{FIXTURE_DIR}/parse_render/#{example[:name]}/input#{extension}" }
|
41
|
+
let(:output_file) { "#{FIXTURE_DIR}/parse_render/#{example[:name]}/output.conf" }
|
42
|
+
let(:expected) { example[:hash] }
|
43
|
+
let(:reparsed) { Hocon::ConfigFactory.parse_file("#{output_file}") }
|
44
|
+
let(:output) { File.read("#{output_file}") }
|
45
|
+
|
46
|
+
it "should make the config data available as a map" do
|
47
|
+
expect(conf.root.unwrapped).to eq(expected)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should render the config data to a string with comments intact" do
|
51
|
+
rendered_conf = conf.root.render(render_options)
|
52
|
+
rendered_conf_comment_hash = get_comment_config_hash(rendered_conf)
|
53
|
+
output_comment_hash = get_comment_config_hash(output)
|
54
|
+
|
55
|
+
expect(rendered_conf_comment_hash).to eq(output_comment_hash)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should generate the same conf data via re-parsing the rendered output" do
|
59
|
+
expect(reparsed.root.unwrapped).to eq(expected)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "example1" do
|
64
|
+
let(:example) { EXAMPLE1 }
|
65
|
+
let (:extension) { ".conf" }
|
66
|
+
|
67
|
+
context "parsing a HOCON string" do
|
68
|
+
let(:string) { File.open(input_file).read }
|
69
|
+
let(:conf) { Hocon::ConfigFactory.parse_string(string) }
|
70
|
+
include_examples "config_factory_parsing"
|
71
|
+
end
|
72
|
+
|
73
|
+
context "parsing a .conf file" do
|
74
|
+
let(:conf) { Hocon::ConfigFactory.parse_file(input_file) }
|
75
|
+
include_examples "config_factory_parsing"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "example2" do
|
80
|
+
let(:example) { EXAMPLE2 }
|
81
|
+
let (:extension) { ".conf" }
|
82
|
+
|
83
|
+
context "parsing a HOCON string" do
|
84
|
+
let(:string) { File.open(input_file).read }
|
85
|
+
let(:conf) { Hocon::ConfigFactory.parse_string(string) }
|
86
|
+
include_examples "config_factory_parsing"
|
87
|
+
end
|
88
|
+
|
89
|
+
context "parsing a .conf file" do
|
90
|
+
let(:conf) { Hocon::ConfigFactory.parse_file(input_file) }
|
91
|
+
include_examples "config_factory_parsing"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "example3" do
|
96
|
+
let (:example) { EXAMPLE3 }
|
97
|
+
let (:extension) { ".conf" }
|
98
|
+
|
99
|
+
context "loading a HOCON file with substitutions" do
|
100
|
+
let(:conf) { Hocon::ConfigFactory.load_file(input_file) }
|
101
|
+
include_examples "config_factory_parsing"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "example4" do
|
106
|
+
let(:example) { EXAMPLE4 }
|
107
|
+
let (:extension) { ".json" }
|
108
|
+
|
109
|
+
context "parsing a .json file" do
|
110
|
+
let (:conf) { Hocon::ConfigFactory.parse_file(input_file) }
|
111
|
+
include_examples "config_factory_parsing"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "example5" do
|
116
|
+
it "should raise a ConfigParseError when given an invalid .conf file" do
|
117
|
+
expect{Hocon::ConfigFactory.parse_string("abcdefg")}.to raise_error(Hocon::ConfigError::ConfigParseError)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,552 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'hocon'
|
5
|
+
require 'test_utils'
|
6
|
+
|
7
|
+
describe Hocon::Parser::ConfigNode do
|
8
|
+
Tokens = Hocon::Impl::Tokens
|
9
|
+
|
10
|
+
shared_examples_for "single token node test" do
|
11
|
+
it "should render the node with the text of the token" do
|
12
|
+
node = TestUtils.config_node_single_token(token)
|
13
|
+
expect(node.render).to eq(token.token_text)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples_for "key node test" do
|
18
|
+
it "should render the node with the text of the path" do
|
19
|
+
node = TestUtils.config_node_key(path)
|
20
|
+
expect(path).to eq(node.render)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
shared_examples_for "simple value node test" do
|
25
|
+
it "should render the original token text" do
|
26
|
+
node = TestUtils.config_node_simple_value(token)
|
27
|
+
expect(node.render).to eq(token.token_text)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
shared_examples_for "field node test" do
|
32
|
+
it "should properly replace the value of a field node" do
|
33
|
+
key_val_node = TestUtils.node_key_value_pair(key, value)
|
34
|
+
expect(key_val_node.render).to eq("#{key.render} : #{value.render}")
|
35
|
+
expect(key_val_node.path.render).to eq(key.render)
|
36
|
+
expect(key_val_node.value.render).to eq(value.render)
|
37
|
+
|
38
|
+
new_key_val_node = key_val_node.replace_value(new_value)
|
39
|
+
expect(new_key_val_node.render).to eq("#{key.render} : #{new_value.render}")
|
40
|
+
expect(new_key_val_node.value.render).to eq(new_value.render)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
shared_examples_for "top level value replace test" do
|
45
|
+
it "should replace a value in a ConfigNodeObject" do
|
46
|
+
complex_node_children = [TestUtils.node_open_brace,
|
47
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key(key), value),
|
48
|
+
TestUtils.node_close_brace]
|
49
|
+
complex_node = TestUtils.config_node_object(complex_node_children)
|
50
|
+
new_node = complex_node.set_value_on_path(key, new_value)
|
51
|
+
orig_text = "{#{key} : #{value.render}}"
|
52
|
+
final_text = "{#{key} : #{new_value.render}}"
|
53
|
+
|
54
|
+
expect(complex_node.render).to eq(orig_text)
|
55
|
+
expect(new_node.render).to eq(final_text)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
shared_examples_for "replace duplicates test" do
|
60
|
+
it "should remove duplicates of a key when setting a value" do
|
61
|
+
key = TestUtils.config_node_key('foo')
|
62
|
+
key_val_pair_1 = TestUtils.node_key_value_pair(key, value1)
|
63
|
+
key_val_pair_2 = TestUtils.node_key_value_pair(key, value2)
|
64
|
+
key_val_pair_3 = TestUtils.node_key_value_pair(key, value3)
|
65
|
+
complex_node = TestUtils.config_node_object([key_val_pair_1, key_val_pair_2, key_val_pair_3])
|
66
|
+
orig_text = "#{key_val_pair_1.render}#{key_val_pair_2.render}#{key_val_pair_3.render}"
|
67
|
+
final_text = "#{key.render} : 15"
|
68
|
+
|
69
|
+
expect(complex_node.render).to eq(orig_text)
|
70
|
+
expect(complex_node.set_value_on_path("foo", TestUtils.node_int(15)).render).to eq(final_text)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
shared_examples_for "non existent path test" do
|
75
|
+
it "should properly add a key/value pair if the key does not exist in the object" do
|
76
|
+
node = TestUtils.config_node_object([TestUtils.node_key_value_pair(TestUtils.config_node_key("bar"), TestUtils.node_int(15))])
|
77
|
+
expect(node.render).to eq('bar : 15')
|
78
|
+
new_node = node.set_value_on_path('foo', value)
|
79
|
+
final_text = "bar : 15, foo : #{value.render}"
|
80
|
+
expect(new_node.render).to eq(final_text)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
########################
|
85
|
+
# ConfigNodeSingleToken
|
86
|
+
########################
|
87
|
+
context "create basic config node" do
|
88
|
+
# Ensure a ConfigNodeSingleToken can handle all its required token types
|
89
|
+
context "start of file" do
|
90
|
+
let(:token) { Tokens::START }
|
91
|
+
include_examples "single token node test"
|
92
|
+
end
|
93
|
+
|
94
|
+
context "end of file" do
|
95
|
+
let(:token) { Tokens::EOF }
|
96
|
+
include_examples "single token node test"
|
97
|
+
end
|
98
|
+
|
99
|
+
context "{" do
|
100
|
+
let (:token) { Tokens::OPEN_CURLY }
|
101
|
+
include_examples "single token node test"
|
102
|
+
end
|
103
|
+
|
104
|
+
context "}" do
|
105
|
+
let (:token) { Tokens::CLOSE_CURLY }
|
106
|
+
include_examples "single token node test"
|
107
|
+
end
|
108
|
+
|
109
|
+
context "[" do
|
110
|
+
let (:token) { Tokens::OPEN_SQUARE }
|
111
|
+
include_examples "single token node test"
|
112
|
+
end
|
113
|
+
|
114
|
+
context "]" do
|
115
|
+
let (:token) { Tokens::CLOSE_SQUARE }
|
116
|
+
include_examples "single token node test"
|
117
|
+
end
|
118
|
+
|
119
|
+
context "," do
|
120
|
+
let (:token) { Tokens::COMMA }
|
121
|
+
include_examples "single token node test"
|
122
|
+
end
|
123
|
+
|
124
|
+
context "=" do
|
125
|
+
let (:token) { Tokens::EQUALS }
|
126
|
+
include_examples "single token node test"
|
127
|
+
end
|
128
|
+
|
129
|
+
context ":" do
|
130
|
+
let (:token) { Tokens::COLON }
|
131
|
+
include_examples "single token node test"
|
132
|
+
end
|
133
|
+
|
134
|
+
context "+=" do
|
135
|
+
let (:token) { Tokens::PLUS_EQUALS }
|
136
|
+
include_examples "single token node test"
|
137
|
+
end
|
138
|
+
|
139
|
+
context "unquoted text" do
|
140
|
+
let (:token) { TestUtils.token_unquoted(' ') }
|
141
|
+
include_examples "single token node test"
|
142
|
+
end
|
143
|
+
|
144
|
+
context "ignored whitespace" do
|
145
|
+
let (:token) { TestUtils.token_whitespace(' ') }
|
146
|
+
include_examples "single token node test"
|
147
|
+
end
|
148
|
+
|
149
|
+
context '\n' do
|
150
|
+
let (:token) { TestUtils.token_line(1) }
|
151
|
+
include_examples "single token node test"
|
152
|
+
end
|
153
|
+
|
154
|
+
context "double slash comment" do
|
155
|
+
let (:token) { TestUtils.token_comment_double_slash(" this is a double slash comment ") }
|
156
|
+
include_examples "single token node test"
|
157
|
+
end
|
158
|
+
|
159
|
+
context "hash comment" do
|
160
|
+
let (:token) { TestUtils.token_comment_hash(" this is a hash comment ") }
|
161
|
+
include_examples "single token node test"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
####################
|
166
|
+
# ConfigNodeSetting
|
167
|
+
####################
|
168
|
+
context "create config node setting" do
|
169
|
+
# Ensure a ConfigNodeSetting can handle the normal key types
|
170
|
+
context "unquoted key" do
|
171
|
+
let (:path) { "foo" }
|
172
|
+
include_examples "key node test"
|
173
|
+
end
|
174
|
+
|
175
|
+
context "quoted_key" do
|
176
|
+
let (:path) { "\"Hello I am a key how are you today\"" }
|
177
|
+
include_examples "key node test"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "path node subpath" do
|
182
|
+
it "should produce correct subpaths of path nodes with subpath method" do
|
183
|
+
orig_path = 'a.b.c."@$%#@!@#$"."".1234.5678'
|
184
|
+
path_node = TestUtils.config_node_key(orig_path)
|
185
|
+
|
186
|
+
expect(path_node.render).to eq(orig_path)
|
187
|
+
expect(path_node.sub_path(2).render).to eq('c."@$%#@!@#$"."".1234.5678')
|
188
|
+
expect(path_node.sub_path(6).render).to eq('5678')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
########################
|
193
|
+
# ConfigNodeSimpleValue
|
194
|
+
########################
|
195
|
+
context "create config node simple value" do
|
196
|
+
context "integer" do
|
197
|
+
let (:token) { TestUtils.token_int(10) }
|
198
|
+
include_examples "simple value node test"
|
199
|
+
end
|
200
|
+
|
201
|
+
context "double" do
|
202
|
+
let (:token) { TestUtils.token_double(3.14159) }
|
203
|
+
include_examples "simple value node test"
|
204
|
+
end
|
205
|
+
|
206
|
+
context "false" do
|
207
|
+
let (:token) { TestUtils.token_false }
|
208
|
+
include_examples "simple value node test"
|
209
|
+
end
|
210
|
+
|
211
|
+
context "true" do
|
212
|
+
let (:token) { TestUtils.token_true }
|
213
|
+
include_examples "simple value node test"
|
214
|
+
end
|
215
|
+
|
216
|
+
context "null" do
|
217
|
+
let (:token) { TestUtils.token_null }
|
218
|
+
include_examples "simple value node test"
|
219
|
+
end
|
220
|
+
|
221
|
+
context "quoted text" do
|
222
|
+
let (:token) { TestUtils.token_string("Hello my name is string") }
|
223
|
+
include_examples "simple value node test"
|
224
|
+
end
|
225
|
+
|
226
|
+
context "unquoted text" do
|
227
|
+
let (:token) { TestUtils.token_unquoted("mynameisunquotedstring") }
|
228
|
+
include_examples "simple value node test"
|
229
|
+
end
|
230
|
+
|
231
|
+
context "key substitution" do
|
232
|
+
let (:token) { TestUtils.token_key_substitution("c.d") }
|
233
|
+
include_examples "simple value node test"
|
234
|
+
end
|
235
|
+
|
236
|
+
context "optional substitution" do
|
237
|
+
let (:token) { TestUtils.token_optional_substitution(TestUtils.token_unquoted("x.y")) }
|
238
|
+
include_examples "simple value node test"
|
239
|
+
end
|
240
|
+
|
241
|
+
context "substitution" do
|
242
|
+
let (:token) { TestUtils.token_substitution(TestUtils.token_unquoted("a.b")) }
|
243
|
+
include_examples "simple value node test"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
####################
|
248
|
+
# ConfigNodeField
|
249
|
+
####################
|
250
|
+
context "create ConfigNodeField" do
|
251
|
+
let (:key) { TestUtils.config_node_key('"abc"') }
|
252
|
+
let (:value) { TestUtils.node_int(123) }
|
253
|
+
|
254
|
+
context "supports quoted keys" do
|
255
|
+
let (:new_value) { TestUtils.node_int(245) }
|
256
|
+
include_examples "field node test"
|
257
|
+
end
|
258
|
+
|
259
|
+
context "supports unquoted keys" do
|
260
|
+
let (:key) { TestUtils.config_node_key('abc') }
|
261
|
+
let (:new_value) { TestUtils.node_int(245) }
|
262
|
+
include_examples "field node test"
|
263
|
+
end
|
264
|
+
|
265
|
+
context "can replace a simple value with a different type of simple value" do
|
266
|
+
let (:new_value) { TestUtils.node_string('I am a string') }
|
267
|
+
include_examples "field node test"
|
268
|
+
end
|
269
|
+
|
270
|
+
context "can replace a simple value with a complex value" do
|
271
|
+
let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace]) }
|
272
|
+
include_examples "field node test"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
####################
|
277
|
+
# Node Replacement
|
278
|
+
####################
|
279
|
+
context "replace nodes" do
|
280
|
+
let (:key) { "foo" }
|
281
|
+
array = TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_int(10), TestUtils.node_space, TestUtils.node_comma,
|
282
|
+
TestUtils.node_space, TestUtils.node_int(15), TestUtils.node_close_bracket])
|
283
|
+
nested_map = TestUtils.config_node_object([TestUtils.node_open_brace,
|
284
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key("abc"),
|
285
|
+
TestUtils.config_node_simple_value(TestUtils.token_string("a string"))),
|
286
|
+
TestUtils.node_close_brace])
|
287
|
+
|
288
|
+
context "replace an integer with an integer" do
|
289
|
+
let (:value) { TestUtils.node_int(10) }
|
290
|
+
let (:new_value) { TestUtils.node_int(15) }
|
291
|
+
include_examples "top level value replace test"
|
292
|
+
end
|
293
|
+
|
294
|
+
context "replace a double with an integer" do
|
295
|
+
let (:value) { TestUtils.node_double(3.14159) }
|
296
|
+
let (:new_value) { TestUtils.node_int(10000) }
|
297
|
+
include_examples "top level value replace test"
|
298
|
+
end
|
299
|
+
|
300
|
+
context "replace false with true" do
|
301
|
+
let (:value) { TestUtils.node_false }
|
302
|
+
let (:new_value) { TestUtils.node_true }
|
303
|
+
include_examples "top level value replace test"
|
304
|
+
end
|
305
|
+
|
306
|
+
context "replace true with null" do
|
307
|
+
let (:value) { TestUtils.node_true }
|
308
|
+
let (:new_value) { TestUtils.node_null }
|
309
|
+
include_examples "top level value replace test"
|
310
|
+
end
|
311
|
+
|
312
|
+
context "replace null with a string" do
|
313
|
+
let (:value) { TestUtils.node_null }
|
314
|
+
let (:new_value) { TestUtils.node_string("Hello my name is string") }
|
315
|
+
include_examples "top level value replace test"
|
316
|
+
end
|
317
|
+
|
318
|
+
context "replace a string with unquoted text" do
|
319
|
+
let (:value) { TestUtils.node_string("Hello my name is string") }
|
320
|
+
let (:new_value) { TestUtils.node_unquoted_text("mynameisunquotedstring") }
|
321
|
+
include_examples "top level value replace test"
|
322
|
+
end
|
323
|
+
|
324
|
+
context "replace unquoted text with a key substitution" do
|
325
|
+
let (:value) { TestUtils.node_unquoted_text("mynameisunquotedstring") }
|
326
|
+
let (:new_value) { TestUtils.node_key_substitution("c.d") }
|
327
|
+
include_examples "top level value replace test"
|
328
|
+
end
|
329
|
+
|
330
|
+
context "replace int with an optional substitution" do
|
331
|
+
let (:value) { TestUtils.node_int(10) }
|
332
|
+
let (:new_value) { TestUtils.node_optional_substitution(TestUtils.token_unquoted("x.y")) }
|
333
|
+
include_examples "top level value replace test"
|
334
|
+
end
|
335
|
+
|
336
|
+
context "replace int with a substitution" do
|
337
|
+
let (:value) { TestUtils.node_int(10) }
|
338
|
+
let (:new_value) { TestUtils.node_substitution(TestUtils.token_unquoted("a.b")) }
|
339
|
+
include_examples "top level value replace test"
|
340
|
+
end
|
341
|
+
|
342
|
+
context "replace substitution with an int" do
|
343
|
+
let (:value) { TestUtils.node_substitution(TestUtils.token_unquoted("a.b")) }
|
344
|
+
let (:new_value) { TestUtils.node_int(10) }
|
345
|
+
include_examples "top level value replace test"
|
346
|
+
end
|
347
|
+
|
348
|
+
context "ensure arrays can be replaced" do
|
349
|
+
context "can replace a simple value with an array" do
|
350
|
+
let (:value) { TestUtils.node_int(10) }
|
351
|
+
let (:new_value) { array }
|
352
|
+
include_examples "top level value replace test"
|
353
|
+
end
|
354
|
+
|
355
|
+
context "can replace an array with a simple value" do
|
356
|
+
let (:value) { array }
|
357
|
+
let (:new_value) { TestUtils.node_int(10) }
|
358
|
+
include_examples "top level value replace test"
|
359
|
+
end
|
360
|
+
|
361
|
+
context "can replace an array with another complex value" do
|
362
|
+
let (:value) { array }
|
363
|
+
let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace])}
|
364
|
+
include_examples "top level value replace test"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context "ensure objects can be replaced" do
|
369
|
+
context "can replace an object with a simple value" do
|
370
|
+
let (:value) { nested_map }
|
371
|
+
let (:new_value) { TestUtils.node_int(10) }
|
372
|
+
include_examples "top level value replace test"
|
373
|
+
end
|
374
|
+
|
375
|
+
context "can replace a simple value with an object" do
|
376
|
+
let (:value) { TestUtils.node_int(10) }
|
377
|
+
let (:new_value) { nested_map }
|
378
|
+
include_examples "top level value replace test"
|
379
|
+
end
|
380
|
+
|
381
|
+
context "can replace an array with an object" do
|
382
|
+
let (:value) { array }
|
383
|
+
let (:new_value) { nested_map }
|
384
|
+
include_examples "top level value replace test"
|
385
|
+
end
|
386
|
+
|
387
|
+
context "can replace an object with an array" do
|
388
|
+
let (:value) { nested_map }
|
389
|
+
let (:new_value) { array }
|
390
|
+
include_examples "top level value replace test"
|
391
|
+
end
|
392
|
+
|
393
|
+
context "can replace an object with an empty object" do
|
394
|
+
let (:value) { nested_map }
|
395
|
+
let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace]) }
|
396
|
+
include_examples "top level value replace test"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "ensure concatenations can be replaced" do
|
401
|
+
concatenation = TestUtils.config_node_concatenation([TestUtils.node_int(10), TestUtils.node_space, TestUtils.node_string("Hello")])
|
402
|
+
|
403
|
+
context "can replace a concatenation with a simple value" do
|
404
|
+
let (:value) { concatenation }
|
405
|
+
let (:new_value) { TestUtils.node_int(12) }
|
406
|
+
include_examples "top level value replace test"
|
407
|
+
end
|
408
|
+
|
409
|
+
context "can replace a simple value with a concatenation" do
|
410
|
+
let (:value) { TestUtils.node_int(12) }
|
411
|
+
let (:new_value) { concatenation }
|
412
|
+
include_examples "top level value replace test"
|
413
|
+
end
|
414
|
+
|
415
|
+
context "can replace an object with a concatenation" do
|
416
|
+
let (:value) { nested_map }
|
417
|
+
let (:new_value) { concatenation }
|
418
|
+
include_examples "top level value replace test"
|
419
|
+
end
|
420
|
+
|
421
|
+
context "can replace a concatenation with an object" do
|
422
|
+
let (:value) { concatenation }
|
423
|
+
let (:new_value) { nested_map }
|
424
|
+
include_examples "top level value replace test"
|
425
|
+
end
|
426
|
+
|
427
|
+
context "can replace an array with a concatenation" do
|
428
|
+
let (:value) { array }
|
429
|
+
let (:new_value) { concatenation }
|
430
|
+
include_examples "top level value replace test"
|
431
|
+
end
|
432
|
+
|
433
|
+
context "can replace a concatenation with an array" do
|
434
|
+
let (:value) { concatenation }
|
435
|
+
let (:new_value) { array }
|
436
|
+
include_examples "top level value replace test"
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
context 'ensure a key with format "a.b" will be properly replaced' do
|
441
|
+
let (:key) { 'foo.bar' }
|
442
|
+
let (:value) { TestUtils.node_int(10) }
|
443
|
+
let (:new_value) { nested_map }
|
444
|
+
include_examples "top level value replace test"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
####################
|
449
|
+
# Duplicate Removal
|
450
|
+
####################
|
451
|
+
context "remove duplicates" do
|
452
|
+
empty_map_node = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace])
|
453
|
+
empty_array_node = TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_close_bracket])
|
454
|
+
|
455
|
+
context "duplicates containing simple values will all be removed" do
|
456
|
+
let (:value1) { TestUtils.node_int(10) }
|
457
|
+
let (:value2) { TestUtils.node_true }
|
458
|
+
let (:value3) { TestUtils.node_null }
|
459
|
+
include_examples "replace duplicates test"
|
460
|
+
end
|
461
|
+
|
462
|
+
context "duplicates containing objects will be removed" do
|
463
|
+
let (:value1) { empty_map_node }
|
464
|
+
let (:value2) { empty_map_node }
|
465
|
+
let (:value3) { empty_map_node }
|
466
|
+
include_examples "replace duplicates test"
|
467
|
+
end
|
468
|
+
|
469
|
+
context "duplicates containing arrays will be removed" do
|
470
|
+
let (:value1) { empty_array_node }
|
471
|
+
let (:value2) { empty_array_node }
|
472
|
+
let (:value3) { empty_array_node }
|
473
|
+
include_examples "replace duplicates test"
|
474
|
+
end
|
475
|
+
|
476
|
+
context "duplicates containing a mix of value types will be removed" do
|
477
|
+
let (:value1) { TestUtils.node_int(10) }
|
478
|
+
let (:value2) { empty_map_node }
|
479
|
+
let (:value3) { empty_array_node }
|
480
|
+
include_examples "replace duplicates test"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
#################################
|
485
|
+
# Addition of non-existent paths
|
486
|
+
#################################
|
487
|
+
context "add non existent paths" do
|
488
|
+
context "adding an integer" do
|
489
|
+
let (:value) { TestUtils.node_int(10) }
|
490
|
+
include_examples "non existent path test"
|
491
|
+
end
|
492
|
+
|
493
|
+
context "adding an array" do
|
494
|
+
let (:value) { TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_int(15), TestUtils.node_close_bracket]) }
|
495
|
+
include_examples "non existent path test"
|
496
|
+
end
|
497
|
+
|
498
|
+
context "adding an object" do
|
499
|
+
let (:value) { TestUtils.config_node_object([TestUtils.node_open_brace,
|
500
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key('foo'),
|
501
|
+
TestUtils.node_double(3.14)),
|
502
|
+
TestUtils.node_close_brace]) }
|
503
|
+
include_examples "non existent path test"
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
#################################
|
508
|
+
# Replacement of nested nodes
|
509
|
+
#################################
|
510
|
+
context "replace nested nodes" do
|
511
|
+
orig_text = "foo : bar\nbaz : {\n\t\"abc.def\" : 123\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
|
512
|
+
"def : \"this is a string\"\n\t\tghi : ${\"a.b\"}\n\t}\n}\nbaz.abc.ghi : 52\nbaz.abc.ghi : 53\n}"
|
513
|
+
lowest_level_map = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_line(6), TestUtils.node_whitespace("\t\t"),
|
514
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key("def"), TestUtils.config_node_simple_value(TestUtils.token_string("this is a string"))),
|
515
|
+
TestUtils.node_line(7), TestUtils.node_whitespace("\t\t"),
|
516
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key("ghi"), TestUtils.config_node_simple_value(TestUtils.token_key_substitution("a.b"))),
|
517
|
+
TestUtils.node_line(8), TestUtils.node_whitespace("\t"), TestUtils.node_close_brace])
|
518
|
+
higher_level_map = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_line(2), TestUtils.node_whitespace("\t"),
|
519
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key('"abc.def"'), TestUtils.config_node_simple_value(TestUtils.token_int(123))),
|
520
|
+
TestUtils.node_line(3), TestUtils.node_whitespace("\t"), TestUtils.node_comment_double_slash("This is a comment about the below setting"),
|
521
|
+
TestUtils.node_line(4), TestUtils.node_line(5), TestUtils.node_whitespace("\t"),
|
522
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key("abc"), lowest_level_map), TestUtils.node_line(9), TestUtils.node_close_brace])
|
523
|
+
orig_node = TestUtils.config_node_object([TestUtils.node_key_value_pair(TestUtils.config_node_key("foo"), TestUtils.config_node_simple_value(TestUtils.token_unquoted("bar"))),
|
524
|
+
TestUtils.node_line(1), TestUtils.node_key_value_pair(TestUtils.config_node_key('baz'), higher_level_map), TestUtils.node_line(10),
|
525
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key('baz.abc.ghi'), TestUtils.config_node_simple_value(TestUtils.token_int(52))),
|
526
|
+
TestUtils.node_line(11),
|
527
|
+
TestUtils.node_key_value_pair(TestUtils.config_node_key('baz.abc.ghi'), TestUtils.config_node_simple_value(TestUtils.token_int(53))),
|
528
|
+
TestUtils.node_line(12), TestUtils.node_close_brace])
|
529
|
+
it "should properly render the original node" do
|
530
|
+
expect(orig_node.render).to eq(orig_text)
|
531
|
+
end
|
532
|
+
|
533
|
+
it "should properly replae values in the original node" do
|
534
|
+
final_text = "foo : bar\nbaz : {\n\t\"abc.def\" : true\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
|
535
|
+
"def : false\n\t\t\n\t\t\"this.does.not.exist@@@+$#\" : {\n\t\t end : doesnotexist\n\t\t}\n\t}\n}\n\nbaz.abc.ghi : randomunquotedString\n}"
|
536
|
+
|
537
|
+
# Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
|
538
|
+
new_node = orig_node.set_value_on_path('baz."abc.def"', TestUtils.config_node_simple_value(TestUtils.token_true))
|
539
|
+
new_node = new_node.set_value_on_path('baz.abc.def', TestUtils.config_node_simple_value(TestUtils.token_false))
|
540
|
+
|
541
|
+
# Repeats are removed from nested maps
|
542
|
+
new_node = new_node.set_value_on_path('baz.abc.ghi', TestUtils.config_node_simple_value(TestUtils.token_unquoted('randomunquotedString')))
|
543
|
+
|
544
|
+
# Missing paths are added to the top level if they don't appear anywhere, including in nested maps
|
545
|
+
new_node = new_node.set_value_on_path('baz.abc."this.does.not.exist@@@+$#".end', TestUtils.config_node_simple_value(TestUtils.token_unquoted('doesnotexist')))
|
546
|
+
|
547
|
+
# The above operations cause the resultant map to be rendered properly
|
548
|
+
expect(new_node.render).to eq(final_text)
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
end
|