/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.config;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static io.datakernel.config.Config.ROOT;
import static io.datakernel.config.ConfigConverters.ofString;
import static org.junit.Assert.*;
public class ConfigTest {
@Rule
public ExpectedException expected = ExpectedException.none();
@Test
public void rootConfigDoesNotHaveKey() {
Config root = Config.create();
assertEquals("", root.getKey());
}
@Test
public void configShouldStoreInfoAboutChildConfigs() {
Config root = Config.create();
root.set("name1", "value1");
root.set("name2", "value2");
assertEquals("value1", root.getChild("name1").get(ofString(), ROOT));
assertEquals("value2", root.getChild("name2").get(ofString(), ROOT));
}
@Test
public void configShouldProperlySplitChildKey() {
Config root = Config.create();
root.set("base.sub1.sub2", "value");
Config base = (Config) root.getChild("base");
Config sub1 = (Config) base.getChild("sub1");
Config sub2 = (Config) sub1.getChild("sub2");
assertNull(root.get());
assertNull(base.get());
assertNull(sub1.get());
assertEquals("value", sub2.get());
}
@Test
public void configShouldUpdateValueIfKeyWasAlreadyUsed() {
Config root = Config.create();
root.set("key1", "value1");
root.set("key1", "value2");
assertEquals("value2", root.getChild("key1").get(ofString(), ROOT));
}
@Test
public void testConfigWithValueCanHaveNoChildren() {
Properties properties = new Properties();
properties.setProperty("a.b.c", "a.b.c");
Config config = Config.ofProperties(properties);
config.get(ofString(), "a.b.c.d", "a.b.c.d");
expected.expect(IllegalStateException.class);
config.get(ofString(), "a.b.c");
}
@Ignore("stress test to find out whether config works correctly in multithreaded environment")
@Test
public void testStressSynchronizedGetKey() {
// IllegalStateException is ok, when adding default value
ExecutorService executor = Executors.newFixedThreadPool(200);
final List<String> existingPaths = new ArrayList<>();
final Random random = new Random(2L);
Properties properties = createRandomProperties(random, existingPaths, 100000);
final Config config = Config.ofProperties(properties);
final ConcurrentHashMap<String, Throwable> exceptions = new ConcurrentHashMap<>();
for (int i = 0; i < 16000; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
String path = existingPaths.get(random.nextInt(existingPaths.size()));
try {
config.get(ofString(), path, "DefaultValue");
} catch (Exception e) {
e.printStackTrace();
exceptions.put(path, e);
}
}
});
}
executor.shutdown();
assertTrue(exceptions.isEmpty());
}
private Properties createRandomProperties(Random random, Collection<String> existingPaths, int n) {
Properties properties = new Properties();
List<Character> alphabet = new ArrayList<>();
int maxPathLength = 5;
for (int i = 65; i <= 90; i++) {
alphabet.add((char) i);
}
int fails = 0;
for (int i = 0; i < n; ) {
String path = "";
int length = random.nextInt(maxPathLength) + 1;
for (int j = 0; j < length; j++) {
path += alphabet.get(random.nextInt(alphabet.size()));
if (j != (length - 1)) {
path += ".";
}
}
boolean exists = false;
for (String s : existingPaths) {
if (s.startsWith(path) || path.startsWith(s)) {
exists = true;
break;
}
}
existingPaths.add(path);
if (!exists) {
if (random.nextBoolean()) {
properties.setProperty(path, path);
}
i++;
fails = 0;
} else {
if (++fails > 10) {
break;
}
}
}
return properties;
}
}