Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#114): implements configuration file per module. #262

Merged
merged 20 commits into from
Dec 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eea8c8c
feat(#114): Implement configuration file per module.
dipak-pawar Nov 15, 2017
1c7d848
Adds changes as per review
dipak-pawar Nov 22, 2017
a201245
Merge branch 'master' of https://github.com/arquillian/smart-testing …
dipak-pawar Nov 23, 2017
bf8964b
Merge branch 'master' of https://github.com/arquillian/smart-testing …
dipak-pawar Nov 24, 2017
220e383
Implemented map level inheritance
dipak-pawar Nov 24, 2017
09cf3c8
use Deque for implentation
dipak-pawar Nov 28, 2017
3dd67de
Rename method def in config file builder
dipak-pawar Nov 28, 2017
24d40e5
Adds changes as per review
dipak-pawar Nov 30, 2017
a12c724
* overwrite inner properties
dipak-pawar Dec 1, 2017
9994449
* documentation changes
dipak-pawar Dec 5, 2017
5debb68
Moved all skipChecker to checker package
dipak-pawar Dec 5, 2017
4757070
Merge branch 'master' of https://github.com/arquillian/smart-testing …
dipak-pawar Dec 5, 2017
838d95f
Merge branch 'master' of https://github.com/arquillian/smart-testing …
dipak-pawar Dec 5, 2017
b2c0e99
Merging into one test
dipak-pawar Dec 6, 2017
74042dc
Update documentation
dipak-pawar Dec 6, 2017
271aedb
Use SkipSTInstallationChecker in SmartTestingMavenConfigure
dipak-pawar Dec 6, 2017
e8df310
update doc
dipak-pawar Dec 6, 2017
352d648
make mapToObject static
dipak-pawar Dec 6, 2017
ca75381
moved SkipSTInstannationChecker logic to SkipInstallationChecker
MatousJobanek Dec 12, 2017
27e0713
Merge branch 'master' of https://github.com/arquillian/smart-testing …
dipak-pawar Dec 13, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.arquillian.smart.testing.RunMode;
import org.arquillian.smart.testing.hub.storage.local.LocalStorage;
Expand Down Expand Up @@ -38,6 +39,8 @@ public class Configuration implements ConfigurationSection {
public static final String SMART_TESTING_DEBUG = "smart.testing.debug";
public static final String SMART_TESTING_AUTOCORRECT = "smart.testing.autocorrect";

static final String INHERIT = "inherit";

private String[] strategies = new String[0];
private String[] customStrategies = new String[0];
private String[] customProviders = new String[0];
Expand Down Expand Up @@ -166,25 +169,25 @@ public void loadStrategyConfigurations(String... strategies) {
}

private List<StrategyConfiguration> getStrategiesConfigurations(String... strategies) {
List<StrategyConfiguration> convertedList = new ArrayList<>();

StreamSupport.stream(new JavaSPILoader().all(TestExecutionPlannerFactory.class).spliterator(), false)
return StreamSupport.stream(new JavaSPILoader().all(TestExecutionPlannerFactory.class).spliterator(), false)
.filter(
testExecutionPlannerFactory -> Arrays.asList(strategies).contains(testExecutionPlannerFactory.alias()))
.map(TestExecutionPlannerFactory::strategyConfiguration)
.filter(Objects::nonNull)
.forEach(strategyConfiguration -> {
final Class<StrategyConfiguration> strategyConfigurationClass =
(Class<StrategyConfiguration>) strategyConfiguration.getClass();
final Object strategyConfig = strategiesConfig.get(strategyConfiguration.name());
Map<String, Object> strategyConfigMap = new HashMap<>();
if (strategyConfig != null) {
strategyConfigMap = (Map<String, Object>) strategyConfig;
}
convertedList.add(mapToObject(strategyConfigurationClass, strategyConfigMap));
});
.map(this::loadStrategyConfiguration)
.collect(Collectors.toList());
}

private StrategyConfiguration loadStrategyConfiguration(StrategyConfiguration strategyConfiguration) {
final Class<StrategyConfiguration> strategyConfigurationClass =
(Class<StrategyConfiguration>) strategyConfiguration.getClass();
final Object strategyConfig = strategiesConfig.get(strategyConfiguration.name());
Map<String, Object> strategyConfigMap = new HashMap<>();
if (strategyConfig != null) {
strategyConfigMap = (Map<String, Object>) strategyConfig;
}

return convertedList;
return mapToObject(strategyConfigurationClass, strategyConfigMap);
}

public File dump(File rootDir) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.arquillian.smart.testing.hub.storage.local.LocalStorage;
import org.arquillian.smart.testing.logger.Log;
import org.arquillian.smart.testing.logger.Logger;
import org.yaml.snakeyaml.Yaml;

import static org.arquillian.smart.testing.configuration.ConfigurationReader.readEffectiveConfiguration;
import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject;

public class ConfigurationLoader {
Expand Down Expand Up @@ -72,8 +67,13 @@ public static Configuration load(File projectDir, Function<File, Boolean> stopCo
configFile = projectDir;
}

Map<String, Object> yamlConfiguration = readConfiguration(configFile);
return parseConfiguration(yamlConfiguration);
return loadEffectiveConfiguration(configFile);
}

private static Configuration loadEffectiveConfiguration(File configFile) {
final Map<String, Object> effectiveConfig = readEffectiveConfiguration(configFile);

return loadAsConfiguration(effectiveConfig);
}

/**
Expand Down Expand Up @@ -105,42 +105,6 @@ public static Configuration loadPrecalculated(File projectDir) {
}
}

private static Map<String, Object> readConfiguration(File configPath) {
if (!configPath.isDirectory()) {
return getConfigParametersFromFile(getConfigurationFilePath(configPath));
}

final File[] files =
configPath.listFiles((dir, name) -> name.equals(SMART_TESTING_YML) || name.equals(SMART_TESTING_YAML));

if (files == null) {
throw new RuntimeException("I/O errors occurs while listing dir " + configPath);
}

if (files.length == 0) {
logger.info("Config file `" + SMART_TESTING_YAML + "` OR `" + SMART_TESTING_YML + "` is not found. "
+ "Using system properties to load configuration for smart testing.");
} else {
return getConfigParametersFromFile(getConfigurationFilePath(files));
}
return Collections.emptyMap();
}

private static Map<String, Object> getConfigParametersFromFile(Path filePath) {
try (InputStream io = Files.newInputStream(filePath)) {
final Yaml yaml = new Yaml();
Map<String, Object> yamlConfig = yaml.load(io);
if (yamlConfig == null) {
logger.warn(String.format("The configuration file %s is empty.", filePath));
return new HashMap<>();
} else {
return yamlConfig;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

static Configuration loadConfigurationFromFile(File configFile) {
try (FileReader fileReader = new FileReader(configFile)) {
final Yaml yaml = new Yaml();
Expand All @@ -150,50 +114,9 @@ static Configuration loadConfigurationFromFile(File configFile) {
}
}

// testing
static Configuration load(Path path) {
try (InputStream io = Files.newInputStream(path)) {
final Yaml yaml = new Yaml();
Map<String, Object> yamlConfiguration = yaml.load(io);
return parseConfiguration(yamlConfiguration);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private static Path getConfigurationFilePath(File... files) {
Path configPath;
if (files.length == 1) {
configPath = files[0].toPath();
} else {
configPath = getDefaultConfigFile(files);
}
logger.info("Using configuration from " + configPath);
return configPath;
}

private static Path getDefaultConfigFile(File... files) {
if (files.length == 2) {
logger.warn(
"Found multiple config files with supported names: " + SMART_TESTING_YAML + ", " + SMART_TESTING_YML);
}

final Path configFilePath = Arrays.stream(files)
.filter(file -> {
if (files.length == 2) {
return file.getName().equals(SMART_TESTING_YML);
}
return file.getName().equals(SMART_TESTING_YAML) || file.getName().equals(SMART_TESTING_YML);
})
.map(File::toPath)
.findFirst()
.get();

return configFilePath;
}

private static Configuration parseConfiguration(Map<String, Object> yamlConfiguration) {
private static Configuration loadAsConfiguration(Map<String, Object> yamlConfiguration) {
final Object strategiesConfiguration = yamlConfiguration.get("strategiesConfiguration");

final Configuration configuration = mapToObject(Configuration.class, yamlConfiguration);
if (strategiesConfiguration != null) {
configuration.setStrategiesConfig((Map<String, Object>) strategiesConfiguration);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.arquillian.smart.testing.configuration;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.Map;
import org.arquillian.smart.testing.logger.Log;
import org.arquillian.smart.testing.logger.Logger;
import org.yaml.snakeyaml.Yaml;

import static org.arquillian.smart.testing.configuration.Configuration.INHERIT;
import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML;
import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML;

class ConfigurationReader {

private static final Logger logger = Log.getLogger();

static Map<String, Object> readEffectiveConfiguration(File configPath) {
if (!configPath.isDirectory()) {
return readEffectiveConfig(getConfigurationFilePath(configPath));
}

final File[] files =
configPath.listFiles((dir, name) -> name.equals(SMART_TESTING_YML) || name.equals(SMART_TESTING_YAML));

if (files == null) {
throw new RuntimeException("I/O errors occurs while listing dir " + configPath);
}

if (files.length == 0) {
logger.info("Config file `" + SMART_TESTING_YAML + "` OR `" + SMART_TESTING_YML + "` is not found. "
+ "Using system properties to load configuration for smart testing.");
} else {
return readEffectiveConfig(getConfigurationFilePath(files));
}
return Collections.emptyMap();
}

private static Map<String, Object> readEffectiveConfig(Path filePath){
Map<String, Object> config = getConfigParametersFromFile(filePath);
Deque<Map<String, Object>> configs = new ArrayDeque<>();
configs.add(config);
while (config.get(INHERIT) != null) {
String inherit = String.valueOf(config.get(INHERIT));
filePath = filePath.getParent().resolve(inherit);
config = getConfigParametersFromFile(filePath);
if (!config.isEmpty()) {
configs.addFirst(config);
}
}

Map<String, Object> effectiveConfig = configs.pollFirst();
while (!configs.isEmpty()) {
overwriteInnerProperties(effectiveConfig, configs.pollFirst());
}

effectiveConfig.remove(INHERIT);

return effectiveConfig;
}

private static void overwriteInnerProperties(Map<String, Object> effective, Map<String, Object> inner) {
for (String key: inner.keySet()) {
if (isNonTrivialPropertyContainedInMap(key, inner, effective)) {
effective.put(key, inner.get(key));
} else {
final Map<String, Object> effectiveValue = ((Map<String, Object>) effective.get(key));
final Map<String, Object> innerValue = ((Map<String, Object>) inner.get(key));
overwriteInnerProperties(effectiveValue, innerValue);
effective.put(key, effectiveValue);
}
}
}

private static boolean isNonTrivialPropertyContainedInMap(String key, Map<String, Object> inner,
Map<String, Object> effective) {
return !Map.class.isAssignableFrom(inner.get(key).getClass()) || !effective.containsKey(key);
}

private static Map<String, Object> getConfigParametersFromFile(Path filePath) {
if (!filePath.toFile().exists()) {
logger.warn(String.format("The configuration file %s is not exists.", filePath));
return Collections.emptyMap();
}
try (InputStream io = Files.newInputStream(filePath)) {
final Yaml yaml = new Yaml();
Map<String, Object> yamlConfig = yaml.load(io);
if (yamlConfig == null) {
logger.warn(String.format("The configuration file %s is empty.", filePath));
return Collections.emptyMap();
} else {
return yamlConfig;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private static Path getConfigurationFilePath(File... files) {
Path configPath;
if (files.length == 1) {
configPath = files[0].toPath();
} else {
configPath = getDefaultConfigFile(files);
}
logger.info("Using configuration from " + configPath);
return configPath;
}

private static Path getDefaultConfigFile(File... files) {
if (files.length == 2) {
logger.warn(
"Found multiple config files with supported names: " + SMART_TESTING_YAML + ", " + SMART_TESTING_YML);
}

return Arrays.stream(files)
.filter(file -> {
if (files.length == 2) {
return file.getName().equals(SMART_TESTING_YML);
}
return file.getName().equals(SMART_TESTING_YAML) || file.getName().equals(SMART_TESTING_YML);
})
.map(File::toPath)
.findFirst()
.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -42,10 +42,7 @@ private static <T> void invokeMethodWithMappedValue(List<ConfigurationItem> conf
final String property = Character.toLowerCase(field.charAt(0)) + field.substring(1);
Object configFileValue = map.get(property);

Optional<ConfigurationItem> foundConfigItem =
configItems.stream().filter(item -> property.equals(item.getParamName())).findFirst();

Object converted = getConvertedObject(method, configFileValue, foundConfigItem);
Object converted = getConvertedObject(method, configFileValue, property, configItems);

try {
if (converted != null) {
Expand All @@ -56,21 +53,24 @@ private static <T> void invokeMethodWithMappedValue(List<ConfigurationItem> conf
}
}

private static Object getConvertedObject(Method method, Object configFileValue, Optional<ConfigurationItem> foundConfigItem) {
private static Object getConvertedObject(Method method, Object configFileValue,
String property, List<ConfigurationItem> configItems) {
Optional<ConfigurationItem> foundConfigItem =
configItems.stream().filter(item -> property.equals(item.getParamName())).findFirst();

if (!foundConfigItem.isPresent()) {
Class<?> parameterType = method.getParameterTypes()[0];
if (!ConfigurationSection.class.isAssignableFrom(parameterType)) {
return null;
} else if (configFileValue == null) {
return mapToObject((Class<ConfigurationSection>) parameterType, new HashMap<>(0));
return mapToObject((Class<ConfigurationSection>) parameterType, Collections.emptyMap());
} else {
return mapToObject((Class<ConfigurationSection>) parameterType, (Map<String, Object>) configFileValue);
}
} else {
Object mappedValue = null;
ConfigurationItem configItem = foundConfigItem.get();
Object mappedValue = getUserSetProperty(method, configItem, configFileValue);

mappedValue = getUserSetProperty(method, configItem, configFileValue);
if (mappedValue == null && configItem.getDefaultValue() != null) {
mappedValue = configItem.getDefaultValue();
}
Expand Down Expand Up @@ -101,7 +101,7 @@ private static List<Object> createMultipleOccurrenceProperty(Method method, Conf
System.getProperties().entrySet()
.stream()
.filter(prop -> prop.getKey().toString().startsWith(sysPropKey))
.collect(Collectors.toMap(prop -> prop.getKey(), prop -> prop.getValue()));
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

List<Object> multipleValue = new ArrayList<>();
if (configFileValue != null) {
Expand Down
Loading