Add tests to verify starter dependencies when used with Gradle

We've had problems with the starters when used with Gradle. They have
been pulling in commons-logging (#987) and the wrong version of Spring
(#1028) due to Gradle's different exclusion and dependency resolution
semantics.

This commit adds some integration tests that use Gradle's tooling API
to take each starter in turn and build a Gradle project that depends
upon it. The build looks at the transitive dependencies and checks
that neither commons-logging nor any Spring modules with the wrong
version are present.

Closes #1036
This commit is contained in:
Andy Wilkinson 2014-06-05 12:01:50 +01:00
parent dff7a3bf7c
commit e0a5c29601
3 changed files with 203 additions and 1 deletions

View File

@ -8,7 +8,7 @@
<relativePath>../spring-boot-parent</relativePath>
</parent>
<artifactId>spring-boot-integration-tests</artifactId>
<packaging>pom</packaging>
<packaging>jar</packaging>
<name>Spring Boot Integration Tests</name>
<description>Spring Boot Integration Tests</description>
<url>http://projects.spring.io/spring-boot/</url>
@ -19,6 +19,19 @@
<properties>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-tooling-api</artifactId>
<version>${gradle.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependency-tools</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>default</id>
@ -57,4 +70,16 @@
<id>full</id>
</profile>
</profiles>
<repositories>
<repository>
<id>gradle</id>
<url>http://repo.gradle.org/gradle/libs-releases-local</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,128 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.starter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.gradle.tooling.BuildException;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.boot.dependency.tools.VersionManagedDependencies;
import org.springframework.util.FileCopyUtils;
import static org.junit.Assert.fail;
/**
* Tests for the various starter projects to check that they don't pull in unwanted
* transitive dependencies when used with Gradle
*
* @author Andy Wilkinson
*/
@RunWith(Parameterized.class)
public class StarterDependenciesIntegrationTests {
private static final String STARTER_NAME_PREFIX = "spring-boot-starter";
private static final List<String> EXCLUDED_STARTERS = Arrays
.asList("spring-boot-starter-parent");
private static ProjectConnection project;
private static String bootVersion;
private static String springVersion;
private final String[] buildArguments;
@Parameters
public static List<String[]> getStarters() {
List<String[]> starters = new ArrayList<String[]>();
for (File file : new File("../spring-boot-starters").listFiles()) {
if (file.isDirectory()) {
String name = file.getName();
if (name.startsWith(STARTER_NAME_PREFIX)
&& !EXCLUDED_STARTERS.contains(file.getName())) {
starters.add(new String[] { file.getName() });
}
}
}
return starters;
}
@BeforeClass
public static void createProject() throws IOException {
File projectDirectory = new File("target/starter-dependencies");
projectDirectory.mkdirs();
File gradleScript = new File(projectDirectory, "build.gradle");
FileCopyUtils.copy(new File("src/test/resources/build.gradle"), gradleScript);
GradleConnector gradleConnector = GradleConnector.newConnector();
((DefaultGradleConnector) gradleConnector).embedded(true);
project = gradleConnector.forProjectDirectory(projectDirectory).connect();
}
@BeforeClass
public static void determineVersions() throws Exception {
springVersion = new VersionManagedDependencies().find("spring-core").getVersion();
bootVersion = new VersionManagedDependencies().find("spring-boot").getVersion();
}
@AfterClass
public static void closeProject() {
project.close();
}
public StarterDependenciesIntegrationTests(String starter) {
this.buildArguments = new String[] { "-Pstarter=" + starter,
"-PbootVersion=" + bootVersion, "-PspringVersion=" + springVersion };
}
@Test
public void commonsLoggingIsNotATransitiveDependency() throws IOException {
runBuildForTask("checkCommonsLogging");
}
@Test
public void oldSpringModulesAreNotTransitiveDependencies() throws IOException {
runBuildForTask("checkSpring");
}
private void runBuildForTask(String task) {
try {
project.newBuild().forTasks(task).withArguments(this.buildArguments).run();
}
catch (BuildException ex) {
Throwable root = ex;
while (root.getCause() != null) {
root = root.getCause();
}
fail(root.getMessage());
}
}
}

View File

@ -0,0 +1,49 @@
import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
repositories {
mavenLocal()
mavenCentral()
}
configurations {
springBootStarter
}
dependencies {
springBootStarter "org.springframework.boot:${project.starter}:${project.bootVersion}"
}
task checkCommonsLogging {
doFirst {
def commonsLogging = resolvedDependencies
.find { it.selected.id.group == 'commons-logging' }
if (commonsLogging) {
throw new GradleException("${project.starter} pulls in commons-logging")
}
}
}
task checkSpring {
doFirst {
def wrongSpring = resolvedDependencies
.findAll{it.selected.id.group == 'org.springframework'}
.findAll{it.selected.id.version != project.springVersion}
.collect {it.selected.id}
if (wrongSpring) {
throw new GradleException("${project.starter} pulled in ${wrongSpring as Set}")
}
}
}
def getResolvedDependencies() {
def allDependencies = configurations.springBootStarter.incoming
.resolutionResult.allDependencies
.split { it instanceof UnresolvedDependencyResult }
def unresolved = allDependencies.first()
def resolved = allDependencies.last()
if (unresolved) {
throw new GradleException("Resolution of ${project.starter} failed: ${unresolved}")
}
resolved
}