Skip to content

Commit

Permalink
fix(#278): introduced delegation to custom providers (#281)
Browse files Browse the repository at this point in the history
if a custom surefire provider is specified then the logic is delegated to it and known providers resolution is skipped
  • Loading branch information
MatousJobanek committed Dec 12, 2017
1 parent 1a7e79f commit ae748e7
Show file tree
Hide file tree
Showing 33 changed files with 565 additions and 187 deletions.
1 change: 1 addition & 0 deletions README.adoc
Expand Up @@ -76,6 +76,7 @@ include::{asciidoctor-source}/configuration.adoc[]
include::{asciidoctor-source}/configfile.adoc[]
include::{asciidoctor-source}/usage.adoc[]
include::{asciidoctor-source}/registering-strategies.adoc[]
include::{asciidoctor-source}/surefire-providers.adoc[]
include::{asciidoctor-source}/reports.adoc[]
include::{asciidoctor-source}/jenkins.adoc[]
include::{asciidoctor-source}/refcard.adoc[]
Expand Down
Expand Up @@ -31,6 +31,7 @@ public class Configuration implements ConfigurationSection {
public static final String SMART_TESTING_MODE = "smart.testing.mode";
public static final String SMART_TESTING_CUSTOM_STRATEGIES = "smart.testing.strategy";
public static final String SMART_TESTING_CUSTOM_STRATEGIES_PATTERN = SMART_TESTING_CUSTOM_STRATEGIES + ".*";
public static final String SMART_TESTING_CUSTOM_PROVIDERS = "smart.testing.custom.providers";
public static final String SMART_TESTING_APPLY_TO = "smart.testing.apply.to";
public static final String SMART_TESTING_VERSION = "smart.testing.version";
public static final String SMART_TESTING_DISABLE = "smart.testing.disable";
Expand All @@ -39,6 +40,7 @@ public class Configuration implements ConfigurationSection {

private String[] strategies = new String[0];
private String[] customStrategies = new String[0];
private String[] customProviders = new String[0];
private RunMode mode;
private String applyTo;

Expand Down Expand Up @@ -138,6 +140,14 @@ public void setStrategiesConfig(Map<String, Object> strategiesConfig) {
this.strategiesConfig = strategiesConfig;
}

public String[] getCustomProviders() {
return customProviders;
}

public void setCustomProviders(String[] customProviders) {
this.customProviders = customProviders;
}

public List<ConfigurationItem> registerConfigurationItems() {
List<ConfigurationItem> configItems = new ArrayList<>();
configItems.add(new ConfigurationItem("strategies", SMART_TESTING, new String[0]));
Expand All @@ -147,6 +157,7 @@ public List<ConfigurationItem> registerConfigurationItems() {
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("customProviders", SMART_TESTING_CUSTOM_PROVIDERS, new String[0]));
return configItems;
}

Expand Down
Expand Up @@ -6,25 +6,32 @@ public class TemporaryInternalFiles {

private static final String TEMP_REPORT_DIR = "reports";
private static final String SMART_TESTING_SCM_CHANGES = "scm-changes";
private static final String JUNIT_5_PLATFORM_VERSION = "junit5PlatformVersion";
private static final String CUSTOM_PROVIDERS_DIRECTORY = "customProviders";

private TemporaryInternalFiles() {
}

public static String getScmChangesFileName(){
return SMART_TESTING_SCM_CHANGES;
}

public static String getJunit5PlatformVersionFileName(String pluginArtifactId){
return pluginArtifactId + "_" + JUNIT_5_PLATFORM_VERSION;
public static String getCustomProvidersDirName(String pluginArtifactId) {
return pluginArtifactId + "_" + CUSTOM_PROVIDERS_DIRECTORY;
}

public String getTestReportDirectoryName(){
return TEMP_REPORT_DIR;
public static LocalStorageDirectoryAction createCustomProvidersDirAction(File rootDir,
String pluginArtifactId) {
return new LocalStorage(rootDir)
.duringExecution()
.temporary()
.directory(getCustomProvidersDirName(pluginArtifactId));
}

public LocalStorageDirectoryAction createTestReportDirectoryAction(String rootDir){
return createTestReportDirectoryAction(new File(rootDir));
public static String getTestReportDirName() {
return TEMP_REPORT_DIR;
}

public LocalStorageDirectoryAction createTestReportDirectoryAction(File rootDir){
public static LocalStorageDirectoryAction createTestReportDirAction(File rootDir) {
return new LocalStorage(rootDir)
.duringExecution()
.temporary()
Expand Down
Expand Up @@ -68,6 +68,8 @@ public void should_load_configuration_with_default_values_if_property_is_not_spe
expectedConfiguration.setCustomStrategies(
new String[] {"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"});
expectedConfiguration.setCustomProviders(
new String[] {"org.foo:my-custom-provider=fully.qualified.name.to.SurefireProviderImpl"});

// when
final Configuration actualConfiguration =
Expand Down
Expand Up @@ -138,6 +138,8 @@ public void should_load_configuration_with_overwriting_system_properties_over_pr
new String[] {"smart.testing.strategy.experimental=org.arquillian.smart.testing:strategy-experimental:1.0.0",
"smart.testing.strategy.my=org.arquillian.smart.testing:strategy-my:1.0.0",
"smart.testing.strategy.cool=org.arquillian.smart.testing:strategy-cool:1.0.1"});
expectedConfiguration.setCustomProviders(
new String[] {"org.foo:my-custom-provider=fully.qualified.name.to.SurefireProviderImpl"});

// when
final Configuration actualConfiguration =
Expand Down Expand Up @@ -217,6 +219,8 @@ public void should_load_configuration_file_from_a_particular_location() throws I
expectedConfiguration.setCustomStrategies(
new String[] {"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"});
expectedConfiguration.setCustomProviders(
new String[] {"org.foo:my-custom-provider=fully.qualified.name.to.SurefireProviderImpl"});

final File tempConfigFile = getCustomConfigFile();
System.setProperty(SMART_TESTING_CONFIG, tempConfigFile.getAbsolutePath());
Expand Down
2 changes: 2 additions & 0 deletions core/src/test/resources/configuration/smart-testing.yml
Expand Up @@ -22,3 +22,5 @@ strategiesConfiguration: #<!--11-->
inclusions: #<!--14-->
- org.package.exclude.*
- org.arquillian.package.exclude.*
customProviders: #<!--15-->
- org.foo:my-custom-provider=fully.qualified.name.to.SurefireProviderImpl
4 changes: 4 additions & 0 deletions docs/configfile.adoc
Expand Up @@ -27,6 +27,7 @@ copyToClipboard:config-file[]
<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.
<15> GroupId and artifactId with fully qualified name of the implementation of SurefireProvider the execution should be delegated to.

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.
Expand Down Expand Up @@ -73,6 +74,9 @@ a| A list of custom strategies in the form of key/value. It is important to noti

a| strategiesConfiguration
a| A list of strategies with it's configuration.

a| customProviders
a| A pair of groupId and artifactId with fully qualified name of `SurefireProvider` implementation the test execution should be delegated to. For more information see <<_custom_surefire_providers, Custom Surefire Providers>>
|===

==== Report Options
Expand Down
43 changes: 43 additions & 0 deletions docs/surefire-providers.adoc
@@ -0,0 +1,43 @@
== Surefire Providers

Internally, Smart Testing uses its own implementation of `SurefireProvider` where calls Smart Testing API. Then, based on the information retrieved from the classpath, it decides which provider should be used for a delegation of the test execution - eg. JUnit4 or TestNG (in the same way as it is done inside of the Surefire plugin itself).
But some projects may use their own surefire provider implementations or can specify one specific known provider that should scan the classpath and execute the test suite.

=== Known Surefire Providers

Known Surefire Providers are those implementations that are part of the Surefire project itself plus one implementation distributed as part of JUnit 5. The complete list of known providers:

* JUnit 4
** groupId: `org.apache.maven.surefire`
** artifactId: `surefire-junit4`
** provider implementation: `org.apache.maven.surefire.junit4.JUnit4Provider`

* JUnit 47 and above
** groupId: `org.apache.maven.surefire`
** artifactId: `surefire-junit47`
** provider implementation: `org.apache.maven.surefire.junitcore.JUnitCoreProvider`

* JUnit 5
** groupId: `org.junit.platform`
** artifactId: `junit-platform-surefire-provider`
** provider implementation: `org.junit.platform.surefire.provider.JUnitPlatformProvider`

* TestNG
** groupId: `org.apache.maven.surefire`
** artifactId: `surefire-testng`
** provider implementation: `org.apache.maven.surefire.testng.TestNGProvider`

When one of these providers is specified inside of the Surefire's dependencies, then Smart Testing is able to automatically detect it and delegate the execution to it.

=== Custom Surefire Providers

If some unknown provider (or some other `SurefireProvider` implementation) is used then it is necessary to set this provider either in the configuration file or using system properties.
The format is a pair of groupId and artifactId with a fully qualified name of the class that implements `SurefireProvider` interface and that should be loaded. For JUnit 5 provider it would look like this (in case of configuration file):
[source, yaml]
----
customProviders:
- org.junit.platform:junit-platform-surefire-provider=org.junit.platform.surefire.provider.JUnitPlatformProvider
----
If this is specified then Smart Testing will detect the dependency and delegate the test execution to it.
System property dedicated for this usage is `const:core/src/main/java/org/arquillian/smart/testing/configuration/Configuration.java[name="SMART_TESTING_CUSTOM_PROVIDERS"]`

Expand Up @@ -165,7 +165,7 @@ private TestResults accumulatedTestResults() {

private boolean isSurefireReportFile(Path path) {
return !path.toFile().getAbsolutePath()
.contains(File.separator + new TemporaryInternalFiles().getTestReportDirectoryName()) && path.getFileName()
.contains(File.separator + TemporaryInternalFiles.getTestReportDirName()) && path.getFileName()
.toString().startsWith(TEST_REPORT_PREFIX);
}
}
Expand Up @@ -62,6 +62,6 @@ public void should_only_execute_previously_failing_tests_when_failed_is_enabled(
.containsAll(expectedTestResults)
.hasSameSizeAs(expectedTestResults);

softly.assertThat(project).doesNotContainDirectory(new TemporaryInternalFiles().getTestReportDirectoryName());
softly.assertThat(project).doesNotContainDirectory(TemporaryInternalFiles.getTestReportDirName());
}
}
Expand Up @@ -58,6 +58,6 @@ public void should_only_execute_previously_failing_tests_when_failed_is_enabled(
.containsAll(expectedTestResults)
.hasSameSizeAs(expectedTestResults);

softly.assertThat(project).doesNotContainDirectory(new TemporaryInternalFiles().getTestReportDirectoryName());
softly.assertThat(project).doesNotContainDirectory(TemporaryInternalFiles.getTestReportDirName());
}
}
14 changes: 14 additions & 0 deletions known-surefire-providers/pom.xml
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>smart-testing-parent</artifactId>
<groupId>org.arquillian.smart.testing</groupId>
<version>0.0.6-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>known-surefire-providers</artifactId>

</project>
@@ -0,0 +1,46 @@
package org.arquillian.smart.testing.known.surefire.providers;

public enum KnownProvider {

JUNIT_4(
"org.apache.maven.surefire",
"surefire-junit4",
"org.apache.maven.surefire.junit4.JUnit4Provider"),

JUNIT_47(
"org.apache.maven.surefire",
"surefire-junit47",
"org.apache.maven.surefire.junitcore.JUnitCoreProvider"),

JUNIT_5(
"org.junit.platform",
"junit-platform-surefire-provider",
"org.junit.platform.surefire.provider.JUnitPlatformProvider"),

TESTNG(
"org.apache.maven.surefire",
"surefire-testng",
"org.apache.maven.surefire.testng.TestNGProvider");

private final String groupId;
private final String artifactId;
private final String providerClassName;

KnownProvider(String groupId, String artifactId, String providerClassName) {
this.groupId = groupId;
this.artifactId = artifactId;
this.providerClassName = providerClassName;
}

public String getGroupId() {
return groupId;
}

public String getArtifactId() {
return artifactId;
}

public String getProviderClassName() {
return providerClassName;
}
}
5 changes: 5 additions & 0 deletions mvn-extension/pom.xml
Expand Up @@ -21,6 +21,11 @@
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.arquillian.smart.testing</groupId>
<artifactId>known-surefire-providers</artifactId>
<version>${project.version}</version>
</dependency>

<!--
This explicit include prevents us from adding new implementations dynamically,
Expand Down
@@ -1,23 +1,17 @@
package org.arquillian.smart.testing.mvn.ext;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.arquillian.smart.testing.configuration.Configuration;
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.dependencies.DependencyResolver;
import org.arquillian.smart.testing.mvn.ext.dependencies.ExtensionVersion;
import org.arquillian.smart.testing.mvn.ext.dependencies.Version;

import static org.arquillian.smart.testing.hub.storage.local.TemporaryInternalFiles.getJunit5PlatformVersionFileName;

class MavenProjectConfigurator {

private static final Version MINIMUM_VERSION = Version.from("2.19.1");
Expand All @@ -44,23 +38,10 @@ boolean configureTestRunner(Model model) {

dependencyResolver.addRequiredDependencies(model);

final LocalStorage localStorage = new LocalStorage(model.getProjectDirectory());
effectiveTestRunnerPluginConfigurations
.forEach(plugin -> {
dependencyResolver.removeAndRegisterFirstCustomProvider(model, plugin);
dependencyResolver.addAsPluginDependency(plugin);

final Optional<Dependency> dependency = dependencyResolver.findJUnit5PlatformDependency(plugin);
dependency.ifPresent(d -> {
try {
localStorage.duringExecution()
.temporary()
.file(getJunit5PlatformVersionFileName(plugin.getArtifactId()))
.create(d.getVersion().getBytes());
plugin.removeDependency(dependency.get());
} catch (IOException e) {
throw new IllegalStateException(e);
}
});
});
return true;
} else {
Expand Down
Expand Up @@ -29,7 +29,7 @@ static void copySurefireReports(Model model) {

private static void copyReportsDirectory(Model model, File surefireReportsDir) {
LocalStorageDirectoryAction reportsDirectory =
new TemporaryInternalFiles().createTestReportDirectoryAction(model.getProjectDirectory());
TemporaryInternalFiles.createTestReportDirAction(model.getProjectDirectory());
logger.debug("Copying surefire report directory from [%s] to [%s]", surefireReportsDir,
reportsDirectory.getPath());

Expand Down

0 comments on commit ae748e7

Please sign in to comment.