/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.configuration.internal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.Test; import de.rcenvironment.core.configuration.ConfigurationException; import de.rcenvironment.core.configuration.ConfigurationSegment; import de.rcenvironment.core.configuration.WritableConfigurationSegment; import de.rcenvironment.core.utils.common.TempFileService; import de.rcenvironment.core.utils.common.TempFileServiceAccess; /** * {@link ConfigurationStoreImpl} unit tests. * * @author Robert Mischke */ public class ConfigurationStoreImplTest { private static final String MINIMAL_CONFIG_TEST_FILE_PATH = "/configurationStore/minimal.json"; private static final double DOUBLE_TEST_VALUE = 1.5; private static final String NON_EXISTING_SUB_PATH = "sub"; private TempFileService tempFileService; private File testFile; @SuppressWarnings("unused") private final Log log = LogFactory.getLog(getClass()); /** * Common test setup. * * @throws IOException on uncaught errors */ @Before public void setup() throws IOException { TempFileServiceAccess.setupUnitTestEnvironment(); tempFileService = TempFileServiceAccess.getInstance(); testFile = tempFileService.createTempFileWithFixedFilename("configTest.json"); // log.debug("Using test file " + testFile); } /** * Tests the fallback in case placeholder configuration returned by {@link ConfigurationStoreImpl#createEmptyPlaceholder()}; the most * relevant result is that access to missing keys does not cause errors. * * @throws IOException on uncaught exceptions */ @Test public void testNullValuesFromEmptyPlaceholderConfiguration() throws IOException { ConfigurationSegment rootSegment = setupPlaceholderConfiguration(); assertNullValuesForVariousRelativePaths(rootSegment); // expected behaviour: non-existing sub-segments should be returned, but ConfigurationSegment segment = rootSegment.getSubSegment(NON_EXISTING_SUB_PATH); assertNotNull(segment); assertFalse(segment.isPresentInCurrentConfiguration()); assertNullValuesForVariousRelativePaths(segment); } /** * Tests an empty configuration file; the most relevant result is that access to missing keys does not cause errors. * * @throws IOException on uncaught exceptions */ @Test public void testNullValuesFromEmptyConfigurationFile() throws IOException { ConfigurationSegment rootSegment = setupEmptyFileConfiguration(); assertNullValuesForVariousRelativePaths(rootSegment); // expected behaviour: non-existing sub-segments should be returned, but ConfigurationSegment segment = rootSegment.getSubSegment(NON_EXISTING_SUB_PATH); assertNotNull(segment); assertFalse(segment.isPresentInCurrentConfiguration()); assertNullValuesForVariousRelativePaths(segment); } /** * Tests that default values are applied properly, using a placeholder configuration. * * @throws IOException on uncaught exceptions */ @Test public void testDefaultValueHandlingFromEmptyPlaceholderConfiguration() throws IOException { ConfigurationSegment rootSegment = setupPlaceholderConfiguration(); assertDefaultValueBehaviorForVariousRelativePaths(rootSegment); // expected behaviour: non-existing sub-segments should be returned, but ConfigurationSegment segment = rootSegment.getSubSegment(NON_EXISTING_SUB_PATH); assertNotNull(segment); assertFalse(segment.isPresentInCurrentConfiguration()); assertDefaultValueBehaviorForVariousRelativePaths(segment); } /** * Tests that default values are applied properly, using an empty configuration file. * * @throws IOException on uncaught exceptions */ @Test public void testDefaultValueHandlingFromEmptyConfigurationFile() throws IOException { ConfigurationSegment rootSegment = setupEmptyFileConfiguration(); assertDefaultValueBehaviorForVariousRelativePaths(rootSegment); // expected behaviour: non-existing sub-segments should be returned, but ConfigurationSegment segment = rootSegment.getSubSegment(NON_EXISTING_SUB_PATH); assertNotNull(segment); assertFalse(segment.isPresentInCurrentConfiguration()); assertDefaultValueBehaviorForVariousRelativePaths(segment); } /** * Tests standard hierarchical read operations. * * @throws IOException on uncaught exceptions * @throws ConfigurationException on uncaught exceptions */ @Test public void testBasicNavigation() throws IOException, ConfigurationException { copyResourceToFile(MINIMAL_CONFIG_TEST_FILE_PATH, testFile); ConfigurationStore configStore = new ConfigurationStoreImpl(testFile); ConfigurationSegment rootSegment = configStore.getSnapshotOfRootSegment(); assertTrue(rootSegment.isPresentInCurrentConfiguration()); assertEquals("testValue", rootSegment.getString("general/testStringKey")); assertEquals(Long.valueOf(1), rootSegment.getLong("general/testIntegerKey")); assertEquals(Double.valueOf(5.0), rootSegment.getDouble("general/testFloatKey")); assertEquals(null, rootSegment.getString("general/invalidKey")); ConfigurationSegment subSegment = rootSegment.getSubSegment("general"); assertEquals("testValue", subSegment.getString("testStringKey")); assertEquals(Long.valueOf(1), subSegment.getLong("testIntegerKey")); assertEquals(Double.valueOf(5.0), subSegment.getDouble("testFloatKey")); configStore.update(rootSegment); } /** * Test the basic read/write cycle. * * @throws IOException on uncaught exceptions * @throws ConfigurationException on uncaught exceptions */ @Test public void testConfigurationWriting() throws IOException, ConfigurationException { copyResourceToFile(MINIMAL_CONFIG_TEST_FILE_PATH, testFile); ConfigurationStore configStore = new ConfigurationStoreImpl(testFile); ConfigurationSegment root = configStore.getSnapshotOfRootSegment(); final WritableConfigurationSegment addedSegment = root.getOrCreateWritableSubSegment("added"); addedSegment.setString("stringKey", "stringValue"); configStore.update(root); final String content = FileUtils.readFileToString(testFile); // whitespace-tolerant RegExp test for the new configuration entries assertTrue(content, content.matches("(?s).*\"added\"\\s?: \\{\\s+\"stringKey\"\\s?: \"stringValue\"\\s+\\}.*")); } private ConfigurationSegment setupPlaceholderConfiguration() { testFile.delete(); assertFalse(testFile.exists()); ConfigurationStore configStore = new ConfigurationStoreImpl(testFile); ConfigurationSegment rootSegment = configStore.createEmptyPlaceholder(); assertFalse(rootSegment.isPresentInCurrentConfiguration()); return rootSegment; } private ConfigurationSegment setupEmptyFileConfiguration() throws IOException { FileUtils.write(testFile, "{}"); ConfigurationStore configStore = new ConfigurationStoreImpl(testFile); ConfigurationSegment rootSegment = configStore.getSnapshotOfRootSegment(); assertTrue(rootSegment.isPresentInCurrentConfiguration()); return rootSegment; } private void copyResourceToFile(String resourcePath, File file) throws IOException, FileNotFoundException { InputStream testDataStream = getClass().getResourceAsStream(resourcePath); assertNotNull("Expected test resource not found", testDataStream); try (FileOutputStream fos = new FileOutputStream(file)) { IOUtils.copy(testDataStream, fos); } } private void assertNullValuesForVariousRelativePaths(ConfigurationSegment segment) { assertNull(segment.getString("a")); assertNull(segment.getInteger("a2")); assertNull(segment.getLong("b")); assertNull(segment.getDouble("c")); assertNull(segment.getBoolean("d")); assertNull(segment.getString("e/f")); assertNull(segment.getInteger("g/h")); assertNull(segment.getLong("g/h2")); assertNull(segment.getDouble("i/j")); assertNull(segment.getBoolean("k/l")); } private void assertDefaultValueBehaviorForVariousRelativePaths(ConfigurationSegment segment) { assertEquals("theDefault", segment.getString("a", "theDefault")); assertEquals(Integer.valueOf(3), segment.getInteger("a2", 3)); assertEquals(Long.valueOf(2L), segment.getLong("b", 2L)); assertEquals(Double.valueOf(DOUBLE_TEST_VALUE), segment.getDouble("c", DOUBLE_TEST_VALUE)); assertEquals(Boolean.TRUE, segment.getBoolean("d", true)); assertEquals("theDefault2", segment.getString("x/a", "theDefault2")); assertEquals(Integer.valueOf(2), segment.getInteger("x/a2", 2)); assertEquals(Long.valueOf(3L), segment.getLong("x/b", 3L)); assertEquals(Double.valueOf(DOUBLE_TEST_VALUE), segment.getDouble("x/c", DOUBLE_TEST_VALUE)); assertEquals(Boolean.TRUE, segment.getBoolean("x/d", true)); } }