From e0a5c29601b57b87e746f296535781b454cdc721 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 5 Jun 2014 12:01:50 +0100 Subject: [PATCH] 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 --- spring-boot-integration-tests/pom.xml | 27 +++- .../StarterDependenciesIntegrationTests.java | 128 ++++++++++++++++++ .../src/test/resources/build.gradle | 49 +++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java create mode 100644 spring-boot-integration-tests/src/test/resources/build.gradle diff --git a/spring-boot-integration-tests/pom.xml b/spring-boot-integration-tests/pom.xml index 2ca327be8b0..83743653f7d 100644 --- a/spring-boot-integration-tests/pom.xml +++ b/spring-boot-integration-tests/pom.xml @@ -8,7 +8,7 @@ ../spring-boot-parent spring-boot-integration-tests - pom + jar Spring Boot Integration Tests Spring Boot Integration Tests http://projects.spring.io/spring-boot/ @@ -19,6 +19,19 @@ ${basedir}/.. + + + org.gradle + gradle-tooling-api + ${gradle.version} + test + + + org.springframework.boot + spring-boot-dependency-tools + test + + default @@ -57,4 +70,16 @@ full + + + gradle + http://repo.gradle.org/gradle/libs-releases-local + + true + + + false + + + diff --git a/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java b/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java new file mode 100644 index 00000000000..b92d1124528 --- /dev/null +++ b/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java @@ -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 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 getStarters() { + List starters = new ArrayList(); + 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()); + } + } +} diff --git a/spring-boot-integration-tests/src/test/resources/build.gradle b/spring-boot-integration-tests/src/test/resources/build.gradle new file mode 100644 index 00000000000..6b5c3791ae7 --- /dev/null +++ b/spring-boot-integration-tests/src/test/resources/build.gradle @@ -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 +} \ No newline at end of file