1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
21 import static org.junit.jupiter.api.Assertions.assertNotSame;
22 import static org.junit.jupiter.api.Assertions.assertSame;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.verifyNoMoreInteractions;
28 import static org.mockito.Mockito.when;
29
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.NoSuchElementException;
36 import java.util.Set;
37
38 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
39 import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
40 import org.apache.commons.configuration2.convert.ListDelimiterHandler;
41 import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
42 import org.apache.commons.configuration2.tree.ImmutableNode;
43 import org.apache.commons.configuration2.tree.InMemoryNodeModel;
44 import org.apache.commons.configuration2.tree.NodeSelector;
45 import org.apache.commons.configuration2.tree.NodeStructureHelper;
46 import org.apache.commons.configuration2.tree.TrackedNodeModel;
47 import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50
51
52
53
54 public class TestSubnodeConfiguration {
55
56
57 private static final String SUB_KEY = "tables.table(0)";
58
59
60 private static final NodeSelector SELECTOR = new NodeSelector(SUB_KEY);
61
62
63
64
65
66
67
68 private static void appendTree(final BaseHierarchicalConfiguration configuration, final ImmutableNode root) {
69 configuration.addNodes(null, Collections.singleton(root));
70 }
71
72
73
74
75
76
77 private static BaseHierarchicalConfiguration setUpParentConfig() {
78 final BaseHierarchicalConfiguration conf = new BaseHierarchicalConfiguration();
79 appendTree(conf, NodeStructureHelper.ROOT_TABLES_TREE);
80 return conf;
81 }
82
83
84 private BaseHierarchicalConfiguration parent;
85
86
87 private SubnodeConfiguration config;
88
89
90
91
92
93
94 private void checkInterpolationFromConfigurationAt(final boolean withUpdates) {
95 parent.addProperty("base.dir", "/home/foo");
96 parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
97 parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2");
98 parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3");
99
100 final Configuration sub = parent.configurationAt("test.absolute.dir", withUpdates);
101 for (int i = 1; i < 4; i++) {
102 assertEquals("/home/foo/path" + i, parent.getString("test.absolute.dir.dir" + i));
103 assertEquals("/home/foo/path" + i, sub.getString("dir" + i));
104 }
105 }
106
107
108
109
110 private void checkSubConfigContent() {
111 assertEquals(NodeStructureHelper.table(0), config.getString("name"));
112 final List<Object> fields = config.getList("fields.field.name");
113
114 final List<String> expected = new ArrayList<>();
115 for (int i = 0; i < NodeStructureHelper.fieldsLength(0); i++) {
116 expected.add(NodeStructureHelper.field(0, i));
117 }
118 assertEquals(expected, fields);
119 }
120
121 @BeforeEach
122 public void setUp() throws Exception {
123 parent = setUpParentConfig();
124 }
125
126
127
128
129 private void setUpSubnodeConfig() {
130 setUpSubnodeConfig(SUB_KEY);
131 }
132
133
134
135
136
137
138 private void setUpSubnodeConfig(final String key) {
139 config = (SubnodeConfiguration) parent.configurationAt(key, true);
140 }
141
142
143
144
145
146
147
148 private TrackedNodeModel setUpTrackedModel(final NodeSelector selector) {
149 final InMemoryNodeModel parentModel = (InMemoryNodeModel) parent.getModel();
150 parentModel.trackNode(selector, parent);
151 return new TrackedNodeModel(parent, selector, true);
152 }
153
154
155
156
157 @Test
158 void testAddProperty() {
159 setUpSubnodeConfig();
160 config.addProperty("[@table-type]", "test");
161 assertEquals("test", parent.getString("tables.table(0)[@table-type]"));
162
163 parent.addProperty("tables.table(0).fields.field(-1).name", "newField");
164 final List<Object> fields = config.getList("fields.field.name");
165 assertEquals(NodeStructureHelper.fieldsLength(0) + 1, fields.size());
166 assertEquals("newField", fields.get(fields.size() - 1));
167 }
168
169
170
171
172 @Test
173 void testClone() {
174 setUpSubnodeConfig();
175 final SubnodeConfiguration copy = (SubnodeConfiguration) config.clone();
176 assertNotSame(config.getModel(), copy.getModel());
177 final TrackedNodeModel subModel = (TrackedNodeModel) copy.getModel();
178 assertEquals(SELECTOR, subModel.getSelector());
179 final InMemoryNodeModel parentModel = (InMemoryNodeModel) parent.getModel();
180 assertEquals(parentModel, subModel.getParentModel());
181
182
183 parentModel.untrackNode(SELECTOR);
184 parentModel.untrackNode(SELECTOR);
185 assertTrue(subModel.isReleaseTrackedNodeOnFinalize());
186 }
187
188
189
190
191 @Test
192 void testClose() {
193 final TrackedNodeModel model = mock(TrackedNodeModel.class);
194
195 when(model.getSelector()).thenReturn(SELECTOR);
196
197 final SubnodeConfiguration config = new SubnodeConfiguration(parent, model);
198 config.close();
199
200 verify(model).getSelector();
201 verify(model).close();
202 verifyNoMoreInteractions(model);
203 }
204
205
206
207
208 @Test
209 void testConfiguarationAtNoUpdates() {
210 setUpSubnodeConfig();
211 final HierarchicalConfiguration<ImmutableNode> sub2 = config.configurationAt("fields.field(1)");
212 assertEquals(NodeStructureHelper.field(0, 1), sub2.getString("name"));
213 parent.setProperty("tables.table(0).fields.field(1).name", "otherName");
214 assertEquals(NodeStructureHelper.field(0, 1), sub2.getString("name"));
215 }
216
217
218
219
220 @Test
221 void testConfigurationAtWithUpdateSupport() {
222 setUpSubnodeConfig();
223 final SubnodeConfiguration sub2 = (SubnodeConfiguration) config.configurationAt("fields.field(1)", true);
224 assertEquals(NodeStructureHelper.field(0, 1), sub2.getString("name"));
225 assertEquals(config, sub2.getParent());
226 }
227
228
229
230
231 @Test
232 void testGetKeys() {
233 setUpSubnodeConfig();
234 final Set<String> keys = new HashSet<>(ConfigurationAssert.keysToList(config));
235 assertEquals(new HashSet<>(Arrays.asList("name", "fields.field.name")), keys);
236 }
237
238
239
240
241 @Test
242 void testGetNodeModel() {
243 setUpSubnodeConfig();
244 final InMemoryNodeModel nodeModel = config.getNodeModel();
245
246 assertEquals("table", nodeModel.getNodeHandler().getRootNode().getNodeName());
247 }
248
249
250
251
252 @Test
253 void testGetProperties() {
254 setUpSubnodeConfig();
255 checkSubConfigContent();
256 }
257
258
259
260
261 @Test
262 void testInitSubNodeConfig() {
263 setUpSubnodeConfig();
264 assertSame(NodeStructureHelper.nodeForKey(parent.getModel().getNodeHandler().getRootNode(), "tables/table(0)"),
265 config.getModel().getNodeHandler().getRootNode());
266 assertSame(parent, config.getParent());
267 }
268
269
270
271
272 @Test
273 void testInitSubNodeConfigWithNullNode() {
274 assertThrows(IllegalArgumentException.class, () -> new SubnodeConfiguration(parent, null));
275 }
276
277
278
279
280 @Test
281 void testInitSubNodeConfigWithNullParent() {
282 final TrackedNodeModel model = setUpTrackedModel(SELECTOR);
283 assertThrows(IllegalArgumentException.class, () -> new SubnodeConfiguration(null, model));
284 }
285
286
287
288
289 @Test
290 void testInterpolation() {
291 parent.addProperty("tablespaces.tablespace.name", "default");
292 parent.addProperty("tablespaces.tablespace(-1).name", "test");
293 parent.addProperty("tables.table(0).tablespace", "${tablespaces.tablespace(0).name}");
294 assertEquals("default", parent.getString("tables.table(0).tablespace"));
295
296 setUpSubnodeConfig();
297 assertEquals("default", config.getString("tablespace"));
298 }
299
300
301
302
303
304 @Test
305 void testInterpolationFromConfigurationAtNoUpdateSupport() {
306 checkInterpolationFromConfigurationAt(false);
307 }
308
309
310
311
312 @Test
313 void testInterpolationFromConfigurationAtWithUpdateSupport() {
314 checkInterpolationFromConfigurationAt(true);
315 }
316
317
318
319
320 @Test
321 void testInterpolator() {
322 parent.addProperty("tablespaces.tablespace.name", "default");
323 parent.addProperty("tablespaces.tablespace(-1).name", "test");
324
325 setUpSubnodeConfig();
326 InterpolationTestHelper.testGetInterpolator(config);
327 }
328
329
330
331
332 @Test
333 void testLocalInterpolationFromConfigurationAt() {
334 parent.addProperty("base.dir", "/home/foo");
335 parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
336 parent.addProperty("test.absolute.dir.dir2", "${dir1}");
337
338 final Configuration sub = parent.configurationAt("test.absolute.dir");
339 assertEquals("/home/foo/path1", sub.getString("dir1"));
340 assertEquals("/home/foo/path1", sub.getString("dir2"));
341 }
342
343 @Test
344 void testLocalLookupsInInterpolatorAreInherited() {
345 parent.addProperty("tablespaces.tablespace.name", "default");
346 parent.addProperty("tablespaces.tablespace(-1).name", "test");
347 parent.addProperty("tables.table(0).var", "${brackets:x}");
348
349 final ConfigurationInterpolator interpolator = parent.getInterpolator();
350 interpolator.registerLookup("brackets", key -> "(" + key + ")");
351 setUpSubnodeConfig();
352 assertEquals("(x)", config.getString("var", ""));
353 }
354
355
356
357
358
359 @Test
360 void testParentChangeDetach() {
361 setUpSubnodeConfig();
362 parent.clear();
363 checkSubConfigContent();
364 }
365
366
367
368
369
370 @Test
371 void testParentChangeDetatchException() {
372 setUpSubnodeConfig();
373 parent.setExpressionEngine(new XPathExpressionEngine());
374 parent.addProperty("newProp", "value");
375 checkSubConfigContent();
376 }
377
378
379
380
381 @Test
382 void testSetExpressionEngine() {
383 parent.setExpressionEngine(new XPathExpressionEngine());
384 setUpSubnodeConfig("tables/table[1]");
385 assertEquals(NodeStructureHelper.field(0, 1), config.getString("fields/field[2]/name"));
386 final Set<String> keys = ConfigurationAssert.keysToSet(config);
387 assertEquals(new HashSet<>(Arrays.asList("name", "fields/field/name")), keys);
388 config.setExpressionEngine(null);
389 assertInstanceOf(XPathExpressionEngine.class, parent.getExpressionEngine());
390 }
391
392
393
394
395 @Test
396 void testSetListDelimiterHandler() {
397 final ListDelimiterHandler handler1 = new DefaultListDelimiterHandler('/');
398 final ListDelimiterHandler handler2 = new DefaultListDelimiterHandler(';');
399 parent.setListDelimiterHandler(handler1);
400 setUpSubnodeConfig();
401 parent.setListDelimiterHandler(handler2);
402 assertEquals(handler1, config.getListDelimiterHandler());
403 config.addProperty("newProp", "test1,test2/test3");
404 assertEquals("test1,test2", parent.getString("tables.table(0).newProp"));
405 config.setListDelimiterHandler(DisabledListDelimiterHandler.INSTANCE);
406 assertEquals(handler2, parent.getListDelimiterHandler());
407 }
408
409
410
411
412
413 @Test
414 void testSetProperty() {
415 setUpSubnodeConfig();
416 config.setProperty(null, "testTable");
417 config.setProperty("name", NodeStructureHelper.table(0) + "_tested");
418 assertEquals("testTable", parent.getString("tables.table(0)"));
419 assertEquals(NodeStructureHelper.table(0) + "_tested", parent.getString("tables.table(0).name"));
420
421 parent.setProperty("tables.table(0).fields.field(1).name", "testField");
422 assertEquals("testField", config.getString("fields.field(1).name"));
423 }
424
425
426
427
428 @Test
429 void testSetThrowExceptionOnMissing() {
430 parent.setThrowExceptionOnMissing(true);
431 setUpSubnodeConfig();
432 assertTrue(config.isThrowExceptionOnMissing());
433 assertThrows(NoSuchElementException.class, () -> config.getString("non existing key"));
434 }
435
436
437
438
439 @Test
440 void testSetThrowExceptionOnMissingAffectsParent() {
441 parent.setThrowExceptionOnMissing(true);
442 setUpSubnodeConfig();
443 config.setThrowExceptionOnMissing(false);
444 assertTrue(parent.isThrowExceptionOnMissing());
445 }
446 }