package io.eguan.configuration; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * 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. * #L% */ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import io.eguan.configuration.AbstractConfigKey; import io.eguan.configuration.AbstractConfigurationContext; import io.eguan.configuration.ConfigValidationException; import io.eguan.configuration.MetaConfiguration; import io.eguan.configuration.ValidationError; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; import org.junit.Test; import org.junit.runners.model.InitializationError; /** * Class for validation test common to all implementations of {@link AbstractConfigurationContext}. * * @author oodrive * @author pwehrle * @author ebredzinski * @author llambert * */ public abstract class ValidConfigurationContext { /** * Helper class including utility methods and common methods to test subclasses of * {@link AbstractConfigurationContext}. * * * @param <T> * the {@link AbstractConfigurationContext} subclass this instance helps to test */ public static abstract class ContextTestHelper<T extends AbstractConfigurationContext> { /** * Proxy helper method to get a given {@link Properties} instance as {@link InputStream}. * * Calls {@link ConfigTestHelper#getPropertiesAsInputStream(Properties)}. * * @param properties * the non-{@code null} {@link Properties} instance to convert * @return an {@link InputStream} providing the exact content of the argument * @throws IOException * if storing fails * @see ConfigTestHelper#getPropertiesAsInputStream(Properties) */ public static InputStream getPropertiesAsInputStream(final Properties properties) throws IOException { return ConfigTestHelper.getPropertiesAsInputStream(properties); } /** * Utility method to allow tests to get values directly parsed by a given {@link AbstractConfigKey}. * * @param key * the {@link AbstractConfigKey} to parse the value for * @param value * a raw String value to be parsed * @return the correctly typed value * @see AbstractConfigKey#parseValue(String) */ public static Object getParsedValue(final AbstractConfigKey key, final String value) { return key.parseValue(value); } /** * Utility method to get a {@link String} representation of an {@link AbstractConfigKey}'s value in a given * {@link MetaConfiguration}. * * @param config * the {@link MetaConfiguration} from which to extract the value * @param key * the {@link AbstractConfigKey} managed by the configuration for which to get the value * @return a {@link String} as provided by {@link AbstractConfigKey#valueToString(Object)} */ public static String getStringValue(final MetaConfiguration config, final AbstractConfigKey key) { return key.valueToString(key.getTypedValue(config)); } /** * Utility method to produce a complete property key mapping used by tests. * * @param context * the {@link AbstractConfigurationContext} to produce the mapping for * @return a complete mapping with all keys returned by {@link AbstractConfigurationContext#getConfigKeys()} */ protected static Map<AbstractConfigKey, String> getPropertyKeyMapping(final AbstractConfigurationContext context) { final HashMap<AbstractConfigKey, String> propertyKeys = new HashMap<AbstractConfigKey, String>(); for (final AbstractConfigKey currKey : context.getConfigKeys()) { propertyKeys.put(currKey, context.getPropertyKey(currKey)); } return propertyKeys; } /** * The target {@link AbstractConfigurationContext context}. */ private final T context; /** * Map of all property keys to be found in the test property file content, indexed by {@link AbstractConfigKey}. */ private final Map<AbstractConfigKey, String> propertyKeys; /** * Constructs the instance, initializing the common context and property key functionalities. * * @param context * the {@link AbstractConfigurationContext} instance to be tested */ public ContextTestHelper(final T context) { this.context = Objects.requireNonNull(context); this.propertyKeys = getPropertyKeyMapping(context); } /** * Gets the default config, i.e. the configuration where all keys with a defined default value are set to that * value. * * Note: required keys without a default value are set to the value provided by {@link #getConfig()} * * @return a complete and valid configuration with a maximum of default values for the tested context */ public final Properties getDefaultConfig() { final Properties result = getConfig(); for (final AbstractConfigKey currKey : context.getConfigKeys()) { if (currKey.hasDefaultValue()) { result.setProperty(context.getPropertyKey(currKey), currKey.valueToString(currKey.getDefaultValue())); } } return result; } /** * A method to be called by JUnit {@link org.junit.BeforeClass} or {@link org.junit.Before} methods to set up * the necessary fixture to test this configuration. * * @throws InitializationError * if setup fails */ public abstract void setUp() throws InitializationError; /** * A method to be called by JUnit {@link org.junit.AfterClass} or {@link org.junit.After} methods to tear down * the fixture set up by {@link #setUp()}. * * @throws InitializationError * if tearing down fails */ public abstract void tearDown() throws InitializationError; /** * Gets the valid configuration to test. * * @return a complete and valid configuration for the tested context */ public abstract Properties getConfig(); /** * Utility method to get the property key of a given {@link AbstractConfigKey} as it appears in the * configuration input or output. * * @param key * the {@link AbstractConfigKey} for which to retrieve the property key * @return the property key or <code>null</code> if it's not managed by the target context */ public final String getPropertyKey(final AbstractConfigKey key) { return propertyKeys.get(key); } /** * Gets the context to test. * * @return the {@link AbstractConfigurationContext} */ public final T getContext() { return this.context; } } /** * Gets the {@link ContextTestHelper} instance used in all tests of this class. * * @return a functional instance of {@link ContextTestHelper} */ public abstract ContextTestHelper<?> getTestHelper(); /** * Tests successful validation of a {@link MetaConfiguration} with * {@link CommonConfigurationContext#validateConfiguration(MetaConfiguration)}. * * @throws ConfigValidationException * if the {@link ContextTestHelper#getConfig() configuration} is invalid. Considered a test failure. * @throws IOException * if reading the {@link InputStream} fails. Not part of this test. * @throws RuntimeException * if creation fails. Not part of this test. */ @Test public final void testValidateConfiguration() throws RuntimeException, IOException, ConfigValidationException { final ContextTestHelper<?> testHelper = getTestHelper(); final AbstractConfigurationContext context = testHelper.getContext(); final MetaConfiguration config = MetaConfiguration.newConfiguration( ContextTestHelper.getPropertiesAsInputStream(testHelper.getConfig()), context); final List<ValidationError> result = context.validateConfiguration(config); assertNotNull(result); assertTrue(result.isEmpty()); } /** * Tests successful validation of a minimal {@link MetaConfiguration} with * {@link CommonConfigurationContext#validateConfiguration(MetaConfiguration)}. * * @throws ConfigValidationException * if the {@link ContextTestHelper#getConfig() configuration} is invalid. Considered a test failure. * @throws IOException * if reading the {@link InputStream} fails. Not part of this test. * @throws RuntimeException * if creation fails. Not part of this test. */ @Test public final void testValidateConfigurationMinimal() throws RuntimeException, IOException, ConfigValidationException { final ContextTestHelper<?> testHelper = getTestHelper(); final Properties props = testHelper.getConfig(); final AbstractConfigurationContext context = testHelper.getContext(); // removes all keys that are not required or do have a default value for (final AbstractConfigKey currKey : context.getConfigKeys()) { if (!currKey.hasDefaultValue() || currKey.isRequired()) { continue; } props.remove(testHelper.getPropertyKey(currKey)); } final InputStream emptyInput = ContextTestHelper.getPropertiesAsInputStream(props); final MetaConfiguration config = MetaConfiguration.newConfiguration(emptyInput, context); for (final AbstractConfigKey currKey : context.getConfigKeys()) { if (!currKey.hasDefaultValue() || currKey.isRequired()) { continue; } assertEquals(currKey.getDefaultValue(), currKey.getTypedValue(config)); } final AbstractConfigurationContext target = context; final List<ValidationError> result = target.validateConfiguration(config); assertNotNull(result); assertTrue(result.isEmpty()); } /** * Tests successful creation of a {@link MetaConfiguration} with the target {@link AbstractConfigurationContext} as * only context. * * @throws RuntimeException * if creation fails. Considered a test failure. * @throws IOException * if reading the {@link InputStream} fails. Considered a test failure. * @throws ConfigValidationException * if the {@link ContextTestHelper#getConfig() configuration} is invalid. Considered a test failure. */ @Test public final void testCreateMetaConfigurationWithConfigurationContext() throws RuntimeException, IOException, ConfigValidationException { final ContextTestHelper<?> testHelper = getTestHelper(); final Properties configProperties = testHelper.getConfig(); final AbstractConfigurationContext target = testHelper.getContext(); final MetaConfiguration config = MetaConfiguration.newConfiguration( ContextTestHelper.getPropertiesAsInputStream(configProperties), target); for (final AbstractConfigKey currKey : target.getConfigKeys()) { assertEquals( ContextTestHelper.getParsedValue(currKey, configProperties.getProperty(testHelper.getPropertyKey((currKey)))), currKey.getTypedValue(config)); } } /** * Test {@link AbstractConfigurationContext#toString()} for all implementing classes. */ @Test public final void testToString() { final ContextTestHelper<?> testHelper = getTestHelper(); final AbstractConfigurationContext target = testHelper.getContext(); assertEquals("toString returns class' simple name", target.getClass().getSimpleName(), target.toString()); } }