From eea8c8c051eb5f31a0cab508824d3bcbe1fc7154 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 15 Nov 2017 18:26:38 +0530 Subject: [PATCH 01/15] feat(#114): Implement configuration file per module. --- .../testing/configuration/Configuration.java | 15 +- .../configuration/ConfigurationLoader.java | 27 ++-- .../testing/configuration/ObjectMapper.java | 130 +++++++++++++++--- .../smart/testing/configuration/Range.java | 18 +++ .../smart/testing/configuration/Report.java | 15 ++ .../smart/testing/configuration/Scm.java | 15 ++ ...onfigurationOverWriteUsingInheretTest.java | 118 ++++++++++++++++ ...iteUsingInheretWithSystemPropertyTest.java | 62 +++++++++ .../resources/configuration/smart-testing.yml | 29 ++-- docs/configfile.adoc | 74 ++++++++-- .../testbed/project/ProjectConfigurator.java | 35 ++--- .../ConfigurationFilePerModuleTest.java | 71 ++++++++++ ...ExecutionWithConfigFileFunctionalTest.java | 4 +- ...ExecutionWithConfigFileFunctionalTest.java | 2 +- ...ExecutionWithConfigFileFunctionalTest.java | 4 +- .../smart/testing/mvn/ext/ConfigLookup.java | 26 +++- .../mvn/ext/SmartTestingMavenConfigurer.java | 54 ++++++-- 17 files changed, 604 insertions(+), 95 deletions(-) create mode 100644 core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java create mode 100644 core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java create mode 100644 functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java index ca1914f81..5bcb1a461 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java @@ -36,10 +36,14 @@ 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 DISABLE = "disable"; + static final String INHERIT = "inherit"; + private String[] strategies = new String[0]; private String[] customStrategies = new String[0]; private RunMode mode; private String applyTo; + private String inherit; private boolean disable; private boolean debug; @@ -81,6 +85,14 @@ public void setApplyTo(String applyTo) { this.applyTo = applyTo; } + public String getInherit() { + return inherit; + } + + public void setInherit(String inherit) { + this.inherit = inherit; + } + public boolean isDisable() { return disable; } @@ -142,10 +154,11 @@ public List registerConfigurationItems() { configItems.add(new ConfigurationItem("strategies", SMART_TESTING, new String[0])); configItems.add(new ConfigurationItem("mode", SMART_TESTING_MODE, RunMode.valueOf(DEFAULT_MODE.toUpperCase()))); configItems.add(new ConfigurationItem("applyTo", SMART_TESTING_APPLY_TO)); - configItems.add(new ConfigurationItem("disable", SMART_TESTING_DISABLE, false)); + configItems.add(new ConfigurationItem(DISABLE, SMART_TESTING_DISABLE, false)); configItems.add(new ConfigurationItem("debug", SMART_TESTING_DEBUG, false)); configItems.add(new ConfigurationItem("autocorrect", SMART_TESTING_AUTOCORRECT, false)); configItems.add(new ConfigurationItem("customStrategies", SMART_TESTING_CUSTOM_STRATEGIES_PATTERN)); + configItems.add(new ConfigurationItem(INHERIT, null)); return configItems; } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java index bb5116d91..4e35e291a 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java @@ -39,8 +39,17 @@ public static Configuration load(File projectDir) { } else { configFile = projectDir; } + + return readParseOverWriteConfiguration(configFile); + } + + private static Configuration readParseOverWriteConfiguration(File configFile) { + Path configFileDir = configFile.isFile()? configFile.getParentFile().toPath(): configFile.toPath(); Map yamlConfiguration = readConfiguration(configFile); - return parseConfiguration(yamlConfiguration); + final Configuration configuration = parseConfiguration(yamlConfiguration); + final ObjectMapper objectMapper = new ObjectMapper(); + + return objectMapper.overWriteDefaultPropertiesFromParent(configuration, configFileDir); } private static Map readConfiguration(File configPath) { @@ -64,13 +73,17 @@ private static Map readConfiguration(File configPath) { return Collections.emptyMap(); } - private static Map getConfigParametersFromFile(Path filePath) { + static Map getConfigParametersFromFile(Path filePath) { + if (!filePath.toFile().exists()) { + logger.warn(String.format("The configuration file %s is not exists.", filePath)); + return new HashMap<>(0); + } try (InputStream io = Files.newInputStream(filePath)) { final Yaml yaml = new Yaml(); Map yamlConfig = yaml.load(io); if (yamlConfig == null) { logger.warn(String.format("The configuration file %s is empty.", filePath)); - return new HashMap<>(); + return new HashMap<>(0); } else { return yamlConfig; } @@ -107,13 +120,7 @@ static Configuration loadConfigurationFromFile(File configFile) { // testing static Configuration load(Path path) { - try (InputStream io = Files.newInputStream(path)) { - final Yaml yaml = new Yaml(); - Map yamlConfiguration = yaml.load(io); - return parseConfiguration(yamlConfiguration); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return readParseOverWriteConfiguration(path.toFile()); } private static Path getConfigurationFilePath(File... files) { diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index 66f34c1d3..58597c824 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -1,10 +1,13 @@ package org.arquillian.smart.testing.configuration; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -14,9 +17,49 @@ import java.util.Optional; import java.util.stream.Collectors; +import static org.arquillian.smart.testing.configuration.Configuration.DISABLE; +import static org.arquillian.smart.testing.configuration.Configuration.INHERIT; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.getConfigParametersFromFile; + class ObjectMapper { + private boolean userSetProperty = true; + static T mapToObject(Class aClass, Map map) { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(aClass, map); + } + + Configuration overWriteDefaultPropertiesFromParent(Configuration configuration, Path currentDir) { + final String inherit = configuration.getInherit(); + if (inherit == null) { + return configuration; + } else { + List fieldNamesWithDefaultValue = fieldNamesWithDefaultValues(configuration); + + final Path inheritPath = currentDir.resolve(inherit); + final Map parameters = getConfigParametersFromFile(inheritPath); + if (parameters.isEmpty()) { + configuration.setInherit(null); + return configuration; + } + final Map map = parameters.entrySet().stream() + .filter(entry -> fieldNamesWithDefaultValue.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + List configItems = configuration.registerConfigurationItems(); + + Arrays.stream(configuration.getClass().getMethods()) + .filter(method -> fieldNamesWithDefaultValue.contains(fieldName(method)) && isSetter(method)) + .forEach(method -> invokeMethodWithMappedValue(configItems, method, configuration, map)); + + configuration.setInherit((String) parameters.get(INHERIT)); + + return overWriteDefaultPropertiesFromParent(configuration, inheritPath.getParent()); + } + } + + T readValue(Class aClass, Map map) { T instance; try { instance = aClass.newInstance(); @@ -26,13 +69,60 @@ static T mapToObject(Class aClass, Map configItems = instance.registerConfigurationItems(); - Arrays.stream(aClass.getMethods()).filter(ObjectMapper::isSetter) + Arrays.stream(aClass.getMethods()).filter(this::isSetter) .forEach(method -> invokeMethodWithMappedValue(configItems, method, instance, map)); return instance; } - private static void invokeMethodWithMappedValue(List configItems, Method method, T instance, + private List fieldNamesWithDefaultValues(Configuration configuration) { + List fieldsWithDefaultValue = new ArrayList<>(); + Arrays.stream(configuration.getClass().getDeclaredFields()) + .filter(field -> !Modifier.isStatic(field.getModifiers())) + .forEach(field -> addConfigurationFieldsWithDefaultValue(field, configuration, fieldsWithDefaultValue)); + + return fieldsWithDefaultValue.stream().map(Field::getName).collect(Collectors.toList()); + } + + private void addConfigurationFieldsWithDefaultValue(Field field, Configuration configuration, + List defaultValues) { + if (field.getName().equals(DISABLE) || field.getName().equals(INHERIT)) { + return; + } + final ConfigurationSection defaultConfiguration = mapToDefaultObject(Configuration.class, new HashMap<>(0)); + field.setAccessible(true); + try { + final Object actualValue = field.get(configuration); + final Object defaultValue = field.get(defaultConfiguration); + if (actualValue == null && defaultValue == null) { + defaultValues.add(field); + return; + } + if (field.getType().isArray() && Arrays.equals(((Object[]) actualValue), ((Object[]) defaultValue))) { + defaultValues.add(field); + return; + } + if (actualValue != null && actualValue.equals(defaultValue)) { + defaultValues.add(field); + } + } catch (IllegalAccessException e) { + throw new IllegalStateException("Failed to access fieldName: " + field, e); + } + } + + private String fieldName(Method method) { + final String field = method.getName().substring(3); + return Character.toLowerCase(field.charAt(0)) + field.substring(1); + } + + private T mapToDefaultObject(Class aClass, Map map) { + this.userSetProperty = false; + final T defaultObject = readValue(aClass, map); + this.userSetProperty = true; + return defaultObject; + } + + private void invokeMethodWithMappedValue(List configItems, Method method, T instance, Map map) { method.setAccessible(true); if (method.getParameterTypes().length != 1) { @@ -56,21 +146,23 @@ private static void invokeMethodWithMappedValue(List conf } } - private static Object getConvertedObject(Method method, Object configFileValue, Optional foundConfigItem) { + private Object getConvertedObject(Method method, Object configFileValue, + Optional foundConfigItem) { if (!foundConfigItem.isPresent()) { Class parameterType = method.getParameterTypes()[0]; if (!ConfigurationSection.class.isAssignableFrom(parameterType)) { return null; } else if (configFileValue == null) { - return mapToObject((Class) parameterType, new HashMap<>(0)); + return readValue((Class) parameterType, new HashMap<>(0)); } else { - return mapToObject((Class) parameterType, (Map) configFileValue); + return readValue((Class) parameterType, (Map) configFileValue); } } else { Object mappedValue = null; ConfigurationItem configItem = foundConfigItem.get(); - - mappedValue = getUserSetProperty(method, configItem, configFileValue); + if (this.userSetProperty) { + mappedValue = getUserSetProperty(method, configItem, configFileValue); + } if (mappedValue == null && configItem.getDefaultValue() != null) { mappedValue = configItem.getDefaultValue(); } @@ -81,7 +173,7 @@ private static Object getConvertedObject(Method method, Object configFileValue, return null; } - private static Object getUserSetProperty(Method method, ConfigurationItem configItem, Object configFileValue) { + private Object getUserSetProperty(Method method, ConfigurationItem configItem, Object configFileValue) { if (configItem.getSystemProperty() != null) { if (!configItem.getSystemProperty().endsWith(".*")) { String sysPropertyValue = System.getProperty(configItem.getSystemProperty()); @@ -93,7 +185,7 @@ private static Object getUserSetProperty(Method method, ConfigurationItem config return configFileValue; } - private static List createMultipleOccurrenceProperty(Method method, ConfigurationItem configItem, + private List createMultipleOccurrenceProperty(Method method, ConfigurationItem configItem, Object configFileValue) { String sysPropKey = configItem.getSystemProperty().substring(0, configItem.getSystemProperty().lastIndexOf('.')); @@ -120,7 +212,7 @@ private static List createMultipleOccurrenceProperty(Method method, Conf return null; } - private static List getValuesFromFile(Method method, String sysPropKey, Object configFileValue, + private List getValuesFromFile(Method method, String sysPropKey, Object configFileValue, Map systemProperties) { Class parameterType = method.getParameterTypes()[0]; ArrayList fromFileParam = new ArrayList<>(); @@ -137,7 +229,7 @@ private static List getValuesFromFile(Method method, String sysPropKey, .collect(Collectors.toList()); } - private static boolean isSetBySysProperty(Object param, String sysPropKey, Map systemProperties) { + private boolean isSetBySysProperty(Object param, String sysPropKey, Map systemProperties) { String[] paramSplit = String.valueOf(param).split("="); if (paramSplit.length == 2) { String key = paramSplit[0]; @@ -146,7 +238,7 @@ private static boolean isSetBySysProperty(Object param, String sysPropKey, Map parameterType = method.getParameterTypes()[0]; if (parameterType.isArray()) { return handleArray(parameterType.getComponentType(), mappedValue); @@ -157,13 +249,13 @@ private static Object convert(Method method, Object mappedValue) { } else if (parameterType.isAssignableFrom(mappedValue.getClass())) { return mappedValue; } else if (ConfigurationSection.class.isAssignableFrom(parameterType)) { - return mapToObject((Class) parameterType, (Map) mappedValue); + return readValue((Class) parameterType, (Map) mappedValue); } else { return convertToType(parameterType, mappedValue.toString()); } } - private static Enum handleEnum(Method method, Object mapValue) { + private Enum handleEnum(Method method, Object mapValue) { if (mapValue.getClass().isEnum()) { return (Enum) mapValue; } @@ -172,7 +264,7 @@ private static Enum handleEnum(Method method, Object mapValue) { return Enum.valueOf((Class) parameterTypes[0], value.toUpperCase()); } - private static T[] handleArray(Class parameterType, Object mapValue) { + private T[] handleArray(Class parameterType, Object mapValue) { if (mapValue != null && mapValue.getClass().isArray() && ((Object[]) mapValue).length == 0) { return (T[]) mapValue; } @@ -181,7 +273,7 @@ private static T[] handleArray(Class parameterType, Object mapValue) { return convertedList.toArray(array); } - private static Object handleList(Method method, Object mappedValue) { + private Object handleList(Method method, Object mappedValue) { Type[] genericParameterTypes = method.getGenericParameterTypes(); if (genericParameterTypes.length == 1) { Type type = genericParameterTypes[0]; @@ -196,7 +288,7 @@ private static Object handleList(Method method, Object mappedValue) { return null; } - private static Object convertToType(Class clazz, String mappedValue) { + private Object convertToType(Class clazz, String mappedValue) { if (Integer.class.equals(clazz) || int.class.equals(clazz)) { return Integer.valueOf(mappedValue); } else if (Double.class.equals(clazz) || double.class.equals(clazz)) { @@ -211,7 +303,7 @@ private static Object convertToType(Class clazz, String mappedValue) { return null; } - private static List getConvertedList(Class parameterType, Object mappedValue) { + private List getConvertedList(Class parameterType, Object mappedValue) { final Class aClass = mappedValue.getClass(); if (List.class.isAssignableFrom(aClass)) { return (List) mappedValue; @@ -228,7 +320,7 @@ private static List getConvertedList(Class parameterType, Object mappe return null; } - private static boolean isSetter(Method candidate) { + private boolean isSetter(Method candidate) { return candidate.getName().matches("^(set|add)[A-Z].*") && (candidate.getReturnType().equals(Void.TYPE) || candidate.getReturnType() .equals(candidate.getDeclaringClass())) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java index 20b7e4386..1a73e9fcf 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java @@ -46,4 +46,22 @@ public List registerConfigurationItems() { return configItems; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Range range = (Range) o; + + if (head != null ? !head.equals(range.head) : range.head != null) return false; + return tail != null ? tail.equals(range.tail) : range.tail == null; + } + + @Override + public int hashCode() { + int result = head != null ? head.hashCode() : 0; + result = 31 * result + (tail != null ? tail.hashCode() : 0); + return result; + } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java index 78e05f664..3758edb21 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java @@ -45,4 +45,19 @@ public List registerConfigurationItems() { configItems.add(new ConfigurationItem("dir", null, TARGET)); return configItems; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Report report = (Report) o; + + return enable == report.enable; + } + + @Override + public int hashCode() { + return (enable ? 1 : 0); + } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java index d38ab63b4..fc569028d 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java @@ -41,4 +41,19 @@ public List registerConfigurationItems() { return configItems; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Scm scm = (Scm) o; + + return range != null ? range.equals(scm.range) : scm.range == null; + } + + @Override + public int hashCode() { + return range != null ? range.hashCode() : 0; + } } diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java new file mode 100644 index 000000000..e6dc091ee --- /dev/null +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java @@ -0,0 +1,118 @@ +package org.arquillian.smart.testing.configuration; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import static org.arquillian.smart.testing.RunMode.ORDERING; +import static org.arquillian.smart.testing.RunMode.SELECTING; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigurationOverWriteUsingInheretTest { + + private static final String IMPL_BASE = "impl-base"; + static final String CONFIG = "config"; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void should_load_configuration_properties_from_absolute_inherit_if_not_defined_in_child() throws IOException { + // given + final String root = temporaryFolder.getRoot().toString(); + Map child = new HashMap<>(); + child.put("mode", "ordering"); + child.put("applyTo", "surefire"); + child.put("inherit", Paths.get(root, SMART_TESTING_YAML).toString()); + + dumpData(Paths.get(root, SMART_TESTING_YML), child); + + Map parent = new HashMap<>(); + parent.put("strategies", "new, changed, affected"); + dumpData(Paths.get(root, SMART_TESTING_YAML), parent); + + // when + final Configuration configuration = ConfigurationLoader.load(temporaryFolder.getRoot()); + + // then + assertThat(configuration.getMode()).isEqualTo(ORDERING); + assertThat(configuration.getApplyTo()).isEqualTo("surefire"); + assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); + } + + @Test + public void should_load_configuration_properties_from_relative_inherit_if_not_defined_in_child () throws IOException { + // given + temporaryFolder.newFolder(CONFIG, IMPL_BASE); + final String root = temporaryFolder.getRoot().toString(); + Map child = new HashMap<>(); + child.put("inherit", "../smart-testing.yml"); + + dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + + Map child2 = new HashMap<>(); + child2.put("mode", "selecting"); + child2.put("debug", "true"); + child2.put("inherit", "../smart-testing.yml"); + + dumpData(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML), child2); + + Map parent = new HashMap<>(); + parent.put("strategies", "new, changed, affected"); + dumpData(Paths.get(root, SMART_TESTING_YML), parent); + + // when + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG, IMPL_BASE)); + + // then + assertThat(configuration.getMode()).isEqualTo(SELECTING); + assertThat(configuration.isDebug()).isTrue(); + assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); + } + + @Test + public void should_not_overwrite_disable_parameter_from_inherit() throws IOException { + // given + temporaryFolder.newFolder(CONFIG); + final String root = temporaryFolder.getRoot().toString(); + Map child = new HashMap<>(); + child.put("mode", "ordering"); + child.put("disable", true); + child.put("inherit", "../smart-testing.yml"); + + dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + + Map parent = new HashMap<>(); + parent.put("strategies", "new, changed, affected"); + parent.put("disable", false); + dumpData(Paths.get(root, SMART_TESTING_YML), parent); + + // when + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG)); + + // then + assertThat(configuration.getMode()).isEqualTo(ORDERING); + assertThat(configuration.isDisable()).isTrue(); + assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); + } + + static void dumpData(Path filePath, Map data) throws IOException { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + + Yaml yaml = new Yaml(options); + FileWriter writer = new FileWriter(filePath.toString()); + yaml.dump(data, writer); + } +} diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java new file mode 100644 index 000000000..3d5538a8f --- /dev/null +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java @@ -0,0 +1,62 @@ +package org.arquillian.smart.testing.configuration; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import net.jcip.annotations.NotThreadSafe; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; + +import static org.arquillian.smart.testing.RunMode.ORDERING; +import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; +import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheretTest.CONFIG; +import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheretTest.dumpData; +import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; +import static org.arquillian.smart.testing.scm.ScmRunnerProperties.SCM_LAST_CHANGES; +import static org.assertj.core.api.Assertions.assertThat; + +@Category(NotThreadSafe.class) +public class ConfigurationOverWriteUsingInheretWithSystemPropertyTest { + + @Rule + public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void system_properties_should_take_precedence_over_config_file() throws IOException { + // given + System.setProperty(SMART_TESTING, "changed"); + System.setProperty(SCM_LAST_CHANGES, "3"); + + temporaryFolder.newFolder(CONFIG); + final String root = temporaryFolder.getRoot().toString(); + Map child = new HashMap<>(); + child.put("mode", "ordering"); + child.put("inherit", "../smart-testing.yml"); + + dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + + Map parent = new HashMap<>(); + parent.put("strategies", "new, changed, affected"); + dumpData(Paths.get(root, SMART_TESTING_YML), parent); + + final Range range = new Range(); + range.setHead(HEAD); + range.setTail(HEAD + "~3"); + + // when + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG)); + + // then + assertThat(configuration.getStrategies()).isEqualTo(new String[] {"changed"}); + assertThat(configuration.getScm().getRange()).isEqualToComparingFieldByField(range); + assertThat(configuration.getMode()).isEqualTo(ORDERING); + } +} diff --git a/core/src/test/resources/configuration/smart-testing.yml b/core/src/test/resources/configuration/smart-testing.yml index 81964895c..f8b6537e9 100644 --- a/core/src/test/resources/configuration/smart-testing.yml +++ b/core/src/test/resources/configuration/smart-testing.yml @@ -1,24 +1,25 @@ -mode: ordering # -strategies: new, changed, affected # -applyTo: surefire # -debug: true # -disable: false # +inherit: ../smart-testing.yml # +mode: ordering # +strategies: new, changed, affected # +applyTo: surefire # +debug: true # +disable: false # report: - enable: true # + enable: true # scm: range: - head: HEAD # - tail: HEAD~2 # -autocorrect: true # -customStrategies: # + head: HEAD # + tail: HEAD~2 # +autocorrect: true # +customStrategies: # - smart.testing.strategy.cool=org.arquillian.smart.testing:strategy-cool:1.0.0 - smart.testing.strategy.experimental=org.arquillian.smart.testing:strategy-experimental:1.0.0 -strategiesConfiguration: # +strategiesConfiguration: # affected: - transitivity: true # - exclusions: # + transitivity: true # + exclusions: # - org.package.* - org.arquillian.package.* - inclusions: # + inclusions: # - org.package.exclude.* - org.arquillian.package.exclude.* diff --git a/docs/configfile.adoc b/docs/configfile.adoc index e16736e4d..15b46350b 100644 --- a/docs/configfile.adoc +++ b/docs/configfile.adoc @@ -13,20 +13,21 @@ include::../core/src/test/resources/configuration/smart-testing.yml[] ---- copyToClipboard:config-file[] -<1> This defines mode to be used by Smart Testing. -<2> This defines strategies to be used while finding important tests. -<3> This defines plugin to be used for Smart Testing. -<4> This enables debug logs for Smart Testing. -<5> This disables Smart Testing if set to true. -<6> This enables Smart Testing report if set to true. -<7> This sets first commit sha or `HEAD` notation for inspecting changes. -<8> This sets last commit sha or `HEAD` notation for inspecting changes. -<9> This defines if smart testing should auto correct misspelled strategies. -<10> This defines the pair key/value of custom strategies as list. -<11> This defines list of strategy related configurations that you want to apply while applying strategy. -<12> This enables transitivity. -<13> This defines list of packages to be excluded while applying transitivity. -<14> This defines list of packages to be included while applying transitivity. +<1> Config file's absolute or relative path from where Smart Testing overwrites parameters not defined in current config file. +<2> This defines mode to be used by Smart Testing. +<3> This defines strategies to be used while finding important tests. +<4> This defines plugin to be used for Smart Testing. +<5> This enables debug logs for Smart Testing. +<6> This disables Smart Testing if set to true. +<7> This enables Smart Testing report if set to true. +<8> This sets first commit sha or `HEAD` notation for inspecting changes. +<9> This sets last commit sha or `HEAD` notation for inspecting changes. +<10> This defines if smart testing should auto correct misspelled strategies. +<11> This defines the pair key/value of custom strategies as list. +<12> This defines list of strategy related configurations that you want to apply while applying strategy. +<13> This enables transitivity. +<14> This defines list of packages to be excluded while applying transitivity. +<15> This defines list of packages to be included while applying transitivity. All parameters in configuration file are optional. If you haven't used any parameter in configuration file, Smart testing will use default value for that parameter. You can look at <<_reference_card, references>> for default value of parameter. @@ -34,6 +35,48 @@ You can look at <<_reference_card, references>> for default value of parameter. However you can overwrite all configuration options using system properties supported by Smart Testing. You can look <<_reference_card, references>> for all supported system properties. +=== How configuration is applying to each module? +Smart Testing is looking in each module's root dir for configuration file. If it didn't find config file there, it'll look recursively for the first config file in +the parent dir containing pom.xml. + +=== How Configuration parameters are inherited? +If config file has `inherit` defined with absolute or relative path of config file, then Smart Testing will +lookup for all undefined properties in config file defined using inherit. However it won't lookup for `disable` parameter. + +If config file doesn't contain `inherit` parameter. Then Smart Testing won't inherit any configuration properties. + +=== Configuration File per Maven Module: +You can define configuration file per maven module. However it's not mandatory to define configuration per each module. + +NOTE: Whenever you are using configuration file per maven module, make sure to define strategies & scm properties +in project's parent config file or using system property which are required to enable Smart Testing. + +e.g. +```xml +parent + - config + - smart-testing.yml # + - api + - impl-base + - spi + - smart-testing.yml + - container + - api + - impl-base + - smart-testing.yml # +``` + +Configuration file selection for the above example will be as follows: + +* config/api - parent/config/smart-testing.yml +* config/impl-base - parent/config/smart-testing.yml +* config/spi - parent/config/spi/smart-testing.yml +* container/api - parent/smart-testing.yml +* container/impl-base - parent/smart-testing.yml + +<1> inside this file we have inherit: ``../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. +<2> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). + === Configuration File Reference The `const:core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java[name="SMART_TESTING_YML"]` file is a `YAML` file defining required configuration to configure Smart Testing. @@ -43,6 +86,9 @@ NOTE: You can use either a `.yml` or `.yaml` extension for this file. |=== |Field | Description +a| inherit +a| This is used to define absolute or relative path for configuration file from where you want to overwrite paremeters not defined in current config file. + a| strategies a| This is used to define required strategies to find important tests. Look at <<_strategies, strategies>> for all supported options. diff --git a/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java b/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java index 7e5b3f6f4..3facb2df6 100644 --- a/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java +++ b/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java @@ -1,11 +1,10 @@ package org.arquillian.smart.testing.ftest.testbed.project; import java.io.BufferedReader; +import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; @@ -38,7 +37,7 @@ public class ProjectConfigurator { private final Project project; private final Path root; private boolean createConfigFile; - private String customConfigFile; + private Path configFilePath; ProjectConfigurator(Project project, Path root) { this.project = project; @@ -63,16 +62,19 @@ public ProjectConfigurator withConfiguration(Configuration configuration) { return this; } - public ProjectConfigurator createConfigFile() { + public ProjectConfigurator withConfigFile() { this.createConfigFile = true; createConfigurationFile(SMART_TESTING_YML); return this; } - public ProjectConfigurator createConfigFile(String customConfigFile) { + public ProjectConfigurator withConfigFileIn(String path) { + return withConfigFile(path + File.separator + SMART_TESTING_YML); + } + + public ProjectConfigurator withConfigFile(String configFile) { this.createConfigFile = true; - this.customConfigFile = customConfigFile; - createConfigurationFile(customConfigFile); + createConfigurationFile(configFile); return this; } @@ -113,12 +115,8 @@ public Project enable(Using usingInstallation) { .strategies(strategies().split("\\s*,\\s*")) .mode(RunMode.valueOf(getMode().getName().toUpperCase())) .build(); + dumpConfiguration(this.configFilePath); } - - this.project.configureSmartTesting() - .withConfiguration(configuration) - .createConfigFile(customConfigFile != null ? customConfigFile : SMART_TESTING_YML); - } return this.project; } @@ -127,16 +125,11 @@ public String strategies() { return Arrays.stream(getStrategies()).map(Strategy::getName).collect(Collectors.joining(",")); } - private void createConfigurationFile(String customConfigFile) { - Path configFilePath = Paths.get(root.toString(), customConfigFile); - if (!Files.exists(configFilePath)) { - try { - Files.createFile(configFilePath); - } catch (IOException e) { - throw new UncheckedIOException("Failed creating custom configuration file.", e); - } + private void createConfigurationFile(String configFilePath) { + this.configFilePath = Paths.get(root.toString(), configFilePath); + if (configuration != null) { + dumpConfiguration(this.configFilePath); } - dumpConfiguration(configFilePath); } private void dumpConfiguration(Path configFilePath) { diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java new file mode 100644 index 000000000..8aefcdc60 --- /dev/null +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java @@ -0,0 +1,71 @@ +package org.arquillian.smart.testing.ftest.configurationfile; + +import java.util.Collection; +import org.arquillian.smart.testing.configuration.Configuration; +import org.arquillian.smart.testing.ftest.testbed.configuration.builder.ConfigurationBuilder; +import org.arquillian.smart.testing.ftest.testbed.project.Project; +import org.arquillian.smart.testing.ftest.testbed.project.TestResults; +import org.arquillian.smart.testing.ftest.testbed.testresults.TestResult; +import org.arquillian.smart.testing.rules.TestBed; +import org.arquillian.smart.testing.rules.git.GitClone; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; + +import static org.arquillian.smart.testing.RunMode.SELECTING; +import static org.arquillian.smart.testing.ftest.testbed.TestRepository.testRepository; +import static org.arquillian.smart.testing.ftest.testbed.configuration.Strategy.CHANGED; +import static org.arquillian.smart.testing.ftest.testbed.configuration.Strategy.NEW; +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigurationFilePerModuleTest { + @ClassRule + public static final GitClone GIT_CLONE = new GitClone(testRepository()); + + @Rule + public final TestBed testBed = new TestBed(GIT_CLONE); + + @Test + public void should_load_configuration_from_module_if_it_has_config_file() { + // given + final Project project = testBed.getProject(); + + final Configuration parentConfiguration = new ConfigurationBuilder() + .strategies(NEW, CHANGED) + .scm() + .lastChanges("2") + .build() + .build(); + + final Configuration newConfiguration = new ConfigurationBuilder() + .strategies(NEW) + .mode(SELECTING) + .build(); + + final Configuration changedConfiguration = new ConfigurationBuilder() + .strategies(CHANGED) + .mode(SELECTING) + .build(); + + project.configureSmartTesting() + .withConfiguration(parentConfiguration) + .withConfigFile() + .withConfiguration(newConfiguration) + .withConfigFileIn("config/impl-base") + .withConfiguration(changedConfiguration) + .withConfigFileIn("junit/core") + .enable(); + + final Collection expectedTestResults = project + .applyAsLocalChanges("Adds new unit test"); + + expectedTestResults.addAll(project.applyAsCommits("Deletes one test", "Renames unit test")); + + // when + final TestResults actualTestResults = + project.build("junit/core", "config/impl-base").run(); + + // then + assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults).hasSameSizeAs(expectedTestResults); + } +} diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java index 50d27c7e1..4c692556a 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -43,7 +43,7 @@ public void should_only_execute_tests_related_to_single_commit_in_business_logic project.configureSmartTesting() .withConfiguration(configuration) - .createConfigFile(customConfigFile) + .withConfigFile(customConfigFile) .enable(); final Collection expectedTestResults = project @@ -78,7 +78,7 @@ public void should_only_execute_tests_related_to_multiple_commits_in_business_lo // tag::documentation[] project.configureSmartTesting() .withConfiguration(configuration) - .createConfigFile() + .withConfigFile() .enable(); // end::documentation[] diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java index 62ed95d3b..1db3db04a 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -44,7 +44,7 @@ public void should_only_execute_previously_failing_tests_when_failed_is_enabled( project.configureSmartTesting() .executionOrder(FAILED) .inMode(SELECTING) - .createConfigFile() + .withConfigFile() .enable(); // end::documentation[] diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java index 62ce898a0..39c0dacb2 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -32,7 +32,7 @@ public void should_only_execute_new_tests_related_to_single_local_change() throw project.configureSmartTesting() .executionOrder(NEW) .inMode(SELECTING) - .createConfigFile() + .withConfigFile() .enable(); final Collection expectedTestResults = project @@ -53,7 +53,7 @@ public void should_load_configuration_from_parent_dir_if_not_present_in_current_ project.configureSmartTesting() .executionOrder(NEW) .inMode(SELECTING) - .createConfigFile() + .withConfigFile() .enable(); final Collection expectedTestResults = project diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigLookup.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigLookup.java index e352df0fe..3c02f8eaf 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigLookup.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigLookup.java @@ -2,8 +2,11 @@ import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; @@ -11,13 +14,15 @@ class ConfigLookup { private final File executionRootDir; + private final File mavenProjectBaseDir; ConfigLookup(String executionRootDir) { this.executionRootDir = new File(executionRootDir); + this.mavenProjectBaseDir = new File(System.getenv("MAVEN_PROJECTBASEDIR")); } File getFirstDirWithConfigOrProjectRootDir() { - return getFirstDirWithConfigOrProjectRootDir(executionRootDir, new File(System.getenv("MAVEN_PROJECTBASEDIR"))); + return getFirstDirWithConfigOrProjectRootDir(executionRootDir, mavenProjectBaseDir); } private File getFirstDirWithConfigOrProjectRootDir(File projectDir, File multiModuleProjectDirectory) { @@ -40,4 +45,23 @@ private boolean isSameFile(Path path1, Path path2) { throw new RuntimeException(e); } } + + boolean isConfigFromProjectRootDir() { + final File configOrProjectRootDir = getFirstDirWithConfigOrProjectRootDir(); + return isSameFile(configOrProjectRootDir.toPath(), mavenProjectBaseDir.toPath()); + } + + boolean hasMoreThanOneConfigFile(String... fileNames){ + final long count; + try { + count = Files.walk(Paths.get(mavenProjectBaseDir.getAbsolutePath())) + .parallel() + .filter(p -> Arrays.asList(fileNames).contains(p.toFile().getName())) + .count(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + return count > 1; + } } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index 7fa030698..dfdbb7a08 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -8,6 +8,7 @@ import org.apache.maven.MavenExecutionException; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Model; +import org.apache.maven.project.MavenProject; import org.arquillian.smart.testing.configuration.Configuration; import org.arquillian.smart.testing.configuration.ConfigurationLoader; import org.arquillian.smart.testing.hub.storage.ChangeStorage; @@ -25,6 +26,9 @@ import static java.util.stream.StreamSupport.stream; import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_CONFIG; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; @Component(role = AbstractMavenLifecycleParticipant.class, description = "Entry point to install and manage Smart-Testing extension. Takes care of adding needed dependencies and " @@ -83,8 +87,7 @@ private void loadConfigAndCheckIfInstallationShouldBeSkipped(MavenSession sessio if (configuration.isDisable()) { skipExtensionInstallation = true; - logExtensionDisableReason(logger, "System Property " + SMART_TESTING_DISABLE + " is set."); - return; + logExtensionDisableReason(logger, SMART_TESTING_DISABLE + " is set."); } } @@ -124,16 +127,47 @@ private void calculateChanges(File projectDirectory, Configuration configuration } private void configureExtension(MavenSession session, Configuration configuration) { - final MavenProjectConfigurator mavenProjectConfigurator = new MavenProjectConfigurator(configuration); - session.getAllProjects().forEach(mavenProject -> { - boolean wasConfigured = mavenProjectConfigurator.configureTestRunner(mavenProject.getModel()); - if (wasConfigured) { - configuration.dump(mavenProject.getBasedir()); - if (isFailedStrategyUsed()) { - SurefireReportStorage.copySurefireReports(mavenProject.getModel()); + final ConfigLookup lookUp = new ConfigLookup(session.getExecutionRootDirectory()); + final boolean moreThanOneConfigFile = lookUp.hasMoreThanOneConfigFile(SMART_TESTING_YML, SMART_TESTING_YAML); + if (moreThanOneConfigFile && System.getProperty(SMART_TESTING_CONFIG) == null) { + session.getAllProjects().forEach(mavenProject -> { + ConfigLookup configLookup = new ConfigLookup(mavenProject.getBasedir().toString()); + final File dirWithConfig = configLookup.getFirstDirWithConfigOrProjectRootDir(); + Configuration mavenProjectConfiguration = configLookup.isConfigFromProjectRootDir() ? configuration : + ConfigurationLoader.load(dirWithConfig); + if (mavenProjectConfiguration.isDisable()) { + logExtensionDisableReason(logger, + SMART_TESTING_DISABLE + " is set for module " + mavenProject.getArtifactId()); + return; + } + if (mavenProjectConfiguration.areStrategiesDefined()) { + final MavenProjectConfigurator mavenProjectConfigurator = + new MavenProjectConfigurator(mavenProjectConfiguration); + configureMavenProject(mavenProjectConfigurator, mavenProject, mavenProjectConfiguration); + } else { + logger.warn( + "Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " + + "For details on how to configure it head over to http://bit.ly/st-config", + mavenProject.getArtifactId()); } + }); + } else { + final MavenProjectConfigurator mavenProjectConfigurator = new MavenProjectConfigurator(configuration); + session.getAllProjects().forEach(mavenProject -> { + configureMavenProject(mavenProjectConfigurator, mavenProject, configuration); + }); + } + } + + private void configureMavenProject(MavenProjectConfigurator mavenProjectConfigurator, MavenProject mavenProject, + Configuration configuration) { + boolean wasConfigured = mavenProjectConfigurator.configureTestRunner(mavenProject.getModel()); + if (wasConfigured) { + configuration.dump(mavenProject.getBasedir()); + if (isFailedStrategyUsed()) { + SurefireReportStorage.copySurefireReports(mavenProject.getModel()); } - }); + } } private boolean isFailedStrategyUsed() { From 1c7d84811366a5398b30c2db93c279b8722e03e9 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 22 Nov 2017 19:44:09 +0530 Subject: [PATCH 02/15] Adds changes as per review --- .../testing/configuration/Configuration.java | 33 ++--- .../configuration/ConfigurationInheriter.java | 80 ++++++++++++ .../configuration/ConfigurationLoader.java | 97 ++------------- .../configuration/ConfigurationReader.java | 91 ++++++++++++++ .../testing/configuration/ObjectMapper.java | 116 ++---------------- .../smart/testing/configuration/Range.java | 13 +- .../smart/testing/configuration/Report.java | 8 +- .../smart/testing/configuration/Scm.java | 8 +- .../configuration/ConfigurationFile.java | 65 ++++++++++ ...nfigurationOverWriteUsingInheritTest.java} | 76 +++++------- ...teUsingInheritWithSystemPropertyTest.java} | 22 ++-- .../configuration/ObjectMapperTest.java | 29 ++--- docs/configfile.adoc | 34 ++++- .../testbed/project/ProjectConfigurator.java | 8 +- .../ConfigurationFilePerModuleTest.java | 6 +- ...ExecutionWithConfigFileFunctionalTest.java | 4 +- ...ExecutionWithConfigFileFunctionalTest.java | 2 +- ...ExecutionWithConfigFileFunctionalTest.java | 4 +- .../mvn/ext/SmartTestingMavenConfigurer.java | 63 ++++++---- 19 files changed, 428 insertions(+), 331 deletions(-) create mode 100644 core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java create mode 100644 core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java create mode 100644 core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java rename core/src/test/java/org/arquillian/smart/testing/configuration/{ConfigurationOverWriteUsingInheretTest.java => ConfigurationOverWriteUsingInheritTest.java} (54%) rename core/src/test/java/org/arquillian/smart/testing/configuration/{ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java => ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java} (77%) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java index 5bcb1a461..14a819748 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +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; @@ -19,7 +20,6 @@ import org.yaml.snakeyaml.Yaml; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; -import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject; public class Configuration implements ConfigurationSection { @@ -167,25 +167,26 @@ public void loadStrategyConfigurations(String... strategies) { } private List getStrategiesConfigurations(String... strategies) { - List 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 strategyConfigurationClass = - (Class) strategyConfiguration.getClass(); - final Object strategyConfig = strategiesConfig.get(strategyConfiguration.name()); - Map strategyConfigMap = new HashMap<>(); - if (strategyConfig != null) { - strategyConfigMap = (Map) strategyConfig; - } - convertedList.add(mapToObject(strategyConfigurationClass, strategyConfigMap)); - }); - - return convertedList; + .map(this::loadStrategyConfiguration) + .collect(Collectors.toList()); + } + + private StrategyConfiguration loadStrategyConfiguration(StrategyConfiguration strategyConfiguration) { + final ObjectMapper objectMapper = new ObjectMapper(); + final Class strategyConfigurationClass = + (Class) strategyConfiguration.getClass(); + final Object strategyConfig = strategiesConfig.get(strategyConfiguration.name()); + Map strategyConfigMap = new HashMap<>(); + if (strategyConfig != null) { + strategyConfigMap = (Map) strategyConfig; + } + + return objectMapper.readValue(strategyConfigurationClass, strategyConfigMap); } public File dump(File rootDir) { diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java new file mode 100644 index 000000000..d63aeafff --- /dev/null +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java @@ -0,0 +1,80 @@ +package org.arquillian.smart.testing.configuration; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.arquillian.smart.testing.configuration.Configuration.DISABLE; +import static org.arquillian.smart.testing.configuration.Configuration.INHERIT; + +class ConfigurationInheriter { + + Configuration overWriteNotDefinedValuesFromInherit(Configuration configuration, Path currentDir) { + final String inherit = configuration.getInherit(); + if (inherit == null) { + return configuration; + } else { + final List fieldsToInherit = fieldNamesToInherit(configuration); + final Path inheritPath = currentDir.resolve(inherit); + final ConfigurationReader configurationReader = new ConfigurationReader(); + final Map inheritedParameters = configurationReader.getConfigParametersFromFile(inheritPath); + if (inheritedParameters.isEmpty()) { + configuration.setInherit(null); + return configuration; + } + final Map parametersToInherit = inheritedParameters.entrySet().stream() + .filter(entry -> fieldsToInherit.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + List configItems = configuration.registerConfigurationItems(); + + final ObjectMapper objectMapper = new ObjectMapper(); + Arrays.stream(configuration.getClass().getMethods()) + .filter(method -> fieldsToInherit.contains(fieldName(method)) && objectMapper.isSetter(method)) + .forEach(method -> objectMapper.invokeMethodWithMappedValue(configItems, method, configuration, + parametersToInherit)); + + configuration.setInherit((String) inheritedParameters.get(INHERIT)); + + return overWriteNotDefinedValuesFromInherit(configuration, inheritPath.getParent()); + } + } + + private List fieldNamesToInherit(Configuration configuration) { + return Arrays.stream(configuration.getClass().getDeclaredFields()) + .filter(field -> !Modifier.isStatic(field.getModifiers()) && + !hasFieldToIgnore(field) && hasValueNotDefined(field, configuration)) + .map(Field::getName) + .collect(Collectors.toList()); + } + + private boolean hasFieldToIgnore(Field field) { + return Arrays.asList(DISABLE, INHERIT).contains(field.getName()); + } + + private boolean hasValueNotDefined(Field field, Configuration configuration) { + final ObjectMapper objectMapper = new ObjectMapper(); + final Configuration defaultConfiguration = objectMapper.readValue(Configuration.class, Collections.emptyMap()); + + field.setAccessible(true); + try { + final Object actualValue = field.get(configuration); + final Object defaultValue = field.get(defaultConfiguration); + return actualValue == null || actualValue.equals(defaultValue) || + (field.getType().isArray() && Arrays.equals(((Object[]) actualValue), ((Object[]) defaultValue))); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to access fieldName: " + field, e); + } + } + + private String fieldName(Method method) { + final String field = method.getName().substring(3); + return Character.toLowerCase(field.charAt(0)) + field.substring(1); + } +} diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java index 4e35e291a..479ff3757 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java @@ -3,22 +3,14 @@ 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 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.ObjectMapper.mapToObject; - public class ConfigurationLoader { public static final String SMART_TESTING_YML = "smart-testing.yml"; @@ -40,56 +32,19 @@ public static Configuration load(File projectDir) { configFile = projectDir; } - return readParseOverWriteConfiguration(configFile); + return loadInheritedConfiguration(configFile); } - private static Configuration readParseOverWriteConfiguration(File configFile) { + private static Configuration loadInheritedConfiguration(File configFile) { Path configFileDir = configFile.isFile()? configFile.getParentFile().toPath(): configFile.toPath(); - Map yamlConfiguration = readConfiguration(configFile); - final Configuration configuration = parseConfiguration(yamlConfiguration); - final ObjectMapper objectMapper = new ObjectMapper(); - - return objectMapper.overWriteDefaultPropertiesFromParent(configuration, configFileDir); - } - - private static Map 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)); + final ConfigurationReader configurationReader = new ConfigurationReader(); + final Map yamlConfiguration = configurationReader.readConfiguration(configFile); - 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(); - } + final Configuration configuration = ConfigurationLoader.loadAsConfiguration(yamlConfiguration); - static Map getConfigParametersFromFile(Path filePath) { - if (!filePath.toFile().exists()) { - logger.warn(String.format("The configuration file %s is not exists.", filePath)); - return new HashMap<>(0); - } - try (InputStream io = Files.newInputStream(filePath)) { - final Yaml yaml = new Yaml(); - Map yamlConfig = yaml.load(io); - if (yamlConfig == null) { - logger.warn(String.format("The configuration file %s is empty.", filePath)); - return new HashMap<>(0); - } else { - return yamlConfig; - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } + final ConfigurationInheriter inheriter = new ConfigurationInheriter(); + return inheriter.overWriteNotDefinedValuesFromInherit(configuration, configFileDir); } public static Configuration load(File projectDir, String... strategies) { @@ -120,43 +75,13 @@ static Configuration loadConfigurationFromFile(File configFile) { // testing static Configuration load(Path path) { - return readParseOverWriteConfiguration(path.toFile()); - } - - 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; + return loadInheritedConfiguration(path.toFile()); } - private static Configuration parseConfiguration(Map yamlConfiguration) { + private static Configuration loadAsConfiguration(Map yamlConfiguration) { final Object strategiesConfiguration = yamlConfiguration.get("strategiesConfiguration"); - final Configuration configuration = mapToObject(Configuration.class, yamlConfiguration); + final ObjectMapper objectMapper = new ObjectMapper(); + final Configuration configuration = objectMapper.readValue(Configuration.class, yamlConfiguration); if (strategiesConfiguration != null) { configuration.setStrategiesConfig((Map) strategiesConfiguration); } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java new file mode 100644 index 000000000..50e7eb97e --- /dev/null +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -0,0 +1,91 @@ +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.Arrays; +import java.util.Collections; +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.ConfigurationLoader.SMART_TESTING_YAML; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; + +class ConfigurationReader { + + private static final Logger logger = Log.getLogger(); + + Map 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(); + } + + Map 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 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 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 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(); + } +} diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index 58597c824..b23937250 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -1,64 +1,21 @@ package org.arquillian.smart.testing.configuration; import java.lang.reflect.Array; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.nio.file.Path; 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; import java.util.stream.Collectors; -import static org.arquillian.smart.testing.configuration.Configuration.DISABLE; -import static org.arquillian.smart.testing.configuration.Configuration.INHERIT; -import static org.arquillian.smart.testing.configuration.ConfigurationLoader.getConfigParametersFromFile; - class ObjectMapper { - private boolean userSetProperty = true; - - static T mapToObject(Class aClass, Map map) { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(aClass, map); - } - - Configuration overWriteDefaultPropertiesFromParent(Configuration configuration, Path currentDir) { - final String inherit = configuration.getInherit(); - if (inherit == null) { - return configuration; - } else { - List fieldNamesWithDefaultValue = fieldNamesWithDefaultValues(configuration); - - final Path inheritPath = currentDir.resolve(inherit); - final Map parameters = getConfigParametersFromFile(inheritPath); - if (parameters.isEmpty()) { - configuration.setInherit(null); - return configuration; - } - final Map map = parameters.entrySet().stream() - .filter(entry -> fieldNamesWithDefaultValue.contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - List configItems = configuration.registerConfigurationItems(); - - Arrays.stream(configuration.getClass().getMethods()) - .filter(method -> fieldNamesWithDefaultValue.contains(fieldName(method)) && isSetter(method)) - .forEach(method -> invokeMethodWithMappedValue(configItems, method, configuration, map)); - - configuration.setInherit((String) parameters.get(INHERIT)); - - return overWriteDefaultPropertiesFromParent(configuration, inheritPath.getParent()); - } - } - T readValue(Class aClass, Map map) { T instance; try { @@ -75,54 +32,7 @@ T readValue(Class aClass, Map fieldNamesWithDefaultValues(Configuration configuration) { - List fieldsWithDefaultValue = new ArrayList<>(); - Arrays.stream(configuration.getClass().getDeclaredFields()) - .filter(field -> !Modifier.isStatic(field.getModifiers())) - .forEach(field -> addConfigurationFieldsWithDefaultValue(field, configuration, fieldsWithDefaultValue)); - - return fieldsWithDefaultValue.stream().map(Field::getName).collect(Collectors.toList()); - } - - private void addConfigurationFieldsWithDefaultValue(Field field, Configuration configuration, - List defaultValues) { - if (field.getName().equals(DISABLE) || field.getName().equals(INHERIT)) { - return; - } - final ConfigurationSection defaultConfiguration = mapToDefaultObject(Configuration.class, new HashMap<>(0)); - field.setAccessible(true); - try { - final Object actualValue = field.get(configuration); - final Object defaultValue = field.get(defaultConfiguration); - if (actualValue == null && defaultValue == null) { - defaultValues.add(field); - return; - } - if (field.getType().isArray() && Arrays.equals(((Object[]) actualValue), ((Object[]) defaultValue))) { - defaultValues.add(field); - return; - } - if (actualValue != null && actualValue.equals(defaultValue)) { - defaultValues.add(field); - } - } catch (IllegalAccessException e) { - throw new IllegalStateException("Failed to access fieldName: " + field, e); - } - } - - private String fieldName(Method method) { - final String field = method.getName().substring(3); - return Character.toLowerCase(field.charAt(0)) + field.substring(1); - } - - private T mapToDefaultObject(Class aClass, Map map) { - this.userSetProperty = false; - final T defaultObject = readValue(aClass, map); - this.userSetProperty = true; - return defaultObject; - } - - private void invokeMethodWithMappedValue(List configItems, Method method, T instance, + void invokeMethodWithMappedValue(List configItems, Method method, T instance, Map map) { method.setAccessible(true); if (method.getParameterTypes().length != 1) { @@ -132,10 +42,7 @@ private void invokeMethodWithMappedValue(List configItems final String property = Character.toLowerCase(field.charAt(0)) + field.substring(1); Object configFileValue = map.get(property); - Optional 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) { @@ -147,22 +54,23 @@ private void invokeMethodWithMappedValue(List configItems } private Object getConvertedObject(Method method, Object configFileValue, - Optional foundConfigItem) { + String property, List configItems) { + Optional 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 readValue((Class) parameterType, new HashMap<>(0)); + return readValue((Class) parameterType, Collections.emptyMap()); } else { return readValue((Class) parameterType, (Map) configFileValue); } } else { - Object mappedValue = null; ConfigurationItem configItem = foundConfigItem.get(); - if (this.userSetProperty) { - mappedValue = getUserSetProperty(method, configItem, configFileValue); - } + Object mappedValue = getUserSetProperty(method, configItem, configFileValue); + if (mappedValue == null && configItem.getDefaultValue() != null) { mappedValue = configItem.getDefaultValue(); } @@ -193,7 +101,7 @@ private List createMultipleOccurrenceProperty(Method method, Configurati 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 multipleValue = new ArrayList<>(); if (configFileValue != null) { @@ -320,7 +228,7 @@ private List getConvertedList(Class parameterType, Object mappedValue) return null; } - private boolean isSetter(Method candidate) { + boolean isSetter(Method candidate) { return candidate.getName().matches("^(set|add)[A-Z].*") && (candidate.getReturnType().equals(Void.TYPE) || candidate.getReturnType() .equals(candidate.getDeclaringClass())) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java index 1a73e9fcf..6623ea5d8 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java @@ -49,19 +49,24 @@ public List registerConfigurationItems() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } final Range range = (Range) o; - if (head != null ? !head.equals(range.head) : range.head != null) return false; - return tail != null ? tail.equals(range.tail) : range.tail == null; + return (head != null ? head.equals(range.head) : range.head == null) + && (tail != null ? tail.equals(range.tail) : range.tail == null); } @Override public int hashCode() { int result = head != null ? head.hashCode() : 0; result = 31 * result + (tail != null ? tail.hashCode() : 0); + return result; } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java index 3758edb21..ec327c4e9 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java @@ -48,8 +48,12 @@ public List registerConfigurationItems() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } final Report report = (Report) o; diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java index fc569028d..97c00a27c 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java @@ -44,8 +44,12 @@ public List registerConfigurationItems() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } final Scm scm = (Scm) o; diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java new file mode 100644 index 000000000..793bad72e --- /dev/null +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java @@ -0,0 +1,65 @@ +package org.arquillian.smart.testing.configuration; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +class ConfigurationFile { + + private HashMap properties; + + private ConfigurationFile() { + properties = new HashMap<>(); + } + + static ConfigurationFile SmartTestingConfigurationFile() { + return new ConfigurationFile(); + } + + ConfigurationFile inherit(String inherit) { + properties.put("inherit", inherit); + return this; + } + + ConfigurationFile mode(String mode) { + properties.put("mode", mode); + return this; + } + + ConfigurationFile applyTo(String applyTo) { + properties.put("applyTo", applyTo); + return this; + } + + ConfigurationFile strategies(String strategies) { + properties.put("strategies", strategies); + return this; + } + + ConfigurationFile debug(boolean debug) { + properties.put("debug", debug); + return this; + } + + ConfigurationFile disable(boolean disable) { + properties.put("disable", disable); + return this; + } + + void create(Path filePath) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + + Yaml yaml = new Yaml(options); + try { + FileWriter writer = new FileWriter(filePath.toString()); + yaml.dump(properties, writer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java similarity index 54% rename from core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java rename to core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java index e6dc091ee..911ec49e2 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java @@ -1,24 +1,19 @@ package org.arquillian.smart.testing.configuration; -import java.io.FileWriter; import java.io.IOException; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.RunMode.SELECTING; +import static org.arquillian.smart.testing.configuration.ConfigurationFile.SmartTestingConfigurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.assertj.core.api.Assertions.assertThat; -public class ConfigurationOverWriteUsingInheretTest { +public class ConfigurationOverWriteUsingInheritTest { private static final String IMPL_BASE = "impl-base"; static final String CONFIG = "config"; @@ -30,16 +25,16 @@ public class ConfigurationOverWriteUsingInheretTest { public void should_load_configuration_properties_from_absolute_inherit_if_not_defined_in_child() throws IOException { // given final String root = temporaryFolder.getRoot().toString(); - Map child = new HashMap<>(); - child.put("mode", "ordering"); - child.put("applyTo", "surefire"); - child.put("inherit", Paths.get(root, SMART_TESTING_YAML).toString()); - dumpData(Paths.get(root, SMART_TESTING_YML), child); + SmartTestingConfigurationFile() + .mode("ordering") + .applyTo("surefire") + .inherit(Paths.get(root, SMART_TESTING_YAML).toString()) + .create(Paths.get(root, SMART_TESTING_YML)); - Map parent = new HashMap<>(); - parent.put("strategies", "new, changed, affected"); - dumpData(Paths.get(root, SMART_TESTING_YAML), parent); + SmartTestingConfigurationFile() + .strategies("new, changed, affected") + .create(Paths.get(root, SMART_TESTING_YAML)); // when final Configuration configuration = ConfigurationLoader.load(temporaryFolder.getRoot()); @@ -55,21 +50,20 @@ public void should_load_configuration_properties_from_relative_inherit_if_not_de // given temporaryFolder.newFolder(CONFIG, IMPL_BASE); final String root = temporaryFolder.getRoot().toString(); - Map child = new HashMap<>(); - child.put("inherit", "../smart-testing.yml"); - dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + SmartTestingConfigurationFile() + .inherit("../smart-testing.yml") + .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); - Map child2 = new HashMap<>(); - child2.put("mode", "selecting"); - child2.put("debug", "true"); - child2.put("inherit", "../smart-testing.yml"); + SmartTestingConfigurationFile() + .inherit("../smart-testing.yml") + .mode("selecting") + .debug(true) + .create(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML)); - dumpData(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML), child2); - - Map parent = new HashMap<>(); - parent.put("strategies", "new, changed, affected"); - dumpData(Paths.get(root, SMART_TESTING_YML), parent); + SmartTestingConfigurationFile() + .strategies("new, changed, affected") + .create(Paths.get(root, SMART_TESTING_YML)); // when final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG, IMPL_BASE)); @@ -85,17 +79,17 @@ public void should_not_overwrite_disable_parameter_from_inherit() throws IOExcep // given temporaryFolder.newFolder(CONFIG); final String root = temporaryFolder.getRoot().toString(); - Map child = new HashMap<>(); - child.put("mode", "ordering"); - child.put("disable", true); - child.put("inherit", "../smart-testing.yml"); - dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + SmartTestingConfigurationFile() + .inherit("../smart-testing.yml") + .mode("ordering") + .disable(true) + .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); - Map parent = new HashMap<>(); - parent.put("strategies", "new, changed, affected"); - parent.put("disable", false); - dumpData(Paths.get(root, SMART_TESTING_YML), parent); + SmartTestingConfigurationFile() + .strategies("new, changed, affected") + .disable(false) + .create(Paths.get(root, SMART_TESTING_YML)); // when final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG)); @@ -105,14 +99,4 @@ public void should_not_overwrite_disable_parameter_from_inherit() throws IOExcep assertThat(configuration.isDisable()).isTrue(); assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); } - - static void dumpData(Path filePath, Map data) throws IOException { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - options.setPrettyFlow(true); - - Yaml yaml = new Yaml(options); - FileWriter writer = new FileWriter(filePath.toString()); - yaml.dump(data, writer); - } } diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java similarity index 77% rename from core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java rename to core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java index 3d5538a8f..8fce8f06b 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheretWithSystemPropertyTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java @@ -2,8 +2,6 @@ import java.io.IOException; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; import net.jcip.annotations.NotThreadSafe; import org.junit.Rule; import org.junit.Test; @@ -13,15 +11,15 @@ import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING; +import static org.arquillian.smart.testing.configuration.ConfigurationFile.SmartTestingConfigurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; -import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheretTest.CONFIG; -import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheretTest.dumpData; +import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheritTest.CONFIG; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.SCM_LAST_CHANGES; import static org.assertj.core.api.Assertions.assertThat; @Category(NotThreadSafe.class) -public class ConfigurationOverWriteUsingInheretWithSystemPropertyTest { +public class ConfigurationOverWriteUsingInheritWithSystemPropertyTest { @Rule public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); @@ -37,15 +35,15 @@ public void system_properties_should_take_precedence_over_config_file() throws I temporaryFolder.newFolder(CONFIG); final String root = temporaryFolder.getRoot().toString(); - Map child = new HashMap<>(); - child.put("mode", "ordering"); - child.put("inherit", "../smart-testing.yml"); - dumpData(Paths.get(root, CONFIG, SMART_TESTING_YML), child); + SmartTestingConfigurationFile() + .inherit("../smart-testing.yml") + .mode("ordering") + .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); - Map parent = new HashMap<>(); - parent.put("strategies", "new, changed, affected"); - dumpData(Paths.get(root, SMART_TESTING_YML), parent); + SmartTestingConfigurationFile() + .strategies("new, changed, affected") + .create(Paths.get(root, SMART_TESTING_YML)); final Range range = new Range(); range.setHead(HEAD); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java index ba4c33370..64b908596 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java @@ -17,7 +17,6 @@ import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.experimental.categories.Category; -import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject; import static org.arquillian.smart.testing.configuration.ObjectMapperTest.TestEnum.FOO; import static org.assertj.core.api.Assertions.assertThat; @@ -29,6 +28,8 @@ public class ObjectMapperTest { private Map map; + private static final ObjectMapper MAPPER = new ObjectMapper(); + @Before public void initMap() { map = new HashMap<>(); @@ -40,7 +41,7 @@ public void should_set_integer_to_object() { map.put("i", 10); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("i", 10); @@ -52,7 +53,7 @@ public void should_set_double_to_object() { map.put("d", 10.0); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("d", 10.0); @@ -64,7 +65,7 @@ public void should_set_boolean_to_object() { map.put("b", true); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("b", true); @@ -76,7 +77,7 @@ public void should_set_string_to_object() { map.put("s", "Hello"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("s", "Hello"); @@ -88,7 +89,7 @@ public void should_set_comma_separated_string_as_array_to_object() { map.put("as", "hello, bar"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("as", new String[] {"hello", "bar"}); @@ -100,7 +101,7 @@ public void should_set_list_to_object() { map.put("l", Arrays.asList("foo", "bar")); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); assertThat(testObject).hasFieldOrPropertyWithValue("l", Arrays.asList("foo", "bar")); } @@ -111,7 +112,7 @@ public void should_set_enum_to_object() { map.put("e", "foo"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("e", FOO); @@ -123,7 +124,7 @@ public void should_read_multiple_system_properties_expression() { System.setProperty("my.property.x", "smart"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=smart"}); @@ -135,7 +136,7 @@ public void should_set_multiple_property_to_object() { map.put("multiple", "my.property.x=smart"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=smart"}); @@ -148,7 +149,7 @@ public void should_override_multiple_property_with_same_key_by_system_property_v System.setProperty("my.property.x", "new-smart"); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=new-smart"}); @@ -169,7 +170,7 @@ public void should_set_nested_object_to_object() { dummyObject.setL(Arrays.asList("foo", "bar")); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).isNotNull(); @@ -185,7 +186,7 @@ public void should_set_map_to_object() { map.put("m", stringMap); // when - final TestObject testObject = mapToObject(TestObject.class, map); + final TestObject testObject = MAPPER.readValue(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("m", stringMap); @@ -212,7 +213,7 @@ public void should_return_default_value_for_empty_map() { expected.setDummyObject(dummyObject); // when - final TestObject testObject = mapToObject(TestObject.class, Collections.emptyMap()); + final TestObject testObject = MAPPER.readValue(TestObject.class, Collections.emptyMap()); // then assertThat(testObject).isEqualToComparingFieldByFieldRecursively(expected); diff --git a/docs/configfile.adoc b/docs/configfile.adoc index 15b46350b..b43ad72b8 100644 --- a/docs/configfile.adoc +++ b/docs/configfile.adoc @@ -48,8 +48,9 @@ If config file doesn't contain `inherit` parameter. Then Smart Testing won't inh === Configuration File per Maven Module: You can define configuration file per maven module. However it's not mandatory to define configuration per each module. -NOTE: Whenever you are using configuration file per maven module, make sure to define strategies & scm properties -in project's parent config file or using system property which are required to enable Smart Testing. +NOTE: Whenever you are using configuration file per maven module, make sure to define strategies in project's +parent configuration file or using system property. If you are running tests against range of commits, you need to define scm properties in project's parent config file or using system property. +It won't work if you define scm properties in any module's config file and not in parent config file. e.g. ```xml @@ -59,13 +60,34 @@ parent - api - impl-base - spi - - smart-testing.yml + - smart-testing.yml # - container - api - impl-base - - smart-testing.yml # + - smart-testing.yml # ``` +<1> +.parent/config/smart-testing.yml +---- +inherit: ../smart-testing.yml +strategies: affected +---- + +<2> +parent/config/spi/smart-testing.yml +---- +strategies: affected,new +---- + +<3> +.parent/smart-testing.yml +---- +strategies: new +scm: + lastChanges: 1 +---- + Configuration file selection for the above example will be as follows: * config/api - parent/config/smart-testing.yml @@ -74,8 +96,8 @@ Configuration file selection for the above example will be as follows: * container/api - parent/smart-testing.yml * container/impl-base - parent/smart-testing.yml -<1> inside this file we have inherit: ``../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. -<2> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). +<1> inside this file we have inherit: ../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. +<3> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). === Configuration File Reference The `const:core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java[name="SMART_TESTING_YML"]` file is a `YAML` file defining required configuration to configure Smart Testing. diff --git a/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java b/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java index 3facb2df6..5dd9bc947 100644 --- a/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java +++ b/functional-tests/test-bed/src/main/java/org/arquillian/smart/testing/ftest/testbed/project/ProjectConfigurator.java @@ -62,17 +62,17 @@ public ProjectConfigurator withConfiguration(Configuration configuration) { return this; } - public ProjectConfigurator withConfigFile() { + public ProjectConfigurator createConfigFile() { this.createConfigFile = true; createConfigurationFile(SMART_TESTING_YML); return this; } - public ProjectConfigurator withConfigFileIn(String path) { - return withConfigFile(path + File.separator + SMART_TESTING_YML); + public ProjectConfigurator createConfigFileIn(String path) { + return createConfigFile(path + File.separator + SMART_TESTING_YML); } - public ProjectConfigurator withConfigFile(String configFile) { + public ProjectConfigurator createConfigFile(String configFile) { this.createConfigFile = true; createConfigurationFile(configFile); return this; diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java index 8aefcdc60..af0cebc35 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java @@ -49,11 +49,11 @@ public void should_load_configuration_from_module_if_it_has_config_file() { project.configureSmartTesting() .withConfiguration(parentConfiguration) - .withConfigFile() + .createConfigFile() .withConfiguration(newConfiguration) - .withConfigFileIn("config/impl-base") + .createConfigFileIn("config/impl-base") .withConfiguration(changedConfiguration) - .withConfigFileIn("junit/core") + .createConfigFileIn("junit/core") .enable(); final Collection expectedTestResults = project diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java index 4c692556a..50d27c7e1 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesAffectedTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -43,7 +43,7 @@ public void should_only_execute_tests_related_to_single_commit_in_business_logic project.configureSmartTesting() .withConfiguration(configuration) - .withConfigFile(customConfigFile) + .createConfigFile(customConfigFile) .enable(); final Collection expectedTestResults = project @@ -78,7 +78,7 @@ public void should_only_execute_tests_related_to_multiple_commits_in_business_lo // tag::documentation[] project.configureSmartTesting() .withConfiguration(configuration) - .withConfigFile() + .createConfigFile() .enable(); // end::documentation[] diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java index 1db3db04a..62ed95d3b 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/HistoricalChangesFailedTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -44,7 +44,7 @@ public void should_only_execute_previously_failing_tests_when_failed_is_enabled( project.configureSmartTesting() .executionOrder(FAILED) .inMode(SELECTING) - .withConfigFile() + .createConfigFile() .enable(); // end::documentation[] diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java index 39c0dacb2..62ce898a0 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/LocalChangesNewTestsSelectionExecutionWithConfigFileFunctionalTest.java @@ -32,7 +32,7 @@ public void should_only_execute_new_tests_related_to_single_local_change() throw project.configureSmartTesting() .executionOrder(NEW) .inMode(SELECTING) - .withConfigFile() + .createConfigFile() .enable(); final Collection expectedTestResults = project @@ -53,7 +53,7 @@ public void should_load_configuration_from_parent_dir_if_not_present_in_current_ project.configureSmartTesting() .executionOrder(NEW) .inMode(SELECTING) - .withConfigFile() + .createConfigFile() .enable(); final Collection expectedTestResults = project diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index dfdbb7a08..9d36b9330 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -3,6 +3,7 @@ import java.io.File; import java.util.Arrays; import java.util.Collection; +import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.maven.AbstractMavenLifecycleParticipant; import org.apache.maven.MavenExecutionException; @@ -127,36 +128,44 @@ private void calculateChanges(File projectDirectory, Configuration configuration } private void configureExtension(MavenSession session, Configuration configuration) { - final ConfigLookup lookUp = new ConfigLookup(session.getExecutionRootDirectory()); - final boolean moreThanOneConfigFile = lookUp.hasMoreThanOneConfigFile(SMART_TESTING_YML, SMART_TESTING_YAML); - if (moreThanOneConfigFile && System.getProperty(SMART_TESTING_CONFIG) == null) { - session.getAllProjects().forEach(mavenProject -> { - ConfigLookup configLookup = new ConfigLookup(mavenProject.getBasedir().toString()); - final File dirWithConfig = configLookup.getFirstDirWithConfigOrProjectRootDir(); - Configuration mavenProjectConfiguration = configLookup.isConfigFromProjectRootDir() ? configuration : - ConfigurationLoader.load(dirWithConfig); - if (mavenProjectConfiguration.isDisable()) { - logExtensionDisableReason(logger, - SMART_TESTING_DISABLE + " is set for module " + mavenProject.getArtifactId()); - return; - } - if (mavenProjectConfiguration.areStrategiesDefined()) { - final MavenProjectConfigurator mavenProjectConfigurator = - new MavenProjectConfigurator(mavenProjectConfiguration); - configureMavenProject(mavenProjectConfigurator, mavenProject, mavenProjectConfiguration); - } else { - logger.warn( - "Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " - + "For details on how to configure it head over to http://bit.ly/st-config", - mavenProject.getArtifactId()); - } - }); + final Consumer configureSmartTestingExtensionAction; + if (hasModuleSpecificConfigurations(session)) { + configureSmartTestingExtensionAction = applyModuleSpecificConfiguration(configuration); } else { final MavenProjectConfigurator mavenProjectConfigurator = new MavenProjectConfigurator(configuration); - session.getAllProjects().forEach(mavenProject -> { - configureMavenProject(mavenProjectConfigurator, mavenProject, configuration); - }); + configureSmartTestingExtensionAction = mavenProject -> configureMavenProject(mavenProjectConfigurator, mavenProject, configuration); } + session.getAllProjects().forEach(configureSmartTestingExtensionAction); + } + + private boolean hasModuleSpecificConfigurations(MavenSession session) { + final ConfigLookup lookUp = new ConfigLookup(session.getExecutionRootDirectory()); + final boolean moreThanOneConfigFile = lookUp.hasMoreThanOneConfigFile(SMART_TESTING_YML, SMART_TESTING_YAML); + return moreThanOneConfigFile && System.getProperty(SMART_TESTING_CONFIG) == null; + } + + private Consumer applyModuleSpecificConfiguration(Configuration configuration) { + return mavenProject -> { + final ConfigLookup configLookup = new ConfigLookup(mavenProject.getBasedir().toString()); + final File dirWithConfig = configLookup.getFirstDirWithConfigOrProjectRootDir(); + final Configuration mavenProjectConfiguration = configLookup.isConfigFromProjectRootDir() ? configuration : + ConfigurationLoader.load(dirWithConfig); + if (mavenProjectConfiguration.isDisable()) { + logExtensionDisableReason(logger, + SMART_TESTING_DISABLE + " is set for module " + mavenProject.getArtifactId()); + return; + } + if (mavenProjectConfiguration.areStrategiesDefined()) { + final MavenProjectConfigurator mavenProjectConfigurator = + new MavenProjectConfigurator(mavenProjectConfiguration); + configureMavenProject(mavenProjectConfigurator, mavenProject, mavenProjectConfiguration); + } else { + logger.warn( + "Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " + + "For details on how to configure it head over to http://bit.ly/st-config", + mavenProject.getArtifactId()); + } + }; } private void configureMavenProject(MavenProjectConfigurator mavenProjectConfigurator, MavenProject mavenProject, From 220e38337ae2e2b20e40f8fc1f831bf79fa0ad3a Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Fri, 24 Nov 2017 15:52:00 +0530 Subject: [PATCH 03/15] Implemented map level inheritance --- .../configuration/ConfigurationInheriter.java | 80 ------------------- .../configuration/ConfigurationLoader.java | 20 ++--- .../configuration/ConfigurationReader.java | 32 +++++++- .../testing/configuration/ObjectMapper.java | 4 +- .../smart/testing/configuration/Range.java | 23 ------ .../smart/testing/configuration/Report.java | 19 ----- .../smart/testing/configuration/Scm.java | 19 ----- ...ile.java => ConfigurationFileBuilder.java} | 20 ++--- ...onfigurationOverWriteUsingInheritTest.java | 6 +- ...iteUsingInheritWithSystemPropertyTest.java | 4 +- .../configuration/ConfigurationTest.java | 2 +- .../ConfigurationUsingPropertyTest.java | 10 +-- 12 files changed, 57 insertions(+), 182 deletions(-) delete mode 100644 core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java rename core/src/test/java/org/arquillian/smart/testing/configuration/{ConfigurationFile.java => ConfigurationFileBuilder.java} (70%) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java deleted file mode 100644 index d63aeafff..000000000 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationInheriter.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.arquillian.smart.testing.configuration; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.arquillian.smart.testing.configuration.Configuration.DISABLE; -import static org.arquillian.smart.testing.configuration.Configuration.INHERIT; - -class ConfigurationInheriter { - - Configuration overWriteNotDefinedValuesFromInherit(Configuration configuration, Path currentDir) { - final String inherit = configuration.getInherit(); - if (inherit == null) { - return configuration; - } else { - final List fieldsToInherit = fieldNamesToInherit(configuration); - final Path inheritPath = currentDir.resolve(inherit); - final ConfigurationReader configurationReader = new ConfigurationReader(); - final Map inheritedParameters = configurationReader.getConfigParametersFromFile(inheritPath); - if (inheritedParameters.isEmpty()) { - configuration.setInherit(null); - return configuration; - } - final Map parametersToInherit = inheritedParameters.entrySet().stream() - .filter(entry -> fieldsToInherit.contains(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - List configItems = configuration.registerConfigurationItems(); - - final ObjectMapper objectMapper = new ObjectMapper(); - Arrays.stream(configuration.getClass().getMethods()) - .filter(method -> fieldsToInherit.contains(fieldName(method)) && objectMapper.isSetter(method)) - .forEach(method -> objectMapper.invokeMethodWithMappedValue(configItems, method, configuration, - parametersToInherit)); - - configuration.setInherit((String) inheritedParameters.get(INHERIT)); - - return overWriteNotDefinedValuesFromInherit(configuration, inheritPath.getParent()); - } - } - - private List fieldNamesToInherit(Configuration configuration) { - return Arrays.stream(configuration.getClass().getDeclaredFields()) - .filter(field -> !Modifier.isStatic(field.getModifiers()) && - !hasFieldToIgnore(field) && hasValueNotDefined(field, configuration)) - .map(Field::getName) - .collect(Collectors.toList()); - } - - private boolean hasFieldToIgnore(Field field) { - return Arrays.asList(DISABLE, INHERIT).contains(field.getName()); - } - - private boolean hasValueNotDefined(Field field, Configuration configuration) { - final ObjectMapper objectMapper = new ObjectMapper(); - final Configuration defaultConfiguration = objectMapper.readValue(Configuration.class, Collections.emptyMap()); - - field.setAccessible(true); - try { - final Object actualValue = field.get(configuration); - final Object defaultValue = field.get(defaultConfiguration); - return actualValue == null || actualValue.equals(defaultValue) || - (field.getType().isArray() && Arrays.equals(((Object[]) actualValue), ((Object[]) defaultValue))); - } catch (IllegalAccessException e) { - throw new RuntimeException("Failed to access fieldName: " + field, e); - } - } - - private String fieldName(Method method) { - final String field = method.getName().substring(3); - return Character.toLowerCase(field.charAt(0)) + field.substring(1); - } -} diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java index 3233624eb..9e9692569 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java @@ -64,19 +64,14 @@ public static Configuration load(File projectDir, Function stopCo configFile = projectDir; } - return loadInheritedConfiguration(configFile); + return loadEffectiveConfiguration(configFile); } - private static Configuration loadInheritedConfiguration(File configFile) { - Path configFileDir = configFile.isFile()? configFile.getParentFile().toPath(): configFile.toPath(); + private static Configuration loadEffectiveConfiguration(File configFile) { + ConfigurationReader configurationReader = new ConfigurationReader(); + final Map effectiveConfig = configurationReader.readConfiguration(configFile); - final ConfigurationReader configurationReader = new ConfigurationReader(); - final Map yamlConfiguration = configurationReader.readConfiguration(configFile); - - final Configuration configuration = ConfigurationLoader.loadAsConfiguration(yamlConfiguration); - - final ConfigurationInheriter inheriter = new ConfigurationInheriter(); - return inheriter.overWriteNotDefinedValuesFromInherit(configuration, configFileDir); + return ConfigurationLoader.loadAsConfiguration(effectiveConfig); } /** @@ -117,11 +112,6 @@ static Configuration loadConfigurationFromFile(File configFile) { } } - // testing - static Configuration load(Path path) { - return loadInheritedConfiguration(path.toFile()); - } - private static Configuration loadAsConfiguration(Map yamlConfiguration) { final Object strategiesConfiguration = yamlConfiguration.get("strategiesConfiguration"); final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index 50e7eb97e..200009e3c 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -6,13 +6,16 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; 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; @@ -22,7 +25,7 @@ class ConfigurationReader { Map readConfiguration(File configPath) { if (!configPath.isDirectory()) { - return getConfigParametersFromFile(getConfigurationFilePath(configPath)); + return readEffectiveConfig(getConfigurationFilePath(configPath)); } final File[] files = @@ -36,12 +39,12 @@ Map readConfiguration(File configPath) { 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 readEffectiveConfig(getConfigurationFilePath(files)); } return Collections.emptyMap(); } - Map getConfigParametersFromFile(Path filePath) { + private Map getConfigParametersFromFile(Path filePath) { if (!filePath.toFile().exists()) { logger.warn(String.format("The configuration file %s is not exists.", filePath)); return Collections.emptyMap(); @@ -88,4 +91,27 @@ private Path getDefaultConfigFile(File... files) { .findFirst() .get(); } + + private Map readEffectiveConfig(Path filePath){ + Map config = getConfigParametersFromFile(filePath); + + List> configs = new ArrayList<>(); + 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.add(0, config); + } + } + Map effectiveConfig = configs.get(0); + for (int i = 1; i < configs.size(); i++){ + effectiveConfig.putAll(configs.get(i)); + } + + effectiveConfig.remove(INHERIT); + + return effectiveConfig; + } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index b23937250..90f9ed10f 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -32,7 +32,7 @@ T readValue(Class aClass, Map void invokeMethodWithMappedValue(List configItems, Method method, T instance, + private void invokeMethodWithMappedValue(List configItems, Method method, T instance, Map map) { method.setAccessible(true); if (method.getParameterTypes().length != 1) { @@ -228,7 +228,7 @@ private List getConvertedList(Class parameterType, Object mappedValue) return null; } - boolean isSetter(Method candidate) { + private boolean isSetter(Method candidate) { return candidate.getName().matches("^(set|add)[A-Z].*") && (candidate.getReturnType().equals(Void.TYPE) || candidate.getReturnType() .equals(candidate.getDeclaringClass())) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java index 6623ea5d8..20b7e4386 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Range.java @@ -46,27 +46,4 @@ public List registerConfigurationItems() { return configItems; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final Range range = (Range) o; - - return (head != null ? head.equals(range.head) : range.head == null) - && (tail != null ? tail.equals(range.tail) : range.tail == null); - } - - @Override - public int hashCode() { - int result = head != null ? head.hashCode() : 0; - result = 31 * result + (tail != null ? tail.hashCode() : 0); - - return result; - } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java index ec327c4e9..78e05f664 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Report.java @@ -45,23 +45,4 @@ public List registerConfigurationItems() { configItems.add(new ConfigurationItem("dir", null, TARGET)); return configItems; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final Report report = (Report) o; - - return enable == report.enable; - } - - @Override - public int hashCode() { - return (enable ? 1 : 0); - } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java index 97c00a27c..d38ab63b4 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Scm.java @@ -41,23 +41,4 @@ public List registerConfigurationItems() { return configItems; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - final Scm scm = (Scm) o; - - return range != null ? range.equals(scm.range) : scm.range == null; - } - - @Override - public int hashCode() { - return range != null ? range.hashCode() : 0; - } } diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java similarity index 70% rename from core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java rename to core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java index 793bad72e..1a07d25ef 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFile.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java @@ -7,44 +7,44 @@ import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; -class ConfigurationFile { +class ConfigurationFileBuilder { private HashMap properties; - private ConfigurationFile() { + private ConfigurationFileBuilder() { properties = new HashMap<>(); } - static ConfigurationFile SmartTestingConfigurationFile() { - return new ConfigurationFile(); + static ConfigurationFileBuilder SmartTestingConfigurationFile() { + return new ConfigurationFileBuilder(); } - ConfigurationFile inherit(String inherit) { + ConfigurationFileBuilder inherit(String inherit) { properties.put("inherit", inherit); return this; } - ConfigurationFile mode(String mode) { + ConfigurationFileBuilder mode(String mode) { properties.put("mode", mode); return this; } - ConfigurationFile applyTo(String applyTo) { + ConfigurationFileBuilder applyTo(String applyTo) { properties.put("applyTo", applyTo); return this; } - ConfigurationFile strategies(String strategies) { + ConfigurationFileBuilder strategies(String strategies) { properties.put("strategies", strategies); return this; } - ConfigurationFile debug(boolean debug) { + ConfigurationFileBuilder debug(boolean debug) { properties.put("debug", debug); return this; } - ConfigurationFile disable(boolean disable) { + ConfigurationFileBuilder disable(boolean disable) { properties.put("disable", disable); return this; } diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java index 911ec49e2..7db9a019e 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java @@ -8,7 +8,7 @@ import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.RunMode.SELECTING; -import static org.arquillian.smart.testing.configuration.ConfigurationFile.SmartTestingConfigurationFile; +import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.SmartTestingConfigurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.assertj.core.api.Assertions.assertThat; @@ -66,7 +66,7 @@ public void should_load_configuration_properties_from_relative_inherit_if_not_de .create(Paths.get(root, SMART_TESTING_YML)); // when - final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG, IMPL_BASE)); + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG, IMPL_BASE).toFile()); // then assertThat(configuration.getMode()).isEqualTo(SELECTING); @@ -92,7 +92,7 @@ public void should_not_overwrite_disable_parameter_from_inherit() throws IOExcep .create(Paths.get(root, SMART_TESTING_YML)); // when - final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG)); + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG).toFile()); // then assertThat(configuration.getMode()).isEqualTo(ORDERING); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java index 8fce8f06b..2bd23b184 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java @@ -11,7 +11,7 @@ import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING; -import static org.arquillian.smart.testing.configuration.ConfigurationFile.SmartTestingConfigurationFile; +import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.SmartTestingConfigurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheritTest.CONFIG; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; @@ -50,7 +50,7 @@ public void system_properties_should_take_precedence_over_config_file() throws I range.setTail(HEAD + "~3"); // when - final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG)); + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG).toFile()); // then assertThat(configuration.getStrategies()).isEqualTo(new String[] {"changed"}); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationTest.java index 4d13413ac..3ba5a4958 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationTest.java @@ -71,7 +71,7 @@ public void should_load_configuration_with_default_values_if_property_is_not_spe // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing.yml")); // then assertThat(actualConfiguration).isEqualToComparingFieldByFieldRecursively(expectedConfiguration); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationUsingPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationUsingPropertyTest.java index d41242bea..58515d630 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationUsingPropertyTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationUsingPropertyTest.java @@ -50,7 +50,7 @@ public void should_load_configuration_with_overwriting_system_property_for_scmLa // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing-with-lastChanges.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing-with-lastChanges.yml")); // then final Range range = actualConfiguration.getScm().getRange(); @@ -61,7 +61,7 @@ public void should_load_configuration_with_overwriting_system_property_for_scmLa public void should_load_configuration_for_scmLastChanges_from_config_file() { // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing-with-lastChanges.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing-with-lastChanges.yml")); // then final Range range = actualConfiguration.getScm().getRange(); @@ -72,7 +72,7 @@ public void should_load_configuration_for_scmLastChanges_from_config_file() { public void should_load_configuration_for_rangeHead_and_rangeTail_from_config_file() { // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing.yml")); // then final Range range = actualConfiguration.getScm().getRange(); @@ -87,7 +87,7 @@ public void should_load_configuration_with_overwriting_system_property_for_range // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing.yml")); // then final Range range = actualConfiguration.getScm().getRange(); @@ -141,7 +141,7 @@ public void should_load_configuration_with_overwriting_system_properties_over_pr // when final Configuration actualConfiguration = - ConfigurationLoader.load(getResourceAsPath("configuration/smart-testing.yml")); + ConfigurationLoader.load(getResourceAsFile("configuration/smart-testing.yml")); // then assertThat(actualConfiguration).isEqualToComparingFieldByFieldRecursively(expectedConfiguration); From 09cf3c86b0c5dcb1547db4e1931fd4285f4aa881 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Tue, 28 Nov 2017 16:31:22 +0530 Subject: [PATCH 04/15] use Deque for implentation --- .../testing/configuration/ConfigurationReader.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index 200009e3c..43496e698 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -6,9 +6,11 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Deque; import java.util.List; import java.util.Map; import org.arquillian.smart.testing.logger.Log; @@ -94,20 +96,20 @@ private Path getDefaultConfigFile(File... files) { private Map readEffectiveConfig(Path filePath){ Map config = getConfigParametersFromFile(filePath); - - List> configs = new ArrayList<>(); + Deque> 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.add(0, config); + configs.addFirst(config); } } - Map effectiveConfig = configs.get(0); - for (int i = 1; i < configs.size(); i++){ - effectiveConfig.putAll(configs.get(i)); + + Map effectiveConfig = configs.pollFirst(); + while (!configs.isEmpty()) { + effectiveConfig.putAll(configs.pollFirst()); } effectiveConfig.remove(INHERIT); From 3dd67deea55368c2f30643a4d4fc44762ddf5e1f Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Tue, 28 Nov 2017 16:31:46 +0530 Subject: [PATCH 05/15] Rename method def in config file builder --- .../ConfigurationFileBuilder.java | 4 +-- ...onfigurationOverWriteUsingInheritTest.java | 30 +++++++++---------- ...iteUsingInheritWithSystemPropertyTest.java | 10 +++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java index 1a07d25ef..c6ae10c0b 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java @@ -15,7 +15,7 @@ private ConfigurationFileBuilder() { properties = new HashMap<>(); } - static ConfigurationFileBuilder SmartTestingConfigurationFile() { + static ConfigurationFileBuilder configurationFile() { return new ConfigurationFileBuilder(); } @@ -49,7 +49,7 @@ ConfigurationFileBuilder disable(boolean disable) { return this; } - void create(Path filePath) { + void writeTo(Path filePath) { DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setPrettyFlow(true); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java index 7db9a019e..8ff8e6c4b 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java @@ -8,7 +8,7 @@ import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.RunMode.SELECTING; -import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.SmartTestingConfigurationFile; +import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.configurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.assertj.core.api.Assertions.assertThat; @@ -26,15 +26,15 @@ public void should_load_configuration_properties_from_absolute_inherit_if_not_de // given final String root = temporaryFolder.getRoot().toString(); - SmartTestingConfigurationFile() + configurationFile() .mode("ordering") .applyTo("surefire") .inherit(Paths.get(root, SMART_TESTING_YAML).toString()) - .create(Paths.get(root, SMART_TESTING_YML)); + .writeTo(Paths.get(root, SMART_TESTING_YML)); - SmartTestingConfigurationFile() + configurationFile() .strategies("new, changed, affected") - .create(Paths.get(root, SMART_TESTING_YAML)); + .writeTo(Paths.get(root, SMART_TESTING_YAML)); // when final Configuration configuration = ConfigurationLoader.load(temporaryFolder.getRoot()); @@ -51,19 +51,19 @@ public void should_load_configuration_properties_from_relative_inherit_if_not_de temporaryFolder.newFolder(CONFIG, IMPL_BASE); final String root = temporaryFolder.getRoot().toString(); - SmartTestingConfigurationFile() + configurationFile() .inherit("../smart-testing.yml") - .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); + .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); - SmartTestingConfigurationFile() + configurationFile() .inherit("../smart-testing.yml") .mode("selecting") .debug(true) - .create(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML)); + .writeTo(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML)); - SmartTestingConfigurationFile() + configurationFile() .strategies("new, changed, affected") - .create(Paths.get(root, SMART_TESTING_YML)); + .writeTo(Paths.get(root, SMART_TESTING_YML)); // when final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG, IMPL_BASE).toFile()); @@ -80,16 +80,16 @@ public void should_not_overwrite_disable_parameter_from_inherit() throws IOExcep temporaryFolder.newFolder(CONFIG); final String root = temporaryFolder.getRoot().toString(); - SmartTestingConfigurationFile() + configurationFile() .inherit("../smart-testing.yml") .mode("ordering") .disable(true) - .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); + .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); - SmartTestingConfigurationFile() + configurationFile() .strategies("new, changed, affected") .disable(false) - .create(Paths.get(root, SMART_TESTING_YML)); + .writeTo(Paths.get(root, SMART_TESTING_YML)); // when final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG).toFile()); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java index 2bd23b184..573edcb27 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java @@ -11,7 +11,7 @@ import static org.arquillian.smart.testing.RunMode.ORDERING; import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING; -import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.SmartTestingConfigurationFile; +import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.configurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheritTest.CONFIG; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; @@ -36,14 +36,14 @@ public void system_properties_should_take_precedence_over_config_file() throws I temporaryFolder.newFolder(CONFIG); final String root = temporaryFolder.getRoot().toString(); - SmartTestingConfigurationFile() + configurationFile() .inherit("../smart-testing.yml") .mode("ordering") - .create(Paths.get(root, CONFIG, SMART_TESTING_YML)); + .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); - SmartTestingConfigurationFile() + configurationFile() .strategies("new, changed, affected") - .create(Paths.get(root, SMART_TESTING_YML)); + .writeTo(Paths.get(root, SMART_TESTING_YML)); final Range range = new Range(); range.setHead(HEAD); From 24d40e5aef5314eb3c6e2e77ef32110c0c548ce8 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Thu, 30 Nov 2017 14:01:09 +0530 Subject: [PATCH 06/15] Adds changes as per review --- .../testing/configuration/ConfigLookup.java | 39 ++------------ .../testing/configuration/Configuration.java | 19 ++----- .../configuration/ConfigurationLoader.java | 10 ++-- .../configuration/ConfigurationReader.java | 50 +++++++++--------- .../testing/configuration/ObjectMapper.java | 5 ++ ...nfigurationOverwriteUsingInheritTest.java} | 16 +++--- ...teUsingInheritWithSystemPropertyTest.java} | 4 +- .../configuration/ObjectMapperTest.java | 29 +++++------ ...igurationFilePerModuleFunctionalTest.java} | 51 ++++++++++++++----- .../mvn/ext/ModuleConfigurationChecker.java | 39 ++++++++++++++ .../mvn/ext/SmartTestingMavenConfigurer.java | 42 +++++---------- 11 files changed, 157 insertions(+), 147 deletions(-) rename core/src/test/java/org/arquillian/smart/testing/configuration/{ConfigurationOverWriteUsingInheritTest.java => ConfigurationOverwriteUsingInheritTest.java} (94%) rename core/src/test/java/org/arquillian/smart/testing/configuration/{ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java => ConfigurationOverwriteUsingInheritWithSystemPropertyTest.java} (95%) rename functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/{ConfigurationFilePerModuleTest.java => ConfigurationFilePerModuleFunctionalTest.java} (67%) create mode 100644 mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigLookup.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigLookup.java index 317179a93..7c08ce65e 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigLookup.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigLookup.java @@ -1,28 +1,22 @@ package org.arquillian.smart.testing.configuration; import java.io.File; -import java.io.IOException; -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.function.Function; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; -public class ConfigLookup { +class ConfigLookup { private final File executionRootDir; private final Function stopCondition; - public ConfigLookup(File executionRootDir, Function stopCondition) { + ConfigLookup(File executionRootDir, Function stopCondition) { this.executionRootDir = executionRootDir; this.stopCondition = stopCondition; } - public File getFirstDirWithConfigOrWithStopCondition() { + File getFirstDirWithConfigOrWithStopCondition() { return getFirstDirWithConfigOrWithStopCondition(executionRootDir); } @@ -38,31 +32,4 @@ private File getFirstDirWithConfigOrWithStopCondition(File projectDir) { return getFirstDirWithConfigOrWithStopCondition(projectDir.getParentFile()); } - - private boolean isSameFile(Path path1, Path path2) { - try { - return Files.isSameFile(path1, path2); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public boolean isConfigFromProjectRootDir() { - final File configOrProjectRootDir = getFirstDirWithConfigOrWithStopCondition(); - return isSameFile(configOrProjectRootDir.toPath(), executionRootDir.toPath()); - } - - public boolean hasMoreThanOneConfigFile(String... fileNames){ - final long count; - try { - count = Files.walk(Paths.get(executionRootDir.getAbsolutePath())) - .parallel() - .filter(p -> Arrays.asList(fileNames).contains(p.toFile().getName())) - .count(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - return count > 1; - } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java index 5b7ceceac..0d29b649e 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java @@ -9,8 +9,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; 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; @@ -21,6 +21,7 @@ import org.yaml.snakeyaml.Yaml; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; +import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject; public class Configuration implements ConfigurationSection { @@ -37,14 +38,12 @@ 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 DISABLE = "disable"; static final String INHERIT = "inherit"; private String[] strategies = new String[0]; private String[] customStrategies = new String[0]; private RunMode mode; private String applyTo; - private String inherit; private boolean disable; private boolean debug; @@ -86,14 +85,6 @@ public void setApplyTo(String applyTo) { this.applyTo = applyTo; } - public String getInherit() { - return inherit; - } - - public void setInherit(String inherit) { - this.inherit = inherit; - } - public boolean isDisable() { return disable; } @@ -155,11 +146,10 @@ public List registerConfigurationItems() { configItems.add(new ConfigurationItem("strategies", SMART_TESTING, new String[0])); configItems.add(new ConfigurationItem("mode", SMART_TESTING_MODE, RunMode.valueOf(DEFAULT_MODE.toUpperCase()))); configItems.add(new ConfigurationItem("applyTo", SMART_TESTING_APPLY_TO)); - configItems.add(new ConfigurationItem(DISABLE, SMART_TESTING_DISABLE, false)); + configItems.add(new ConfigurationItem("disable", SMART_TESTING_DISABLE, false)); configItems.add(new ConfigurationItem("debug", SMART_TESTING_DEBUG, false)); configItems.add(new ConfigurationItem("autocorrect", SMART_TESTING_AUTOCORRECT, false)); configItems.add(new ConfigurationItem("customStrategies", SMART_TESTING_CUSTOM_STRATEGIES_PATTERN)); - configItems.add(new ConfigurationItem(INHERIT, null)); return configItems; } @@ -178,7 +168,6 @@ private List getStrategiesConfigurations(String... strate } private StrategyConfiguration loadStrategyConfiguration(StrategyConfiguration strategyConfiguration) { - final ObjectMapper objectMapper = new ObjectMapper(); final Class strategyConfigurationClass = (Class) strategyConfiguration.getClass(); final Object strategyConfig = strategiesConfig.get(strategyConfiguration.name()); @@ -187,7 +176,7 @@ private StrategyConfiguration loadStrategyConfiguration(StrategyConfiguration st strategyConfigMap = (Map) strategyConfig; } - return objectMapper.readValue(strategyConfigurationClass, strategyConfigMap); + return mapToObject(strategyConfigurationClass, strategyConfigMap); } public File dump(File rootDir) { diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java index 9e9692569..5bb4dd7e2 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java @@ -12,6 +12,8 @@ import org.arquillian.smart.testing.logger.Logger; import org.yaml.snakeyaml.Yaml; +import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject; + public class ConfigurationLoader { public static final String SMART_TESTING_YML = "smart-testing.yml"; @@ -69,9 +71,9 @@ public static Configuration load(File projectDir, Function stopCo private static Configuration loadEffectiveConfiguration(File configFile) { ConfigurationReader configurationReader = new ConfigurationReader(); - final Map effectiveConfig = configurationReader.readConfiguration(configFile); + final Map effectiveConfig = configurationReader.readEffectiveConfiguration(configFile); - return ConfigurationLoader.loadAsConfiguration(effectiveConfig); + return loadAsConfiguration(effectiveConfig); } /** @@ -114,8 +116,8 @@ static Configuration loadConfigurationFromFile(File configFile) { private static Configuration loadAsConfiguration(Map yamlConfiguration) { final Object strategiesConfiguration = yamlConfiguration.get("strategiesConfiguration"); - final ObjectMapper objectMapper = new ObjectMapper(); - final Configuration configuration = objectMapper.readValue(Configuration.class, yamlConfiguration); + + final Configuration configuration = mapToObject(Configuration.class, yamlConfiguration); if (strategiesConfiguration != null) { configuration.setStrategiesConfig((Map) strategiesConfiguration); } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index 43496e698..e27c1005d 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -7,11 +7,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; -import java.util.List; import java.util.Map; import org.arquillian.smart.testing.logger.Log; import org.arquillian.smart.testing.logger.Logger; @@ -25,7 +23,7 @@ class ConfigurationReader { private static final Logger logger = Log.getLogger(); - Map readConfiguration(File configPath) { + Map readEffectiveConfiguration(File configPath) { if (!configPath.isDirectory()) { return readEffectiveConfig(getConfigurationFilePath(configPath)); } @@ -46,6 +44,29 @@ Map readConfiguration(File configPath) { return Collections.emptyMap(); } + private Map readEffectiveConfig(Path filePath){ + Map config = getConfigParametersFromFile(filePath); + Deque> 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 effectiveConfig = configs.pollFirst(); + while (!configs.isEmpty()) { + effectiveConfig.putAll(configs.pollFirst()); + } + + effectiveConfig.remove(INHERIT); + + return effectiveConfig; + } + private Map getConfigParametersFromFile(Path filePath) { if (!filePath.toFile().exists()) { logger.warn(String.format("The configuration file %s is not exists.", filePath)); @@ -93,27 +114,4 @@ private Path getDefaultConfigFile(File... files) { .findFirst() .get(); } - - private Map readEffectiveConfig(Path filePath){ - Map config = getConfigParametersFromFile(filePath); - Deque> 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 effectiveConfig = configs.pollFirst(); - while (!configs.isEmpty()) { - effectiveConfig.putAll(configs.pollFirst()); - } - - effectiveConfig.remove(INHERIT); - - return effectiveConfig; - } } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index 90f9ed10f..dd548bca9 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -16,6 +16,11 @@ class ObjectMapper { + static T mapToObject(Class aClass, Map map) { + final ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(aClass, map); + } + T readValue(Class aClass, Map map) { T instance; try { diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java similarity index 94% rename from core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java rename to core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java index 8ff8e6c4b..bb1780c95 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java @@ -13,7 +13,7 @@ import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; import static org.assertj.core.api.Assertions.assertThat; -public class ConfigurationOverWriteUsingInheritTest { +public class ConfigurationOverwriteUsingInheritTest { private static final String IMPL_BASE = "impl-base"; static final String CONFIG = "config"; @@ -22,7 +22,7 @@ public class ConfigurationOverWriteUsingInheritTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test - public void should_load_configuration_properties_from_absolute_inherit_if_not_defined_in_child() throws IOException { + public void should_load_configuration_properties_from_absolute_inherit_path_if_not_defined_in_child() throws IOException { // given final String root = temporaryFolder.getRoot().toString(); @@ -46,21 +46,21 @@ public void should_load_configuration_properties_from_absolute_inherit_if_not_de } @Test - public void should_load_configuration_properties_from_relative_inherit_if_not_defined_in_child () throws IOException { + public void should_load_configuration_properties_from_relative_inherit_path_if_not_defined_in_child () throws IOException { // given temporaryFolder.newFolder(CONFIG, IMPL_BASE); final String root = temporaryFolder.getRoot().toString(); - configurationFile() - .inherit("../smart-testing.yml") - .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); - configurationFile() .inherit("../smart-testing.yml") .mode("selecting") .debug(true) .writeTo(Paths.get(root, CONFIG, IMPL_BASE, SMART_TESTING_YML)); + configurationFile() + .inherit("../smart-testing.yml") + .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); + configurationFile() .strategies("new, changed, affected") .writeTo(Paths.get(root, SMART_TESTING_YML)); @@ -75,7 +75,7 @@ public void should_load_configuration_properties_from_relative_inherit_if_not_de } @Test - public void should_not_overwrite_disable_parameter_from_inherit() throws IOException { + public void should_not_overwrite_disable_parameter_from_inherit_path() throws IOException { // given temporaryFolder.newFolder(CONFIG); final String root = temporaryFolder.getRoot().toString(); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritWithSystemPropertyTest.java similarity index 95% rename from core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java rename to core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritWithSystemPropertyTest.java index 573edcb27..ad81f7cf1 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverWriteUsingInheritWithSystemPropertyTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritWithSystemPropertyTest.java @@ -13,13 +13,13 @@ import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING; import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.configurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; -import static org.arquillian.smart.testing.configuration.ConfigurationOverWriteUsingInheritTest.CONFIG; +import static org.arquillian.smart.testing.configuration.ConfigurationOverwriteUsingInheritTest.CONFIG; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; import static org.arquillian.smart.testing.scm.ScmRunnerProperties.SCM_LAST_CHANGES; import static org.assertj.core.api.Assertions.assertThat; @Category(NotThreadSafe.class) -public class ConfigurationOverWriteUsingInheritWithSystemPropertyTest { +public class ConfigurationOverwriteUsingInheritWithSystemPropertyTest { @Rule public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java index 64b908596..ba4c33370 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ObjectMapperTest.java @@ -17,6 +17,7 @@ import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.experimental.categories.Category; +import static org.arquillian.smart.testing.configuration.ObjectMapper.mapToObject; import static org.arquillian.smart.testing.configuration.ObjectMapperTest.TestEnum.FOO; import static org.assertj.core.api.Assertions.assertThat; @@ -28,8 +29,6 @@ public class ObjectMapperTest { private Map map; - private static final ObjectMapper MAPPER = new ObjectMapper(); - @Before public void initMap() { map = new HashMap<>(); @@ -41,7 +40,7 @@ public void should_set_integer_to_object() { map.put("i", 10); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("i", 10); @@ -53,7 +52,7 @@ public void should_set_double_to_object() { map.put("d", 10.0); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("d", 10.0); @@ -65,7 +64,7 @@ public void should_set_boolean_to_object() { map.put("b", true); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("b", true); @@ -77,7 +76,7 @@ public void should_set_string_to_object() { map.put("s", "Hello"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("s", "Hello"); @@ -89,7 +88,7 @@ public void should_set_comma_separated_string_as_array_to_object() { map.put("as", "hello, bar"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("as", new String[] {"hello", "bar"}); @@ -101,7 +100,7 @@ public void should_set_list_to_object() { map.put("l", Arrays.asList("foo", "bar")); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); assertThat(testObject).hasFieldOrPropertyWithValue("l", Arrays.asList("foo", "bar")); } @@ -112,7 +111,7 @@ public void should_set_enum_to_object() { map.put("e", "foo"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("e", FOO); @@ -124,7 +123,7 @@ public void should_read_multiple_system_properties_expression() { System.setProperty("my.property.x", "smart"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=smart"}); @@ -136,7 +135,7 @@ public void should_set_multiple_property_to_object() { map.put("multiple", "my.property.x=smart"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=smart"}); @@ -149,7 +148,7 @@ public void should_override_multiple_property_with_same_key_by_system_property_v System.setProperty("my.property.x", "new-smart"); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("multiple", new String[]{"my.property.x=new-smart"}); @@ -170,7 +169,7 @@ public void should_set_nested_object_to_object() { dummyObject.setL(Arrays.asList("foo", "bar")); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).isNotNull(); @@ -186,7 +185,7 @@ public void should_set_map_to_object() { map.put("m", stringMap); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, map); + final TestObject testObject = mapToObject(TestObject.class, map); // then assertThat(testObject).hasFieldOrPropertyWithValue("m", stringMap); @@ -213,7 +212,7 @@ public void should_return_default_value_for_empty_map() { expected.setDummyObject(dummyObject); // when - final TestObject testObject = MAPPER.readValue(TestObject.class, Collections.emptyMap()); + final TestObject testObject = mapToObject(TestObject.class, Collections.emptyMap()); // then assertThat(testObject).isEqualToComparingFieldByFieldRecursively(expected); diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java similarity index 67% rename from functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java rename to functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java index af0cebc35..caac7d325 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java @@ -18,7 +18,7 @@ import static org.arquillian.smart.testing.ftest.testbed.configuration.Strategy.NEW; import static org.assertj.core.api.Assertions.assertThat; -public class ConfigurationFilePerModuleTest { +public class ConfigurationFilePerModuleFunctionalTest { @ClassRule public static final GitClone GIT_CLONE = new GitClone(testRepository()); @@ -26,15 +26,12 @@ public class ConfigurationFilePerModuleTest { public final TestBed testBed = new TestBed(GIT_CLONE); @Test - public void should_load_configuration_from_module_if_it_has_config_file() { + public void should_load_configuration_from_modules_config_file_for_local_changes_where_parent_config_file_has_only_strategies_defined() { // given final Project project = testBed.getProject(); final Configuration parentConfiguration = new ConfigurationBuilder() - .strategies(NEW, CHANGED) - .scm() - .lastChanges("2") - .build() + .strategies(CHANGED) .build(); final Configuration newConfiguration = new ConfigurationBuilder() @@ -42,6 +39,37 @@ public void should_load_configuration_from_module_if_it_has_config_file() { .mode(SELECTING) .build(); + project.configureSmartTesting() + .withConfiguration(parentConfiguration) + .createConfigFile() + .withConfiguration(newConfiguration) + .createConfigFileIn("config/impl-base") + .enable(); + + final Collection expectedTestResults = project + .applyAsLocalChanges("Adds new unit test"); + + + // when + final TestResults actualTestResults = project.build("config/impl-base").run(); + + // then + assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults).hasSameSizeAs(expectedTestResults); + } + + @Test + public void should_load_configuration_from_modules_config_file_for_scm_changes_where_parent_config_file_has_scm_snd_strategies_defined() { + // given + final Project project = testBed.getProject(); + + final Configuration parentConfiguration = new ConfigurationBuilder() + .strategies(NEW) + .scm() + .lastChanges("2") + .build() + .build(); + + final Configuration changedConfiguration = new ConfigurationBuilder() .strategies(CHANGED) .mode(SELECTING) @@ -50,22 +78,19 @@ public void should_load_configuration_from_module_if_it_has_config_file() { project.configureSmartTesting() .withConfiguration(parentConfiguration) .createConfigFile() - .withConfiguration(newConfiguration) - .createConfigFileIn("config/impl-base") .withConfiguration(changedConfiguration) .createConfigFileIn("junit/core") .enable(); - final Collection expectedTestResults = project - .applyAsLocalChanges("Adds new unit test"); - expectedTestResults.addAll(project.applyAsCommits("Deletes one test", "Renames unit test")); + final Collection expectedTestResults = project.applyAsCommits("Deletes one test", "Renames unit test"); // when final TestResults actualTestResults = - project.build("junit/core", "config/impl-base").run(); + project.build("junit/core").run(); // then - assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults).hasSameSizeAs(expectedTestResults); + assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults) + .hasSameSizeAs(expectedTestResults); } } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java new file mode 100644 index 000000000..dc362ccfe --- /dev/null +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java @@ -0,0 +1,39 @@ +package org.arquillian.smart.testing.mvn.ext; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; + +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_CONFIG; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; +import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; + +class ModuleConfigurationChecker { + + private final String projectDir; + + ModuleConfigurationChecker(String projectDir) { + this.projectDir = projectDir; + } + + boolean hasModuleSpecificConfigurations() { + return System.getProperty(SMART_TESTING_CONFIG) == null && hasMoreThanOneConfigFile(); + } + + private boolean hasMoreThanOneConfigFile(){ + final long count; + try { + count = Files.walk(Paths.get(projectDir)) + .parallel() + .filter(p -> Arrays.asList(SMART_TESTING_YML, SMART_TESTING_YAML).contains(p.toFile().getName())) + .count(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + return count > 1; + } + +} diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index d85996748..ba8b9c5c9 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -6,14 +6,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Collectors; import org.apache.maven.AbstractMavenLifecycleParticipant; import org.apache.maven.MavenExecutionException; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Model; import org.apache.maven.project.MavenProject; -import org.arquillian.smart.testing.configuration.ConfigLookup; import org.arquillian.smart.testing.configuration.Configuration; import org.arquillian.smart.testing.configuration.ConfigurationLoader; import org.arquillian.smart.testing.hub.storage.ChangeStorage; @@ -31,9 +29,6 @@ import static java.util.stream.StreamSupport.stream; import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; -import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_CONFIG; -import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; -import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; @Component(role = AbstractMavenLifecycleParticipant.class, description = "Entry point to install and manage Smart-Testing extension. Takes care of adding needed dependencies and " @@ -140,37 +135,28 @@ private void calculateChanges(File projectDirectory, Configuration configuration private void configureExtension(MavenSession session, Configuration configuration) { final Consumer configureSmartTestingExtensionAction; - final File executionRootDirectory = new File(session.getExecutionRootDirectory()); - if (hasModuleSpecificConfigurations(executionRootDirectory, this::isProjectRootDirectory)) { - configureSmartTestingExtensionAction = applyModuleSpecificConfiguration(configuration); + final ModuleConfigurationChecker moduleConfigurationChecker = + new ModuleConfigurationChecker(session.getExecutionRootDirectory()); + if (moduleConfigurationChecker.hasModuleSpecificConfigurations()) { + configureSmartTestingExtensionAction = applyModuleSpecificConfiguration(); } else { - final MavenProjectConfigurator mavenProjectConfigurator = new MavenProjectConfigurator(configuration); - configureSmartTestingExtensionAction = mavenProject -> configureMavenProject(mavenProjectConfigurator, mavenProject, configuration); + configureSmartTestingExtensionAction = mavenProject -> configureMavenProject(mavenProject, configuration); } session.getAllProjects().forEach(configureSmartTestingExtensionAction); } - private boolean hasModuleSpecificConfigurations(File projectDir, Function stopCondition) { - final ConfigLookup lookUp = new ConfigLookup(projectDir, stopCondition); - final boolean moreThanOneConfigFile = lookUp.hasMoreThanOneConfigFile(SMART_TESTING_YML, SMART_TESTING_YAML); - return moreThanOneConfigFile && System.getProperty(SMART_TESTING_CONFIG) == null; - } - - private Consumer applyModuleSpecificConfiguration(Configuration configuration) { + private Consumer applyModuleSpecificConfiguration() { return mavenProject -> { - final ConfigLookup configLookup = new ConfigLookup(mavenProject.getBasedir(), this::isProjectRootDirectory); - final File dirWithConfig = configLookup.getFirstDirWithConfigOrWithStopCondition(); - final Configuration mavenProjectConfiguration = configLookup.isConfigFromProjectRootDir() ? configuration : - ConfigurationLoader.load(dirWithConfig); + Configuration mavenProjectConfiguration = + ConfigurationLoader.load(mavenProject.getBasedir(), this::isProjectRootDirectory); if (mavenProjectConfiguration.isDisable()) { - logExtensionDisableReason(logger, - SMART_TESTING_DISABLE + " is set for module " + mavenProject.getArtifactId()); + logger.info("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", + ExtensionVersion.version().toString(), mavenProject.getArtifactId()); + return; } if (mavenProjectConfiguration.areStrategiesDefined()) { - final MavenProjectConfigurator mavenProjectConfigurator = - new MavenProjectConfigurator(mavenProjectConfiguration); - configureMavenProject(mavenProjectConfigurator, mavenProject, mavenProjectConfiguration); + configureMavenProject(mavenProject, mavenProjectConfiguration); } else { logger.warn( "Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " @@ -180,8 +166,8 @@ private Consumer applyModuleSpecificConfiguration(Configuration co }; } - private void configureMavenProject(MavenProjectConfigurator mavenProjectConfigurator, MavenProject mavenProject, - Configuration configuration) { + private void configureMavenProject(MavenProject mavenProject, Configuration configuration) { + final MavenProjectConfigurator mavenProjectConfigurator = new MavenProjectConfigurator(configuration); boolean wasConfigured = mavenProjectConfigurator.configureTestRunner(mavenProject.getModel()); if (wasConfigured) { configuration.dump(mavenProject.getBasedir()); From a12c724255366201c806d39a3908c40e4bbdad5d Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Fri, 1 Dec 2017 10:30:44 +0530 Subject: [PATCH 07/15] * overwrite inner properties * add configurationchecker & moduleSTInstallationChecker --- .../configuration/ConfigurationReader.java | 17 +++++++- .../ConfigurationFileBuilder.java | 30 ++++++++++++++ ...onfigurationOverwriteUsingInheritTest.java | 36 +++++++++++++++++ ...Checker.java => ConfigurationChecker.java} | 4 +- .../mvn/ext/ModuleSTInstallationChecker.java | 39 +++++++++++++++++++ .../mvn/ext/SmartTestingMavenConfigurer.java | 23 ++++------- 6 files changed, 131 insertions(+), 18 deletions(-) rename mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/{ModuleConfigurationChecker.java => ConfigurationChecker.java} (93%) create mode 100644 mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index e27c1005d..9ec257bc4 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -59,7 +59,7 @@ private Map readEffectiveConfig(Path filePath){ Map effectiveConfig = configs.pollFirst(); while (!configs.isEmpty()) { - effectiveConfig.putAll(configs.pollFirst()); + effectiveConfig = overwriteInnerProperties(effectiveConfig, configs.pollFirst()); } effectiveConfig.remove(INHERIT); @@ -67,6 +67,21 @@ private Map readEffectiveConfig(Path filePath){ return effectiveConfig; } + private Map overwriteInnerProperties(Map effective, Map inner) { + for (String key: inner.keySet()) { + if (!Map.class.isAssignableFrom(inner.get(key).getClass()) || !effective.containsKey(key)){ + effective.put(key, inner.get(key)); + continue; + } + + final Map effectiveValue = ((Map) effective.get(key)); + final Map innerValue = ((Map) inner.get(key)); + effective.put(key, overwriteInnerProperties(effectiveValue, innerValue)); + } + + return effective; + } + private Map getConfigParametersFromFile(Path filePath) { if (!filePath.toFile().exists()) { logger.warn(String.format("The configuration file %s is not exists.", filePath)); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java index c6ae10c0b..5845dcf63 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationFileBuilder.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; +import java.util.Map; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -49,6 +50,35 @@ ConfigurationFileBuilder disable(boolean disable) { return this; } + ConfigurationFileBuilder scm() { + properties.put("scm", new HashMap<>(0)); + return this; + } + + ConfigurationFileBuilder range() { + Map scm = new HashMap<>(); + scm.put("range", new HashMap<>(0)); + + properties.put("scm", scm); + return this; + } + + ConfigurationFileBuilder head(String head) { + final Map scm = (Map) properties.get("scm"); + final Map range = (Map) scm.get("range"); + range.put("head", head); + + return this; + } + + ConfigurationFileBuilder tail(String tail) { + final Map scm = (Map) properties.get("scm"); + final Map range = (Map) scm.get("range"); + range.put("tail", tail); + + return this; + } + void writeTo(Path filePath) { DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); diff --git a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java index bb1780c95..12384c3f7 100644 --- a/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java +++ b/core/src/test/java/org/arquillian/smart/testing/configuration/ConfigurationOverwriteUsingInheritTest.java @@ -11,6 +11,7 @@ import static org.arquillian.smart.testing.configuration.ConfigurationFileBuilder.configurationFile; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; +import static org.arquillian.smart.testing.scm.ScmRunnerProperties.HEAD; import static org.assertj.core.api.Assertions.assertThat; public class ConfigurationOverwriteUsingInheritTest { @@ -99,4 +100,39 @@ public void should_not_overwrite_disable_parameter_from_inherit_path() throws IO assertThat(configuration.isDisable()).isTrue(); assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); } + + @Test + public void should_aggregate_nested_parameters_from_inherit_path() throws IOException { + // given + temporaryFolder.newFolder(CONFIG); + final String root = temporaryFolder.getRoot().toString(); + + configurationFile() + .inherit("../smart-testing.yml") + .mode("ordering") + .disable(true) + .scm() + .range() + .head(HEAD) + .writeTo(Paths.get(root, CONFIG, SMART_TESTING_YML)); + + configurationFile() + .strategies("new, changed, affected") + .disable(false) + .scm() + .range() + .tail(HEAD + "~1") + .writeTo(Paths.get(root, SMART_TESTING_YML)); + + // when + final Configuration configuration = ConfigurationLoader.load(Paths.get(root, CONFIG).toFile()); + + // then + final Range range = configuration.getScm().getRange(); + assertThat(configuration.getMode()).isEqualTo(ORDERING); + assertThat(configuration.isDisable()).isTrue(); + assertThat(configuration.getStrategies()).isEqualTo(new String[]{"new", "changed", "affected"}); + assertThat(range.getHead()).isEqualTo(HEAD); + assertThat(range.getTail()).isEqualTo(HEAD + "~1"); + } } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigurationChecker.java similarity index 93% rename from mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java rename to mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigurationChecker.java index dc362ccfe..ac0c03b84 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleConfigurationChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ConfigurationChecker.java @@ -10,11 +10,11 @@ import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YAML; import static org.arquillian.smart.testing.configuration.ConfigurationLoader.SMART_TESTING_YML; -class ModuleConfigurationChecker { +class ConfigurationChecker { private final String projectDir; - ModuleConfigurationChecker(String projectDir) { + ConfigurationChecker(String projectDir) { this.projectDir = projectDir; } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java new file mode 100644 index 000000000..7ad7d7749 --- /dev/null +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java @@ -0,0 +1,39 @@ +package org.arquillian.smart.testing.mvn.ext; + +import org.apache.maven.project.MavenProject; +import org.arquillian.smart.testing.configuration.Configuration; +import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; + +import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; + +class ModuleSTInstallationChecker { + + private final Configuration configuration; + private final MavenProject mavenProject; + + private String reason; + + ModuleSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { + this.configuration = configuration; + this.mavenProject = mavenProject; + } + + String getReason() { + return reason; + } + + boolean shouldSkip() { + if (configuration.isDisable()) { + reason = String.format("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", + ExtensionVersion.version().toString(), mavenProject.getArtifactId()); + return true; + } + if (!configuration.areStrategiesDefined()) { + reason = String.format("Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " + + "For details on how to configure it head over to http://bit.ly/st-config", mavenProject.getArtifactId()); + return true; + } + return false; + } + +} diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index ba8b9c5c9..0b195e9aa 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -135,9 +135,9 @@ private void calculateChanges(File projectDirectory, Configuration configuration private void configureExtension(MavenSession session, Configuration configuration) { final Consumer configureSmartTestingExtensionAction; - final ModuleConfigurationChecker moduleConfigurationChecker = - new ModuleConfigurationChecker(session.getExecutionRootDirectory()); - if (moduleConfigurationChecker.hasModuleSpecificConfigurations()) { + final ConfigurationChecker configurationChecker = + new ConfigurationChecker(session.getExecutionRootDirectory()); + if (configurationChecker.hasModuleSpecificConfigurations()) { configureSmartTestingExtensionAction = applyModuleSpecificConfiguration(); } else { configureSmartTestingExtensionAction = mavenProject -> configureMavenProject(mavenProject, configuration); @@ -149,19 +149,12 @@ private Consumer applyModuleSpecificConfiguration() { return mavenProject -> { Configuration mavenProjectConfiguration = ConfigurationLoader.load(mavenProject.getBasedir(), this::isProjectRootDirectory); - if (mavenProjectConfiguration.isDisable()) { - logger.info("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", - ExtensionVersion.version().toString(), mavenProject.getArtifactId()); - - return; - } - if (mavenProjectConfiguration.areStrategiesDefined()) { - configureMavenProject(mavenProject, mavenProjectConfiguration); + final ModuleSTInstallationChecker moduleSTInstallationChecker = + new ModuleSTInstallationChecker(mavenProjectConfiguration, mavenProject); + if (moduleSTInstallationChecker.shouldSkip()) { + logger.info(moduleSTInstallationChecker.getReason()); } else { - logger.warn( - "Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " - + "For details on how to configure it head over to http://bit.ly/st-config", - mavenProject.getArtifactId()); + configureMavenProject(mavenProject, mavenProjectConfiguration); } }; } From 99944496e28e951c791eb9cfba200999a1b7b10e Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Tue, 5 Dec 2017 08:30:16 +0530 Subject: [PATCH 08/15] * documentation changes * code refactoring --- .../configuration/ConfigurationReader.java | 21 +++++++++++-------- .../testing/configuration/ObjectMapper.java | 2 +- docs/configfile.adoc | 13 ++++++------ ...figurationFilePerModuleFunctionalTest.java | 4 ++-- ...er.java => SkipSTInstallationChecker.java} | 4 ++-- .../mvn/ext/SmartTestingMavenConfigurer.java | 8 +++---- 6 files changed, 28 insertions(+), 24 deletions(-) rename mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/{ModuleSTInstallationChecker.java => SkipSTInstallationChecker.java} (91%) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index 9ec257bc4..134b2cdd1 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -59,7 +59,7 @@ private Map readEffectiveConfig(Path filePath){ Map effectiveConfig = configs.pollFirst(); while (!configs.isEmpty()) { - effectiveConfig = overwriteInnerProperties(effectiveConfig, configs.pollFirst()); + overwriteInnerProperties(effectiveConfig, configs.pollFirst()); } effectiveConfig.remove(INHERIT); @@ -67,19 +67,22 @@ private Map readEffectiveConfig(Path filePath){ return effectiveConfig; } - private Map overwriteInnerProperties(Map effective, Map inner) { + private void overwriteInnerProperties(Map effective, Map inner) { for (String key: inner.keySet()) { - if (!Map.class.isAssignableFrom(inner.get(key).getClass()) || !effective.containsKey(key)){ + if (isNonTrivialPropertyContainedInMap(key, inner, effective)) { effective.put(key, inner.get(key)); - continue; + } else { + final Map effectiveValue = ((Map) effective.get(key)); + final Map innerValue = ((Map) inner.get(key)); + overwriteInnerProperties(effectiveValue, innerValue); + effective.put(key, effectiveValue); } - - final Map effectiveValue = ((Map) effective.get(key)); - final Map innerValue = ((Map) inner.get(key)); - effective.put(key, overwriteInnerProperties(effectiveValue, innerValue)); } + } - return effective; + private boolean isNonTrivialPropertyContainedInMap(String key, Map inner, + Map effective) { + return !Map.class.isAssignableFrom(inner.get(key).getClass()) || !effective.containsKey(key); } private Map getConfigParametersFromFile(Path filePath) { diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index dd548bca9..38000306d 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -21,7 +21,7 @@ static T mapToObject(Class aClass, Map T readValue(Class aClass, Map map) { + private T readValue(Class aClass, Map map) { T instance; try { instance = aClass.newInstance(); diff --git a/docs/configfile.adoc b/docs/configfile.adoc index b43ad72b8..21c92a53f 100644 --- a/docs/configfile.adoc +++ b/docs/configfile.adoc @@ -37,7 +37,7 @@ You can look <<_reference_card, references>> for all supported system properties === How configuration is applying to each module? Smart Testing is looking in each module's root dir for configuration file. If it didn't find config file there, it'll look recursively for the first config file in -the parent dir containing pom.xml. +it's parent dir. === How Configuration parameters are inherited? If config file has `inherit` defined with absolute or relative path of config file, then Smart Testing will @@ -56,15 +56,15 @@ e.g. ```xml parent - config - - smart-testing.yml # + - smart-testing.yml # - api - impl-base - spi - - smart-testing.yml # + - smart-testing.yml # - container - api - impl-base - - smart-testing.yml # + - smart-testing.yml # ``` <1> @@ -75,7 +75,7 @@ strategies: affected ---- <2> -parent/config/spi/smart-testing.yml +.parent/config/spi/smart-testing.yml ---- strategies: affected,new ---- @@ -97,6 +97,7 @@ Configuration file selection for the above example will be as follows: * container/impl-base - parent/smart-testing.yml <1> inside this file we have inherit: ../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. +<2> inside this file we have only `strategies: affected,new`, so it won't inherit from any other configuration file. <3> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). === Configuration File Reference @@ -109,7 +110,7 @@ NOTE: You can use either a `.yml` or `.yaml` extension for this file. |Field | Description a| inherit -a| This is used to define absolute or relative path for configuration file from where you want to overwrite paremeters not defined in current config file. +a| This is used to define absolute or relative path to parent configuration file a| strategies a| This is used to define required strategies to find important tests. Look at <<_strategies, strategies>> for all supported options. diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java index caac7d325..1b95abb28 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java @@ -26,7 +26,7 @@ public class ConfigurationFilePerModuleFunctionalTest { public final TestBed testBed = new TestBed(GIT_CLONE); @Test - public void should_load_configuration_from_modules_config_file_for_local_changes_where_parent_config_file_has_only_strategies_defined() { + public void should_load_config_from_module_config_file_for_local_changes_instead_of_parent_config_with_strategy() { // given final Project project = testBed.getProject(); @@ -58,7 +58,7 @@ public void should_load_configuration_from_modules_config_file_for_local_changes } @Test - public void should_load_configuration_from_modules_config_file_for_scm_changes_where_parent_config_file_has_scm_snd_strategies_defined() { + public void should_load_config_from_module_config_file_for_local_changes_instead_of_parent_config_with_scm_and_strategy() { // given final Project project = testBed.getProject(); diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java similarity index 91% rename from mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java rename to mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java index 7ad7d7749..48c6b8184 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/ModuleSTInstallationChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java @@ -6,14 +6,14 @@ import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; -class ModuleSTInstallationChecker { +class SkipSTInstallationChecker { private final Configuration configuration; private final MavenProject mavenProject; private String reason; - ModuleSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { + SkipSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { this.configuration = configuration; this.mavenProject = mavenProject; } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index 0b195e9aa..8c99f4e45 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -149,10 +149,10 @@ private Consumer applyModuleSpecificConfiguration() { return mavenProject -> { Configuration mavenProjectConfiguration = ConfigurationLoader.load(mavenProject.getBasedir(), this::isProjectRootDirectory); - final ModuleSTInstallationChecker moduleSTInstallationChecker = - new ModuleSTInstallationChecker(mavenProjectConfiguration, mavenProject); - if (moduleSTInstallationChecker.shouldSkip()) { - logger.info(moduleSTInstallationChecker.getReason()); + final SkipSTInstallationChecker skipSTInstallationChecker = + new SkipSTInstallationChecker(mavenProjectConfiguration, mavenProject); + if (skipSTInstallationChecker.shouldSkip()) { + logger.info(skipSTInstallationChecker.getReason()); } else { configureMavenProject(mavenProject, mavenProjectConfiguration); } From 5debb6871606d07b6c997da9289444f92518984f Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Tue, 5 Dec 2017 08:38:32 +0530 Subject: [PATCH 09/15] Moved all skipChecker to checker package --- .../mvn/ext/MavenProjectConfigurator.java | 1 + .../mvn/ext/SmartTestingMavenConfigurer.java | 2 ++ .../{ => checker}/SkipInstallationChecker.java | 18 +++++++++--------- .../ext/{ => checker}/SkipModuleChecker.java | 13 +++++++------ .../SkipSTInstallationChecker.java | 10 +++++----- .../SkipInstallationCheckerTest.java | 11 ++++++----- 6 files changed, 30 insertions(+), 25 deletions(-) rename mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/{ => checker}/SkipInstallationChecker.java (81%) rename mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/{ => checker}/SkipModuleChecker.java (89%) rename mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/{ => checker}/SkipSTInstallationChecker.java (82%) rename mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/{ => checker}/SkipInstallationCheckerTest.java (92%) diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/MavenProjectConfigurator.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/MavenProjectConfigurator.java index 3e3f87a48..6103ca375 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/MavenProjectConfigurator.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/MavenProjectConfigurator.java @@ -12,6 +12,7 @@ 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.arquillian.smart.testing.mvn.ext.checker.SkipModuleChecker; import org.arquillian.smart.testing.mvn.ext.dependencies.DependencyResolver; import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; import org.arquillian.smart.testing.mvn.ext.dependencies.Version; diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index 8c99f4e45..25e69f818 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -19,6 +19,8 @@ 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.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker; +import org.arquillian.smart.testing.mvn.ext.checker.SkipSTInstallationChecker; import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; import org.arquillian.smart.testing.mvn.ext.logger.MavenExtensionLoggerFactory; import org.arquillian.smart.testing.scm.Change; diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java similarity index 81% rename from mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationChecker.java rename to mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java index a9801dda8..d13814876 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java @@ -1,4 +1,4 @@ -package org.arquillian.smart.testing.mvn.ext; +package org.arquillian.smart.testing.mvn.ext.checker; import java.util.Arrays; import java.util.List; @@ -6,14 +6,14 @@ import java.util.regex.Pattern; import org.apache.maven.execution.MavenSession; -import static org.arquillian.smart.testing.mvn.ext.SkipModuleChecker.MAVEN_TEST_SKIP; -import static org.arquillian.smart.testing.mvn.ext.SkipModuleChecker.SKIP_TESTS; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipModuleChecker.MAVEN_TEST_SKIP; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipModuleChecker.SKIP_TESTS; -class SkipInstallationChecker { +public class SkipInstallationChecker { private static final List EXPECTED_GOALS = Arrays.asList( - new String[] {"test", "prepare-package", "package", "pre-integration-test", "integration-test", - "post-integration-test", "verify", "install", "deploy", "pre-site", "site", "post-site", "site-deploy"}); + "test", "prepare-package", "package", "pre-integration-test", "integration-test", + "post-integration-test", "verify", "install", "deploy", "pre-site", "site", "post-site", "site-deploy"); static final String NO_GOAL_REASON = "No goals have been specified for the build."; static final String NO_TEST_GOAL_REASON = @@ -26,11 +26,11 @@ class SkipInstallationChecker { private final MavenSession session; private String reason; - SkipInstallationChecker(MavenSession session) { + public SkipInstallationChecker(MavenSession session) { this.session = session; } - boolean shouldSkip() { + public boolean shouldSkip() { List goals = session.getGoals(); String defaultGoal = session.getTopLevelProject().getBuild().getDefaultGoal(); if (goals.isEmpty() && (defaultGoal == null || defaultGoal.isEmpty())) { @@ -79,7 +79,7 @@ private boolean containsPattern(String testClasses) { .anyMatch(Matcher::find); } - String getReason(){ + public String getReason(){ return reason; } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipModuleChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipModuleChecker.java similarity index 89% rename from mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipModuleChecker.java rename to mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipModuleChecker.java index 42314b2e8..6cb097ed3 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipModuleChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipModuleChecker.java @@ -1,13 +1,14 @@ -package org.arquillian.smart.testing.mvn.ext; +package org.arquillian.smart.testing.mvn.ext.checker; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; +import org.arquillian.smart.testing.mvn.ext.ApplicablePlugins; import org.codehaus.plexus.util.xml.Xpp3Dom; import static org.arquillian.smart.testing.mvn.ext.ApplicablePlugins.FAILSAFE; import static org.arquillian.smart.testing.mvn.ext.ApplicablePlugins.SUREFIRE; -class SkipModuleChecker { +public class SkipModuleChecker { private final Model model; private final Plugin surefirePlugin; @@ -18,17 +19,17 @@ class SkipModuleChecker { public static final String SKIP = "skip"; public static final String MAVEN_TEST_SKIP = "maven.test.skip"; - SkipModuleChecker(Model model) { + public SkipModuleChecker(Model model) { this.model = model; this.surefirePlugin = getPlugin(SUREFIRE); this.failsafePlugin = getPlugin(FAILSAFE); } - boolean areIntegrationTestsSkipped() { + public boolean areIntegrationTestsSkipped() { return Boolean.valueOf(System.getProperty(SKIP_ITs)) || isSkipITsSetInPom(); } - boolean areUnitTestsSkipped() { + public boolean areUnitTestsSkipped() { if (surefirePlugin != null) { Xpp3Dom surefirePluginConfiguration = (Xpp3Dom) surefirePlugin.getConfiguration(); if (surefirePluginConfiguration != null) { @@ -39,7 +40,7 @@ boolean areUnitTestsSkipped() { return false; } - boolean areAllTestsSkipped() { + public boolean areAllTestsSkipped() { return isPropertyInPom(MAVEN_TEST_SKIP) || isPropertyInPom(SKIP_TESTS); } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java similarity index 82% rename from mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java rename to mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java index 48c6b8184..93a4b5611 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SkipSTInstallationChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java @@ -1,4 +1,4 @@ -package org.arquillian.smart.testing.mvn.ext; +package org.arquillian.smart.testing.mvn.ext.checker; import org.apache.maven.project.MavenProject; import org.arquillian.smart.testing.configuration.Configuration; @@ -6,23 +6,23 @@ import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; -class SkipSTInstallationChecker { +public class SkipSTInstallationChecker { private final Configuration configuration; private final MavenProject mavenProject; private String reason; - SkipSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { + public SkipSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { this.configuration = configuration; this.mavenProject = mavenProject; } - String getReason() { + public String getReason() { return reason; } - boolean shouldSkip() { + public boolean shouldSkip() { if (configuration.isDisable()) { reason = String.format("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", ExtensionVersion.version().toString(), mavenProject.getArtifactId()); diff --git a/mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationCheckerTest.java b/mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationCheckerTest.java similarity index 92% rename from mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationCheckerTest.java rename to mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationCheckerTest.java index 6e1ebccaf..07d325460 100644 --- a/mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/SkipInstallationCheckerTest.java +++ b/mvn-extension/src/test/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationCheckerTest.java @@ -1,4 +1,4 @@ -package org.arquillian.smart.testing.mvn.ext; +package org.arquillian.smart.testing.mvn.ext.checker; import java.util.Arrays; import java.util.List; @@ -6,6 +6,7 @@ import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Build; import org.apache.maven.project.MavenProject; +import org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker; import org.assertj.core.api.JUnitSoftAssertions; import org.junit.Rule; import org.junit.Test; @@ -13,10 +14,10 @@ import org.junit.experimental.categories.Category; import org.mockito.Mockito; -import static org.arquillian.smart.testing.mvn.ext.SkipInstallationChecker.NO_GOAL_REASON; -import static org.arquillian.smart.testing.mvn.ext.SkipInstallationChecker.NO_TEST_GOAL_REASON; -import static org.arquillian.smart.testing.mvn.ext.SkipInstallationChecker.SPECIFIC_CLASSES_REASON; -import static org.arquillian.smart.testing.mvn.ext.SkipInstallationChecker.TEST_SKIPPED_REASON; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker.NO_GOAL_REASON; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker.NO_TEST_GOAL_REASON; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker.SPECIFIC_CLASSES_REASON; +import static org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker.TEST_SKIPPED_REASON; import static org.assertj.core.api.Assertions.assertThat; @Category(NotThreadSafe.class) From b2c0e998e108e9c708f931b69a3bb8e3ede31b8a Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 6 Dec 2017 18:30:14 +0530 Subject: [PATCH 10/15] Merging into one test --- ...figurationFilePerModuleFunctionalTest.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java index 1b95abb28..fe6c6abdc 100644 --- a/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java +++ b/functional-tests/test-bed/src/test/java/org/arquillian/smart/testing/ftest/configurationfile/ConfigurationFilePerModuleFunctionalTest.java @@ -25,38 +25,6 @@ public class ConfigurationFilePerModuleFunctionalTest { @Rule public final TestBed testBed = new TestBed(GIT_CLONE); - @Test - public void should_load_config_from_module_config_file_for_local_changes_instead_of_parent_config_with_strategy() { - // given - final Project project = testBed.getProject(); - - final Configuration parentConfiguration = new ConfigurationBuilder() - .strategies(CHANGED) - .build(); - - final Configuration newConfiguration = new ConfigurationBuilder() - .strategies(NEW) - .mode(SELECTING) - .build(); - - project.configureSmartTesting() - .withConfiguration(parentConfiguration) - .createConfigFile() - .withConfiguration(newConfiguration) - .createConfigFileIn("config/impl-base") - .enable(); - - final Collection expectedTestResults = project - .applyAsLocalChanges("Adds new unit test"); - - - // when - final TestResults actualTestResults = project.build("config/impl-base").run(); - - // then - assertThat(actualTestResults.accumulatedPerTestClass()).containsAll(expectedTestResults).hasSameSizeAs(expectedTestResults); - } - @Test public void should_load_config_from_module_config_file_for_local_changes_instead_of_parent_config_with_scm_and_strategy() { // given From 74042dcf42b4a437cc640a3e06b868f7a6da554e Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 6 Dec 2017 19:09:40 +0530 Subject: [PATCH 11/15] Update documentation --- docs/configfile.adoc | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/configfile.adoc b/docs/configfile.adoc index 21c92a53f..079d10155 100644 --- a/docs/configfile.adoc +++ b/docs/configfile.adoc @@ -46,42 +46,41 @@ lookup for all undefined properties in config file defined using inherit. Howeve If config file doesn't contain `inherit` parameter. Then Smart Testing won't inherit any configuration properties. === Configuration File per Maven Module: -You can define configuration file per maven module. However it's not mandatory to define configuration per each module. - -NOTE: Whenever you are using configuration file per maven module, make sure to define strategies in project's -parent configuration file or using system property. If you are running tests against range of commits, you need to define scm properties in project's parent config file or using system property. -It won't work if you define scm properties in any module's config file and not in parent config file. +Let's take a look into following example: e.g. -```xml +[source,xml] +---- parent - config - - smart-testing.yml # + - smart-testing.yml // <1> - api - impl-base - spi - - smart-testing.yml # + - smart-testing.yml // <2> - container - api - impl-base - - smart-testing.yml # -``` + - smart-testing.yml // <3> +---- + +Configuration files used in above example: -<1> .parent/config/smart-testing.yml +[source,yml] ---- inherit: ../smart-testing.yml strategies: affected ---- -<2> .parent/config/spi/smart-testing.yml +[source,yml] ---- strategies: affected,new ---- -<3> .parent/smart-testing.yml +[source,yml] ---- strategies: new scm: @@ -96,10 +95,18 @@ Configuration file selection for the above example will be as follows: * container/api - parent/smart-testing.yml * container/impl-base - parent/smart-testing.yml -<1> inside this file we have inherit: ../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. +<1> inside this file we have `inherit: ../smart-testing.yml`, so it will take all undefined properties in this file from the one in `parent/smart-testing.yml`. <2> inside this file we have only `strategies: affected,new`, so it won't inherit from any other configuration file. <3> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). +NOTE:In case of mvn extension, whenever you are using configuration file per maven module, make sure to define strategies in project's parent configuration file +or using system property. If you haven't defined strategy in parent config, Smart testing will load parent configuration having no strategy defined which will +disable ST by saying `Smart Testing is disabled. Reason: strategy not defined`. + +If you are running tests against range of commits locally, you need to also define scm properties in project's parent config file or using system +properties. If you haven't defined it in parent config, Smart Testing will use default scm configuration with head as `HEAD` & tail as `HEAD~0` +due to which Smart Testing won't find required changes in range of commits. + === Configuration File Reference The `const:core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java[name="SMART_TESTING_YML"]` file is a `YAML` file defining required configuration to configure Smart Testing. From 271aedb4e4969eaf546e62c90d8179f895bb5762 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 6 Dec 2017 19:20:16 +0530 Subject: [PATCH 12/15] Use SkipSTInstallationChecker in SmartTestingMavenConfigure --- .../mvn/ext/SmartTestingMavenConfigurer.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index 25e69f818..969df7818 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -62,14 +62,10 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti logger.debug("Applied user properties: %s", session.getUserProperties()); File projectDirectory = session.getTopLevelProject().getModel().getProjectDirectory(); - if (configuration.areStrategiesDefined()) { - logger.info("Enabling extension."); - configureExtension(session, configuration); - calculateChanges(projectDirectory, configuration); - Runtime.getRuntime().addShutdownHook(new Thread(() -> purgeLocalStorageAndExportPom(session))); - } else { - logStrategiesNotDefined(); - } + logger.info("Enabling extension."); + configureExtension(session, configuration); + calculateChanges(projectDirectory, configuration); + Runtime.getRuntime().addShutdownHook(new Thread(() -> purgeLocalStorageAndExportPom(session))); } private void loadConfigAndCheckIfInstallationShouldBeSkipped(MavenSession session) { @@ -84,10 +80,11 @@ private void loadConfigAndCheckIfInstallationShouldBeSkipped(MavenSession sessio configuration = ConfigurationLoader.load(executionRootDirectory, this::isProjectRootDirectory); Log.setLoggerFactory(new MavenExtensionLoggerFactory(mavenLogger, configuration)); logger = Log.getLogger(); - - if (configuration.isDisable()) { + final SkipSTInstallationChecker skipSTInstallationChecker = + new SkipSTInstallationChecker(configuration, session.getTopLevelProject()); + if (skipSTInstallationChecker.shouldSkip()) { skipExtensionInstallation = true; - logExtensionDisableReason(logger, SMART_TESTING_DISABLE + " is set."); + logExtensionDisableReason(logger, skipSTInstallationChecker.getReason()); } } From e8df3109089c4ac4606753a389d3ee995ce40e05 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 6 Dec 2017 19:46:00 +0530 Subject: [PATCH 13/15] update doc --- docs/configfile.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/configfile.adoc b/docs/configfile.adoc index 079d10155..d6df715bb 100644 --- a/docs/configfile.adoc +++ b/docs/configfile.adoc @@ -99,10 +99,9 @@ Configuration file selection for the above example will be as follows: <2> inside this file we have only `strategies: affected,new`, so it won't inherit from any other configuration file. <3> inside this file we have `strategies: new` & `scm: lastChanges: 1` defined.(You can set it using respective system property also). -NOTE:In case of mvn extension, whenever you are using configuration file per maven module, make sure to define strategies in project's parent configuration file +IMPORTANT: In case of mvn extension, whenever you are using configuration file per maven module, make sure to define strategies in project's parent configuration file or using system property. If you haven't defined strategy in parent config, Smart testing will load parent configuration having no strategy defined which will disable ST by saying `Smart Testing is disabled. Reason: strategy not defined`. - If you are running tests against range of commits locally, you need to also define scm properties in project's parent config file or using system properties. If you haven't defined it in parent config, Smart Testing will use default scm configuration with head as `HEAD` & tail as `HEAD~0` due to which Smart Testing won't find required changes in range of commits. From 352d6480c18c3c91d0df772b284c0fc63e9f3289 Mon Sep 17 00:00:00 2001 From: Dipak Pawar Date: Wed, 6 Dec 2017 19:47:13 +0530 Subject: [PATCH 14/15] make mapToObject static --- .../configuration/ConfigurationLoader.java | 4 +- .../configuration/ConfigurationReader.java | 14 +++---- .../testing/configuration/ObjectMapper.java | 39 ++++++++----------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java index 5bb4dd7e2..974aff509 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationLoader.java @@ -12,6 +12,7 @@ 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 { @@ -70,8 +71,7 @@ public static Configuration load(File projectDir, Function stopCo } private static Configuration loadEffectiveConfiguration(File configFile) { - ConfigurationReader configurationReader = new ConfigurationReader(); - final Map effectiveConfig = configurationReader.readEffectiveConfiguration(configFile); + final Map effectiveConfig = readEffectiveConfiguration(configFile); return loadAsConfiguration(effectiveConfig); } diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java index 134b2cdd1..17434cce4 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ConfigurationReader.java @@ -23,7 +23,7 @@ class ConfigurationReader { private static final Logger logger = Log.getLogger(); - Map readEffectiveConfiguration(File configPath) { + static Map readEffectiveConfiguration(File configPath) { if (!configPath.isDirectory()) { return readEffectiveConfig(getConfigurationFilePath(configPath)); } @@ -44,7 +44,7 @@ Map readEffectiveConfiguration(File configPath) { return Collections.emptyMap(); } - private Map readEffectiveConfig(Path filePath){ + private static Map readEffectiveConfig(Path filePath){ Map config = getConfigParametersFromFile(filePath); Deque> configs = new ArrayDeque<>(); configs.add(config); @@ -67,7 +67,7 @@ private Map readEffectiveConfig(Path filePath){ return effectiveConfig; } - private void overwriteInnerProperties(Map effective, Map inner) { + private static void overwriteInnerProperties(Map effective, Map inner) { for (String key: inner.keySet()) { if (isNonTrivialPropertyContainedInMap(key, inner, effective)) { effective.put(key, inner.get(key)); @@ -80,12 +80,12 @@ private void overwriteInnerProperties(Map effective, Map inner, + private static boolean isNonTrivialPropertyContainedInMap(String key, Map inner, Map effective) { return !Map.class.isAssignableFrom(inner.get(key).getClass()) || !effective.containsKey(key); } - private Map getConfigParametersFromFile(Path filePath) { + private static Map getConfigParametersFromFile(Path filePath) { if (!filePath.toFile().exists()) { logger.warn(String.format("The configuration file %s is not exists.", filePath)); return Collections.emptyMap(); @@ -104,7 +104,7 @@ private Map getConfigParametersFromFile(Path filePath) { } } - private Path getConfigurationFilePath(File... files) { + private static Path getConfigurationFilePath(File... files) { Path configPath; if (files.length == 1) { configPath = files[0].toPath(); @@ -115,7 +115,7 @@ private Path getConfigurationFilePath(File... files) { return configPath; } - private Path getDefaultConfigFile(File... files) { + 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); diff --git a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java index 38000306d..08f28296e 100644 --- a/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java +++ b/core/src/main/java/org/arquillian/smart/testing/configuration/ObjectMapper.java @@ -17,11 +17,6 @@ class ObjectMapper { static T mapToObject(Class aClass, Map map) { - final ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(aClass, map); - } - - private T readValue(Class aClass, Map map) { T instance; try { instance = aClass.newInstance(); @@ -31,13 +26,13 @@ private T readValue(Class aClass, Map configItems = instance.registerConfigurationItems(); - Arrays.stream(aClass.getMethods()).filter(this::isSetter) + Arrays.stream(aClass.getMethods()).filter(ObjectMapper::isSetter) .forEach(method -> invokeMethodWithMappedValue(configItems, method, instance, map)); return instance; } - private void invokeMethodWithMappedValue(List configItems, Method method, T instance, + private static void invokeMethodWithMappedValue(List configItems, Method method, T instance, Map map) { method.setAccessible(true); if (method.getParameterTypes().length != 1) { @@ -58,7 +53,7 @@ private void invokeMethodWithMappedValue(List configItems } } - private Object getConvertedObject(Method method, Object configFileValue, + private static Object getConvertedObject(Method method, Object configFileValue, String property, List configItems) { Optional foundConfigItem = configItems.stream().filter(item -> property.equals(item.getParamName())).findFirst(); @@ -68,9 +63,9 @@ private Object getConvertedObject(Method method, Object configFileValue, if (!ConfigurationSection.class.isAssignableFrom(parameterType)) { return null; } else if (configFileValue == null) { - return readValue((Class) parameterType, Collections.emptyMap()); + return mapToObject((Class) parameterType, Collections.emptyMap()); } else { - return readValue((Class) parameterType, (Map) configFileValue); + return mapToObject((Class) parameterType, (Map) configFileValue); } } else { ConfigurationItem configItem = foundConfigItem.get(); @@ -86,7 +81,7 @@ private Object getConvertedObject(Method method, Object configFileValue, return null; } - private Object getUserSetProperty(Method method, ConfigurationItem configItem, Object configFileValue) { + private static Object getUserSetProperty(Method method, ConfigurationItem configItem, Object configFileValue) { if (configItem.getSystemProperty() != null) { if (!configItem.getSystemProperty().endsWith(".*")) { String sysPropertyValue = System.getProperty(configItem.getSystemProperty()); @@ -98,7 +93,7 @@ private Object getUserSetProperty(Method method, ConfigurationItem configItem, O return configFileValue; } - private List createMultipleOccurrenceProperty(Method method, ConfigurationItem configItem, + private static List createMultipleOccurrenceProperty(Method method, ConfigurationItem configItem, Object configFileValue) { String sysPropKey = configItem.getSystemProperty().substring(0, configItem.getSystemProperty().lastIndexOf('.')); @@ -125,7 +120,7 @@ private List createMultipleOccurrenceProperty(Method method, Configurati return null; } - private List getValuesFromFile(Method method, String sysPropKey, Object configFileValue, + private static List getValuesFromFile(Method method, String sysPropKey, Object configFileValue, Map systemProperties) { Class parameterType = method.getParameterTypes()[0]; ArrayList fromFileParam = new ArrayList<>(); @@ -142,7 +137,7 @@ private List getValuesFromFile(Method method, String sysPropKey, Object .collect(Collectors.toList()); } - private boolean isSetBySysProperty(Object param, String sysPropKey, Map systemProperties) { + private static boolean isSetBySysProperty(Object param, String sysPropKey, Map systemProperties) { String[] paramSplit = String.valueOf(param).split("="); if (paramSplit.length == 2) { String key = paramSplit[0]; @@ -151,7 +146,7 @@ private boolean isSetBySysProperty(Object param, String sysPropKey, Map parameterType = method.getParameterTypes()[0]; if (parameterType.isArray()) { return handleArray(parameterType.getComponentType(), mappedValue); @@ -162,13 +157,13 @@ private Object convert(Method method, Object mappedValue) { } else if (parameterType.isAssignableFrom(mappedValue.getClass())) { return mappedValue; } else if (ConfigurationSection.class.isAssignableFrom(parameterType)) { - return readValue((Class) parameterType, (Map) mappedValue); + return mapToObject((Class) parameterType, (Map) mappedValue); } else { return convertToType(parameterType, mappedValue.toString()); } } - private Enum handleEnum(Method method, Object mapValue) { + private static Enum handleEnum(Method method, Object mapValue) { if (mapValue.getClass().isEnum()) { return (Enum) mapValue; } @@ -177,7 +172,7 @@ private Enum handleEnum(Method method, Object mapValue) { return Enum.valueOf((Class) parameterTypes[0], value.toUpperCase()); } - private T[] handleArray(Class parameterType, Object mapValue) { + private static T[] handleArray(Class parameterType, Object mapValue) { if (mapValue != null && mapValue.getClass().isArray() && ((Object[]) mapValue).length == 0) { return (T[]) mapValue; } @@ -186,7 +181,7 @@ private T[] handleArray(Class parameterType, Object mapValue) { return convertedList.toArray(array); } - private Object handleList(Method method, Object mappedValue) { + private static Object handleList(Method method, Object mappedValue) { Type[] genericParameterTypes = method.getGenericParameterTypes(); if (genericParameterTypes.length == 1) { Type type = genericParameterTypes[0]; @@ -201,7 +196,7 @@ private Object handleList(Method method, Object mappedValue) { return null; } - private Object convertToType(Class clazz, String mappedValue) { + private static Object convertToType(Class clazz, String mappedValue) { if (Integer.class.equals(clazz) || int.class.equals(clazz)) { return Integer.valueOf(mappedValue); } else if (Double.class.equals(clazz) || double.class.equals(clazz)) { @@ -216,7 +211,7 @@ private Object convertToType(Class clazz, String mappedValue) { return null; } - private List getConvertedList(Class parameterType, Object mappedValue) { + private static List getConvertedList(Class parameterType, Object mappedValue) { final Class aClass = mappedValue.getClass(); if (List.class.isAssignableFrom(aClass)) { return (List) mappedValue; @@ -233,7 +228,7 @@ private List getConvertedList(Class parameterType, Object mappedValue) return null; } - private boolean isSetter(Method candidate) { + private static boolean isSetter(Method candidate) { return candidate.getName().matches("^(set|add)[A-Z].*") && (candidate.getReturnType().equals(Void.TYPE) || candidate.getReturnType() .equals(candidate.getDeclaringClass())) From ca7538149b20f0bfcf49f244e0b7379f36e48ba2 Mon Sep 17 00:00:00 2001 From: Matous Jobanek Date: Tue, 12 Dec 2017 15:59:07 +0100 Subject: [PATCH 15/15] moved SkipSTInstannationChecker logic to SkipInstallationChecker --- .../mvn/ext/SmartTestingMavenConfigurer.java | 15 +++---- .../ext/checker/SkipInstallationChecker.java | 28 ++++++++++++- .../checker/SkipSTInstallationChecker.java | 39 ------------------- 3 files changed, 32 insertions(+), 50 deletions(-) delete mode 100644 mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java index 969df7818..d1ffca7c0 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/SmartTestingMavenConfigurer.java @@ -20,7 +20,6 @@ import org.arquillian.smart.testing.logger.Log; import org.arquillian.smart.testing.logger.Logger; import org.arquillian.smart.testing.mvn.ext.checker.SkipInstallationChecker; -import org.arquillian.smart.testing.mvn.ext.checker.SkipSTInstallationChecker; import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; import org.arquillian.smart.testing.mvn.ext.logger.MavenExtensionLoggerFactory; import org.arquillian.smart.testing.scm.Change; @@ -30,7 +29,6 @@ import org.codehaus.plexus.component.annotations.Requirement; import static java.util.stream.StreamSupport.stream; -import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; @Component(role = AbstractMavenLifecycleParticipant.class, description = "Entry point to install and manage Smart-Testing extension. Takes care of adding needed dependencies and " @@ -80,11 +78,9 @@ private void loadConfigAndCheckIfInstallationShouldBeSkipped(MavenSession sessio configuration = ConfigurationLoader.load(executionRootDirectory, this::isProjectRootDirectory); Log.setLoggerFactory(new MavenExtensionLoggerFactory(mavenLogger, configuration)); logger = Log.getLogger(); - final SkipSTInstallationChecker skipSTInstallationChecker = - new SkipSTInstallationChecker(configuration, session.getTopLevelProject()); - if (skipSTInstallationChecker.shouldSkip()) { + if (skipInstallationChecker.shouldSkipForConfiguration(configuration)) { skipExtensionInstallation = true; - logExtensionDisableReason(logger, skipSTInstallationChecker.getReason()); + logExtensionDisableReason(logger, skipInstallationChecker.getReason()); } } @@ -148,10 +144,9 @@ private Consumer applyModuleSpecificConfiguration() { return mavenProject -> { Configuration mavenProjectConfiguration = ConfigurationLoader.load(mavenProject.getBasedir(), this::isProjectRootDirectory); - final SkipSTInstallationChecker skipSTInstallationChecker = - new SkipSTInstallationChecker(mavenProjectConfiguration, mavenProject); - if (skipSTInstallationChecker.shouldSkip()) { - logger.info(skipSTInstallationChecker.getReason()); + final SkipInstallationChecker skipInstallationChecker = new SkipInstallationChecker(mavenProject); + if (skipInstallationChecker.shouldSkipForConfiguration(mavenProjectConfiguration)) { + logger.info(skipInstallationChecker.getReason()); } else { configureMavenProject(mavenProject, mavenProjectConfiguration); } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java index d13814876..d2036c15d 100644 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java +++ b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipInstallationChecker.java @@ -5,7 +5,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.arquillian.smart.testing.configuration.Configuration; +import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; +import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; import static org.arquillian.smart.testing.mvn.ext.checker.SkipModuleChecker.MAVEN_TEST_SKIP; import static org.arquillian.smart.testing.mvn.ext.checker.SkipModuleChecker.SKIP_TESTS; @@ -23,13 +27,18 @@ public class SkipInstallationChecker { private final Pattern TEST_CLASS_PATTERN = Pattern.compile("[^a-z0-9 ]", Pattern.CASE_INSENSITIVE); - private final MavenSession session; + private MavenSession session; + private MavenProject mavenProject; private String reason; public SkipInstallationChecker(MavenSession session) { this.session = session; } + public SkipInstallationChecker(MavenProject mavenProject) { + this.mavenProject = mavenProject; + } + public boolean shouldSkip() { List goals = session.getGoals(); String defaultGoal = session.getTopLevelProject().getBuild().getDefaultGoal(); @@ -56,6 +65,23 @@ public boolean shouldSkip() { return reason != null; } + public boolean shouldSkipForConfiguration(Configuration configuration) { + if (mavenProject == null) { + mavenProject = session.getTopLevelProject(); + } + if (configuration.isDisable()) { + reason = String.format("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", + ExtensionVersion.version().toString(), mavenProject.getArtifactId()); + return true; + } + if (!configuration.areStrategiesDefined()) { + reason = String.format("Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " + + "For details on how to configure it head over to http://bit.ly/st-config", mavenProject.getArtifactId()); + return true; + } + return false; + } + private boolean isSkipTestExecutionSet() { return isSkipTests() || isSkip(); } diff --git a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java b/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java deleted file mode 100644 index 93a4b5611..000000000 --- a/mvn-extension/src/main/java/org/arquillian/smart/testing/mvn/ext/checker/SkipSTInstallationChecker.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.arquillian.smart.testing.mvn.ext.checker; - -import org.apache.maven.project.MavenProject; -import org.arquillian.smart.testing.configuration.Configuration; -import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion; - -import static org.arquillian.smart.testing.configuration.Configuration.SMART_TESTING_DISABLE; - -public class SkipSTInstallationChecker { - - private final Configuration configuration; - private final MavenProject mavenProject; - - private String reason; - - public SkipSTInstallationChecker(Configuration configuration, MavenProject mavenProject) { - this.configuration = configuration; - this.mavenProject = mavenProject; - } - - public String getReason() { - return reason; - } - - public boolean shouldSkip() { - if (configuration.isDisable()) { - reason = String.format("Disabling Smart Testing %s in %s module. Reason: " + SMART_TESTING_DISABLE + " is set.", - ExtensionVersion.version().toString(), mavenProject.getArtifactId()); - return true; - } - if (!configuration.areStrategiesDefined()) { - reason = String.format("Smart Testing Extension is installed but no strategies are provided for %s module. It won't influence the way how your tests are executed. " - + "For details on how to configure it head over to http://bit.ly/st-config", mavenProject.getArtifactId()); - return true; - } - return false; - } - -}