From 18c7c0af222f23c556c52162aa6348ad4b38267a Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Thu, 8 Jul 2021 16:23:43 -0500 Subject: [PATCH] Add Paketo image building system tests A new system test plugin is being made available for running test suites that should be run less frequently than with every commit, such as tests that verify Spring Boot compatibility with external projects. CI pipeline configuration for running system tests is also provided. The first system tests verify the behavior of the Spring Boot image building plugins when building images using Paketo buildpacks. Closes gh-25824 --- buildSrc/build.gradle | 4 + .../boot/build/test/SystemTestPlugin.java | 83 ++++ ci/pipeline.yml | 89 ++++ ci/scripts/run-system-tests.sh | 12 + ci/tasks/run-system-tests.yml | 21 + settings.gradle | 1 + .../GettingStartedDocumentationTests.java | 4 +- ...gratingWithActuatorDocumentationTests.java | 2 +- ...anagingDependenciesDocumentationTests.java | 6 +- .../docs/PackagingDocumentationTests.java | 2 +- .../docs/PublishingDocumentationTests.java | 2 +- .../docs/RunningDocumentationTests.java | 2 +- .../dsl/BuildInfoDslIntegrationTests.java | 2 +- .../gradle/junit/GradleBuildFieldSetter.java | 4 +- .../gradle/junit/GradleCompatibility.java | 2 +- .../junit/GradleCompatibilityExtension.java | 25 +- .../gradle/junit/GradleMultiDslExtension.java | 13 +- ...plicationPluginActionIntegrationTests.java | 4 +- ...anagementPluginActionIntegrationTests.java | 4 +- .../JavaPluginActionIntegrationTests.java | 2 +- .../KotlinPluginActionIntegrationTests.java | 4 +- .../MavenPluginActionIntegrationTests.java | 2 +- ...yDependencyManagementIntegrationTests.java | 4 +- .../SpringBootPluginIntegrationTests.java | 4 +- .../WarPluginActionIntegrationTests.java | 2 +- .../buildinfo/BuildInfoIntegrationTests.java | 2 +- .../AbstractBootArchiveIntegrationTests.java | 2 +- .../BootBuildImageIntegrationTests.java | 2 +- ...ootBuildImageRegistryIntegrationTests.java | 2 +- .../tasks/bundling/MavenIntegrationTests.java | 2 +- .../MavenPublishingIntegrationTests.java | 2 +- .../tasks/run/BootRunIntegrationTests.java | 2 +- .../spring-boot-test-support/build.gradle | 10 + .../boot/testsupport}/gradle/testkit/Dsl.java | 11 +- .../gradle/testkit/GradleBuild.java | 33 +- .../gradle/testkit/GradleBuildExtension.java | 14 +- .../gradle/testkit/GradleVersions.java | 53 +++ .../spring-boot-image-tests/build.gradle | 35 ++ .../assertions/ContainerConfigAssert.java | 134 ++++++ .../boot/image/assertions/ImageAssert.java | 91 ++++ .../image/assertions/ImageAssertions.java | 44 ++ .../junit/GradleBuildInjectionExtension.java | 54 +++ .../boot/image/paketo/LayersIndex.java | 53 +++ .../boot/image/paketo/PaketoBuilderTests.java | 402 ++++++++++++++++++ ...aketoBuilderTests-bootDistZipJarApp.gradle | 33 ++ ...PaketoBuilderTests-executableWarApp.gradle | 26 ++ ...ketoBuilderTests-plainDistZipJarApp.gradle | 33 ++ .../PaketoBuilderTests-plainWarApp.gradle | 26 ++ .../image/paketo/PaketoBuilderTests.gradle | 24 ++ .../boot/image/paketo/settings.gradle | 7 + src/checkstyle/checkstyle-suppressions.xml | 2 + 51 files changed, 1326 insertions(+), 73 deletions(-) create mode 100644 buildSrc/src/main/java/org/springframework/boot/build/test/SystemTestPlugin.java create mode 100755 ci/scripts/run-system-tests.sh create mode 100644 ci/tasks/run-system-tests.yml rename spring-boot-project/spring-boot-tools/{spring-boot-gradle-plugin/src/test/java/org/springframework/boot => spring-boot-test-support/src/main/java/org/springframework/boot/testsupport}/gradle/testkit/Dsl.java (83%) rename spring-boot-project/spring-boot-tools/{spring-boot-gradle-plugin/src/test/java/org/springframework/boot => spring-boot-test-support/src/main/java/org/springframework/boot/testsupport}/gradle/testkit/GradleBuild.java (92%) rename spring-boot-project/spring-boot-tools/{spring-boot-gradle-plugin/src/test/java/org/springframework/boot => spring-boot-test-support/src/main/java/org/springframework/boot/testsupport}/gradle/testkit/GradleBuildExtension.java (87%) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/build.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ContainerConfigAssert.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssertions.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/junit/GradleBuildInjectionExtension.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/LayersIndex.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-executableWarApp.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainWarApp.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests.gradle create mode 100644 spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/settings.gradle diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 31559c058b9d..6176fc8b5c1b 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -67,6 +67,10 @@ gradlePlugin { id = "org.springframework.boot.integration-test" implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin" } + systemTestPlugin { + id = "org.springframework.boot.system-test" + implementationClass = "org.springframework.boot.build.test.SystemTestPlugin" + } mavenPluginPlugin { id = "org.springframework.boot.maven-plugin" implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin" diff --git a/buildSrc/src/main/java/org/springframework/boot/build/test/SystemTestPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/test/SystemTestPlugin.java new file mode 100644 index 000000000000..e0352bbf023b --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/test/SystemTestPlugin.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.build.test; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.testing.Test; +import org.gradle.language.base.plugins.LifecycleBasePlugin; +import org.gradle.plugins.ide.eclipse.EclipsePlugin; +import org.gradle.plugins.ide.eclipse.model.EclipseModel; + +/** + * A {@link Plugin} to configure system testing support in a {@link Project}. + * + * @author Andy Wilkinson + * @author Scott Frederick + */ +public class SystemTestPlugin implements Plugin { + + /** + * Name of the {@code systemTest} task. + */ + public static String SYSTEM_TEST_TASK_NAME = "systemTest"; + + /** + * Name of the {@code systemTest} source set. + */ + public static String SYSTEM_TEST_SOURCE_SET_NAME = "systemTest"; + + @Override + public void apply(Project project) { + project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> configureSystemTesting(project)); + } + + private void configureSystemTesting(Project project) { + SourceSet systemTestSourceSet = createSourceSet(project); + createTestTask(project, systemTestSourceSet); + project.getPlugins().withType(EclipsePlugin.class, (eclipsePlugin) -> { + EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class); + eclipse.classpath((classpath) -> classpath.getPlusConfigurations().add( + project.getConfigurations().getByName(systemTestSourceSet.getRuntimeClasspathConfigurationName()))); + }); + } + + private SourceSet createSourceSet(Project project) { + SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets(); + SourceSet systemTestSourceSet = sourceSets.create(SYSTEM_TEST_SOURCE_SET_NAME); + SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); + systemTestSourceSet + .setCompileClasspath(systemTestSourceSet.getCompileClasspath().plus(mainSourceSet.getOutput())); + systemTestSourceSet + .setRuntimeClasspath(systemTestSourceSet.getRuntimeClasspath().plus(mainSourceSet.getOutput())); + return systemTestSourceSet; + } + + private void createTestTask(Project project, SourceSet systemTestSourceSet) { + Test systemTest = project.getTasks().create(SYSTEM_TEST_TASK_NAME, Test.class); + systemTest.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + systemTest.setDescription("Runs system tests."); + systemTest.setTestClassesDirs(systemTestSourceSet.getOutput().getClassesDirs()); + systemTest.setClasspath(systemTestSourceSet.getRuntimeClasspath()); + systemTest.shouldRunAfter(JavaPlugin.TEST_TASK_NAME); + } + +} diff --git a/ci/pipeline.yml b/ci/pipeline.yml index c83a76ac4eef..63b1f67ff914 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -41,6 +41,14 @@ anchors: BRANCH: ((branch)) <<: *gradle-enterprise-task-params <<: *docker-hub-task-params + run-system-tests-task-params: &run-system-tests-task-params + privileged: true + timeout: ((task-timeout)) + file: git-repo/ci/tasks/run-system-tests.yml + params: + BRANCH: ((branch)) + <<: *gradle-enterprise-task-params + <<: *docker-hub-task-params artifactory-repo-put-params: &artifactory-repo-put-params signing_key: ((signing-key)) signing_passphrase: ((signing-passphrase)) @@ -165,6 +173,12 @@ resources: source: <<: *registry-image-resource-source repository: ((docker-hub-organization))/spring-boot-ci-jdk16 +- name: paketo-builder-base-image + type: registry-image + icon: docker + source: + repository: paketobuildpacks/builder + tag: base - name: artifactory-repo type: artifactory-resource icon: package-variant @@ -668,11 +682,86 @@ jobs: - put: homebrew-tap-repo params: repository: updated-homebrew-tap-repo +- name: run-system-tests + serial: true + public: true + plan: + - get: ci-image + - get: git-repo + - get: paketo-builder-base-image + trigger: true + - get: daily + trigger: true + - do: + - task: run-system-tests + image: ci-image + <<: *run-system-tests-task-params + on_failure: + do: + - put: slack-alert + params: + <<: *slack-fail-params + - put: slack-alert + params: + <<: *slack-success-params +- name: jdk11-run-system-tests + serial: true + public: true + plan: + - get: ci-image-jdk11 + - get: git-repo + - get: paketo-builder-base-image + trigger: true + - get: daily + trigger: true + - do: + - task: run-system-tests + image: ci-image-jdk11 + <<: *run-system-tests-task-params + on_failure: + do: + - put: slack-alert + params: + <<: *slack-fail-params + - put: slack-alert + params: + <<: *slack-success-params +- name: jdk16-run-system-tests + serial: true + public: true + plan: + - get: ci-image-jdk16 + - get: git-repo + - get: paketo-builder-base-image + trigger: true + - get: daily + trigger: true + - do: + - task: run-system-tests + image: ci-image-jdk16 + privileged: true + timeout: ((task-timeout)) + file: git-repo/ci/tasks/run-system-tests.yml + params: + BRANCH: ((branch)) + TOOLCHAIN_JAVA_VERSION: 16 + <<: *gradle-enterprise-task-params + <<: *docker-hub-task-params + on_failure: + do: + - put: slack-alert + params: + <<: *slack-fail-params + - put: slack-alert + params: + <<: *slack-success-params groups: - name: "builds" jobs: ["build", "jdk11-build", "jdk16-build", "windows-build"] - name: "releases" jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release", "publish-gradle-plugin", "publish-to-sdkman", "update-homebrew-tap"] +- name: "system-tests" + jobs: ["run-system-tests", "jdk11-run-system-tests", "jdk16-run-system-tests"] - name: "ci-images" jobs: ["build-ci-images", "detect-docker-updates", "detect-jdk-updates", "detect-ubuntu-image-updates"] - name: "pull-requests" diff --git a/ci/scripts/run-system-tests.sh b/ci/scripts/run-system-tests.sh new file mode 100755 index 000000000000..4cb3025da6e7 --- /dev/null +++ b/ci/scripts/run-system-tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +source $(dirname $0)/common.sh + +pushd git-repo > /dev/null +if [[ -d /opt/openjdk-toolchain ]]; then + ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 --rerun-tasks systemTest -PtoolchainVersion=${TOOLCHAIN_JAVA_VERSION} -Porg.gradle.java.installations.auto-detect=false -Porg.gradle.java.installations.auto-download=false -Porg.gradle.java.installations.paths=/opt/openjdk-toolchain/ +else + ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 --rerun-tasks systemTest +fi +popd > /dev/null diff --git a/ci/tasks/run-system-tests.yml b/ci/tasks/run-system-tests.yml new file mode 100644 index 000000000000..5a1f6e5bd0a1 --- /dev/null +++ b/ci/tasks/run-system-tests.yml @@ -0,0 +1,21 @@ +--- +platform: linux +inputs: +- name: git-repo +caches: +- path: gradle +params: + BRANCH: + CI: true + GRADLE_ENTERPRISE_ACCESS_KEY: + GRADLE_ENTERPRISE_CACHE_USERNAME: + GRADLE_ENTERPRISE_CACHE_PASSWORD: + GRADLE_ENTERPRISE_URL: https://ge.spring.io +run: + path: bash + args: + - -ec + - | + source /docker-lib.sh + start_docker + ${PWD}/git-repo/ci/scripts/run-system-tests.sh diff --git a/settings.gradle b/settings.gradle index c75bb113f1a8..90c976ef515a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -75,6 +75,7 @@ include "spring-boot-tests:spring-boot-integration-tests:spring-boot-configurati include "spring-boot-tests:spring-boot-integration-tests:spring-boot-launch-script-tests" include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-tests" include "spring-boot-tests:spring-boot-integration-tests:spring-boot-server-tests" +include "spring-boot-system-tests:spring-boot-image-tests" file("${rootDir}/spring-boot-project/spring-boot-starters").eachDirMatch(~/spring-boot-starter.*/) { include "spring-boot-project:spring-boot-starters:${it.name}" diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java index 404dba2c7213..a1ce4a316ea5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/GettingStartedDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -20,7 +20,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; /** * Tests for the getting started documentation. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java index da30a982fc20..748899b2aaf1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/IntegratingWithActuatorDocumentationTests.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java index d2608379fa75..be33e3567dbb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/ManagingDependenciesDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -20,8 +20,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.Dsl; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.Dsl; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assumptions.assumingThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java index 6b68af4c5bdf..cc0330e4235a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PackagingDocumentationTests.java @@ -33,7 +33,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java index 028dfdd1094a..6bbba73facca 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/PublishingDocumentationTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java index 87d597f384be..dbf5b9562240 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.gradle.junit.GradleMultiDslExtension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java index 3ca113400007..c584b3dedaa5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/dsl/BuildInfoDslIntegrationTests.java @@ -26,7 +26,7 @@ import org.springframework.boot.gradle.junit.GradleCompatibility; import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleBuildFieldSetter.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleBuildFieldSetter.java index 19ea8e67ccf5..7dcf7be83e2a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleBuildFieldSetter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleBuildFieldSetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -21,7 +21,7 @@ import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.util.ReflectionUtils; /** diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibility.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibility.java index 9617f0d94de6..5ab519faee8f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibility.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibility.java @@ -26,7 +26,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.Extension; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; /** * {@link Extension} that runs {@link TestTemplate templated tests} against multiple diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java index 7d4876e20ad7..ce30517e0f0c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleCompatibilityExtension.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.stream.Stream; -import org.gradle.api.JavaVersion; import org.gradle.util.GradleVersion; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.Extension; @@ -30,8 +29,9 @@ import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import org.junit.platform.commons.util.AnnotationUtils; -import org.springframework.boot.gradle.testkit.GradleBuild; -import org.springframework.boot.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.testsupport.gradle.testkit.GradleVersions; import org.springframework.util.StringUtils; /** @@ -43,26 +43,11 @@ */ final class GradleCompatibilityExtension implements TestTemplateInvocationContextProvider { - private static final List GRADLE_VERSIONS; - - static { - JavaVersion javaVersion = JavaVersion.current(); - if (javaVersion.isCompatibleWith(JavaVersion.VERSION_16)) { - GRADLE_VERSIONS = Arrays.asList("7.0.2", "7.1.1"); - } - else { - GRADLE_VERSIONS = Arrays.asList("6.8.3", "current", "7.0.2", "7.1.1"); - } - } + private static final List GRADLE_VERSIONS = GradleVersions.allCompatible(); @Override public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { - Stream gradleVersions = GRADLE_VERSIONS.stream().map((version) -> { - if (version.equals("current")) { - return GradleVersion.current().getVersion(); - } - return version; - }); + Stream gradleVersions = GRADLE_VERSIONS.stream(); GradleCompatibility gradleCompatibility = AnnotationUtils .findAnnotation(context.getRequiredTestClass(), GradleCompatibility.class).get(); if (StringUtils.hasText(gradleCompatibility.versionsLessThan())) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java index 829e220e1b07..9cebd901210a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/junit/GradleMultiDslExtension.java @@ -20,16 +20,16 @@ import java.util.List; import java.util.stream.Stream; -import org.gradle.api.JavaVersion; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; -import org.springframework.boot.gradle.testkit.Dsl; -import org.springframework.boot.gradle.testkit.GradleBuild; -import org.springframework.boot.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.testsupport.gradle.testkit.Dsl; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.testsupport.gradle.testkit.GradleVersions; /** * {@link Extension} that runs {@link TestTemplate templated tests} against the Groovy and @@ -61,10 +61,7 @@ private static final class DslTestTemplateInvocationContext implements TestTempl @Override public List getAdditionalExtensions() { GradleBuild gradleBuild = new GradleBuild(this.dsl); - JavaVersion javaVersion = JavaVersion.current(); - if (javaVersion.isCompatibleWith(JavaVersion.VERSION_16)) { - gradleBuild.gradleVersion("7.0.2"); - } + gradleBuild.gradleVersion(GradleVersions.currentOrMinimumCompatible()); return Arrays.asList(new GradleBuildFieldSetter(gradleBuild), new GradleBuildExtension()); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java index 5a595258c069..fee3f56d77a9 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/ApplicationPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -32,7 +32,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java index 73975c143569..1041969f107c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/DependencyManagementPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -20,7 +20,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java index 9633fc18303c..8bfb19ac37cb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java @@ -26,7 +26,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java index 4f3a0732e35b..07568b49a56a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/KotlinPluginActionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -19,7 +19,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java index c4672e80719a..3c8fc1279b56 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/MavenPluginActionIntegrationTests.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/OnlyDependencyManagementIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/OnlyDependencyManagementIntegrationTests.java index ad8145a1f8d0..e9424d9032e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/OnlyDependencyManagementIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/OnlyDependencyManagementIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -19,7 +19,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java index d51b3f85f080..72361e8f8234 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootPluginIntegrationTests.java @@ -22,8 +22,8 @@ import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.gradle.testkit.GradleBuild; -import org.springframework.boot.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java index 73e7cda463f0..e1030c63d866 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/WarPluginActionIntegrationTests.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java index e18d9ecee625..bc9e7a4fe7f3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java @@ -30,8 +30,8 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; import org.springframework.boot.loader.tools.FileUtils; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java index 48f62516f997..aa733804e7e1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java @@ -49,9 +49,9 @@ import org.gradle.testkit.runner.TaskOutcome; import org.junit.jupiter.api.TestTemplate; -import org.springframework.boot.gradle.testkit.GradleBuild; import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.JarModeLibrary; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.util.FileSystemUtils; import org.springframework.util.StringUtils; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java index 14742f09a77d..90dc7d3c6175 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java @@ -44,7 +44,7 @@ import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.io.FilePermissions; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.java index 5fd56b4abb86..babc242ed217 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.java @@ -36,7 +36,7 @@ import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java index 975c63c8f4ca..09f21ebc12a4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenIntegrationTests.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java index 2571e1e25614..f39eafce4057 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/MavenPublishingIntegrationTests.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java index 67590476fa96..c9953c3e41eb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java @@ -30,7 +30,7 @@ import org.junit.jupiter.api.TestTemplate; import org.springframework.boot.gradle.junit.GradleCompatibility; -import org.springframework.boot.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; import org.springframework.util.FileSystemUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle index afcb084d112f..922c6530a3e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/build.gradle @@ -20,6 +20,16 @@ dependencies { compileOnly("org.springframework.data:spring-data-redis") compileOnly("org.testcontainers:testcontainers") + compileOnly(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform")) + compileOnly(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-tools")) + compileOnly("io.spring.gradle:dependency-management-plugin") + compileOnly(gradleTestKit()) + compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion") + compileOnly("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinVersion") + compileOnly("org.jetbrains.kotlin:kotlin-daemon-client:$kotlinVersion") + compileOnly("org.apache.commons:commons-compress") + implementation("org.apache.maven.resolver:maven-resolver-connector-basic") implementation("org.apache.maven.resolver:maven-resolver-impl") implementation("org.apache.maven:maven-resolver-provider") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/Dsl.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/Dsl.java similarity index 83% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/Dsl.java rename to spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/Dsl.java index ac352bf03196..27e2ff2e994f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/Dsl.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/Dsl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -14,13 +14,18 @@ * limitations under the License. */ -package org.springframework.boot.gradle.testkit; +package org.springframework.boot.testsupport.gradle.testkit; /** - * The DSLs supported by Gradle and demonstrated in the documentation samples + * The DSLs supported by Gradle and demonstrated in the documentation samples. + * + * @author Andy Wilkinson */ public enum Dsl { + /** + * Supported DSL variants. + */ GROOVY("Groovy", ".gradle"), KOTLIN("Kotlin", ".gradle.kts"); private final String name; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java similarity index 92% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java rename to spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java index d2f3a7be46f4..605b1b02f6ad 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuild.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuild.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.testkit; +package org.springframework.boot.testsupport.gradle.testkit; import java.io.File; import java.io.FileReader; @@ -74,8 +74,12 @@ public class GradleBuild { private String script; + private String settings; + private String gradleVersion; + private String springBootVersion = "TEST-SNAPSHOT"; + private GradleVersion expectDeprecationWarnings; private boolean configurationCache = false; @@ -84,8 +88,6 @@ public class GradleBuild { public GradleBuild() { this(Dsl.GROOVY); - this.scriptProperties.put("bootVersion", getBootVersion()); - this.scriptProperties.put("dependencyManagementPluginVersion", getDependencyManagementPluginVersion()); } public GradleBuild(Dsl dsl) { @@ -133,6 +135,10 @@ public GradleBuild script(String script) { return this; } + public void settings(String settings) { + this.settings = settings; + } + public GradleBuild expectDeprecationWarningsWithAtLeastVersion(String gradleVersion) { this.expectDeprecationWarnings = GradleVersion.version(gradleVersion); return this; @@ -173,12 +179,20 @@ public BuildResult buildAndFail(String... arguments) { public GradleRunner prepareRunner(String... arguments) throws IOException { String scriptContent = FileCopyUtils.copyToString(new FileReader(this.script)); + this.scriptProperties.put("bootVersion", getBootVersion()); + this.scriptProperties.put("dependencyManagementPluginVersion", getDependencyManagementPluginVersion()); for (Entry property : this.scriptProperties.entrySet()) { scriptContent = scriptContent.replace("{" + property.getKey() + "}", property.getValue()); } FileCopyUtils.copy(scriptContent, new FileWriter(new File(this.projectDir, "build" + this.dsl.getExtension()))); - FileSystemUtils.copyRecursively(new File("src/test/resources/repository"), - new File(this.projectDir, "repository")); + if (this.settings != null) { + FileCopyUtils.copy(new FileReader(this.settings), + new FileWriter(new File(this.projectDir, "settings.gradle"))); + } + File repository = new File("src/test/resources/repository"); + if (repository.exists()) { + FileSystemUtils.copyRecursively(repository, new File(this.projectDir, "repository")); + } GradleRunner gradleRunner = GradleRunner.create().withProjectDir(this.projectDir) .withPluginClasspath(pluginClasspath()); if (this.dsl != Dsl.KOTLIN && !this.configurationCache) { @@ -225,8 +239,13 @@ public String getGradleVersion() { return this.gradleVersion; } - private static String getBootVersion() { - return "TEST-SNAPSHOT"; + public GradleBuild bootVersion(String version) { + this.springBootVersion = version; + return this; + } + + private String getBootVersion() { + return this.springBootVersion; } private static String getDependencyManagementPluginVersion() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuildExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuildExtension.java similarity index 87% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuildExtension.java rename to spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuildExtension.java index eadf21d4452c..ee2583cd9a03 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/GradleBuildExtension.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleBuildExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.gradle.testkit; +package org.springframework.boot.testsupport.gradle.testkit; import java.lang.reflect.Field; import java.net.URL; @@ -32,6 +32,7 @@ * field named {@code gradleBuild}. * * @author Andy Wilkinson + * @author Scott Frederick */ public class GradleBuildExtension implements BeforeEachCallback, AfterEachCallback { @@ -46,6 +47,10 @@ public void beforeEach(ExtensionContext context) throws Exception { if (scriptUrl != null) { gradleBuild.script(scriptUrl.getFile()); } + URL settingsUrl = getSettings(context); + if (settingsUrl != null) { + gradleBuild.settings(settingsUrl.getFile()); + } gradleBuild.before(); } @@ -80,6 +85,11 @@ private URL getScriptForTestClass(Class testClass) { return testClass.getResource(testClass.getSimpleName() + this.dsl.getExtension()); } + private URL getSettings(ExtensionContext context) { + Class testClass = context.getRequiredTestClass(); + return testClass.getResource("settings.gradle"); + } + @Override public void afterEach(ExtensionContext context) throws Exception { extractGradleBuild(context).after(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java new file mode 100644 index 000000000000..12280b21bd62 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/gradle/testkit/GradleVersions.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.testsupport.gradle.testkit; + +import java.util.Arrays; +import java.util.List; + +import org.gradle.api.JavaVersion; +import org.gradle.util.GradleVersion; + +/** + * Versions of Gradle used for testing. + * + * @author Scott Frederick + */ +public final class GradleVersions { + + private GradleVersions() { + } + + public static List allCompatible() { + if (isJava16()) { + return Arrays.asList("7.0.2", "7.1"); + } + return Arrays.asList("6.8.3", GradleVersion.current().getVersion(), "7.0.2", "7.1.1"); + } + + public static String currentOrMinimumCompatible() { + if (isJava16()) { + return "7.0.2"; + } + return GradleVersion.current().getVersion(); + } + + private static boolean isJava16() { + return JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16); + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/build.gradle b/spring-boot-system-tests/spring-boot-image-tests/build.gradle new file mode 100644 index 000000000000..a81b650293b0 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/build.gradle @@ -0,0 +1,35 @@ +plugins { + id 'java-gradle-plugin' + id "org.springframework.boot.conventions" + id "org.springframework.boot.system-test" +} + +description = "Spring Boot Image Building Tests" + +configurations { + providedRuntime { + extendsFrom dependencyManagement + } +} + +systemTest { + if (project.hasProperty("springBootVersion")) { + systemProperty "springBootVersion", project.properties["springBootVersion"] + } else { + systemProperty "springBootVersion", project.getVersion() + } +} + +dependencies { + implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { + exclude group: "org.hibernate.validator" + } + + systemTestImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test")) + systemTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) + systemTestImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform")) + systemTestImplementation(gradleTestKit()) + systemTestImplementation("org.assertj:assertj-core") + systemTestImplementation("org.testcontainers:junit-jupiter") + systemTestImplementation("org.testcontainers:testcontainers") +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ContainerConfigAssert.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ContainerConfigAssert.java new file mode 100644 index 000000000000..890af544a8cb --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ContainerConfigAssert.java @@ -0,0 +1,134 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.assertions; + +import java.util.List; +import java.util.Map; + +import com.github.dockerjava.api.model.ContainerConfig; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.AbstractListAssert; +import org.assertj.core.api.AbstractObjectAssert; +import org.assertj.core.api.AbstractStringAssert; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.AssertionsForClassTypes; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.assertj.core.api.ListAssert; +import org.assertj.core.api.ObjectAssert; + +import org.springframework.boot.test.json.JsonContentAssert; + +/** + * AssertJ {@link org.assertj.core.api.Assert} for Docker image container configuration. + * + * @author Scott Frederick + */ +public class ContainerConfigAssert extends AbstractAssert { + + private static final String BUILD_METADATA_LABEL = "io.buildpacks.build.metadata"; + + private static final String LIFECYCLE_METADATA_LABEL = "io.buildpacks.lifecycle.metadata"; + + ContainerConfigAssert(ContainerConfig containerConfig) { + super(containerConfig, ContainerConfigAssert.class); + } + + public BuildMetadataAssert buildMetadata() { + return new BuildMetadataAssert(jsonLabel(BUILD_METADATA_LABEL)); + } + + public LifecycleMetadataAssert lifecycleMetadata() { + return new LifecycleMetadataAssert(jsonLabel(LIFECYCLE_METADATA_LABEL)); + } + + public AbstractStringAssert label(String label) { + return AssertionsForClassTypes.assertThat(getLabel(label)); + } + + private JsonContentAssert jsonLabel(String label) { + return new JsonContentAssert(ContainerConfigAssert.class, getLabel(label)); + } + + private String getLabel(String label) { + Map labels = this.actual.getLabels(); + if (labels == null) { + failWithMessage("Container config contains no labels"); + } + if (!labels.containsKey(label)) { + failWithActualExpectedAndMessage(labels, label, "Expected label not found in container config"); + } + return labels.get(label); + } + + /** + * Asserts for the JSON content in the {@code io.buildpacks.build.metadata} label. + * + * See the + * spec + */ + public static class BuildMetadataAssert extends AbstractAssert { + + BuildMetadataAssert(JsonContentAssert jsonContentAssert) { + super(jsonContentAssert, BuildMetadataAssert.class); + } + + public ListAssert buildpacks() { + return this.actual.extractingJsonPathArrayValue("$.buildpacks[*].id"); + } + + public ListAssert bomDependencies() { + return this.actual + .extractingJsonPathArrayValue("$.bom[?(@.name=='dependencies')].metadata.dependencies[*].name"); + } + + public AbstractStringAssert bomJavaVersion(String javaType) { + return this.actual.extractingJsonPathArrayValue("$.bom[?(@.name=='%s')].metadata.version", javaType) + .singleElement(Assertions.as(InstanceOfAssertFactories.STRING)); + } + + public AbstractObjectAssert processOfType(String type) { + return this.actual.extractingJsonPathArrayValue("$.processes[?(@.type=='%s')]", type).singleElement(); + } + + } + + /** + * Asserts for the the JSON content in the {@code io.buildpacks.lifecycle.metadata} + * label. + * + * See the + * spec + */ + public static class LifecycleMetadataAssert extends AbstractAssert { + + LifecycleMetadataAssert(JsonContentAssert jsonContentAssert) { + super(jsonContentAssert, LifecycleMetadataAssert.class); + } + + public ListAssert buildpackLayers(String buildpackId) { + return this.actual.extractingJsonPathArrayValue("$.buildpacks[?(@.key=='%s')].layers", buildpackId); + } + + public AbstractListAssert, Object, ObjectAssert> appLayerShas() { + return this.actual.extractingJsonPathArrayValue("$.app").extracting("sha"); + } + + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java new file mode 100644 index 000000000000..5baf07649177 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssert.java @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.assertions; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ListAssert; + +import org.springframework.boot.buildpack.platform.docker.DockerApi; +import org.springframework.boot.buildpack.platform.docker.type.ImageReference; +import org.springframework.boot.buildpack.platform.docker.type.Layer; + +/** + * AssertJ {@link org.assertj.core.api.Assert} for Docker image contents. + * + * @author Scott Frederick + */ +public class ImageAssert extends AbstractAssert { + + private final HashMap layers = new HashMap<>(); + + ImageAssert(ImageReference imageReference) throws IOException { + super(imageReference, ImageAssert.class); + getLayers(); + } + + public LayerContentAssert hasLayer(String layerDigest) { + if (!this.layers.containsKey(layerDigest)) { + failWithMessage("Layer with digest '%s' not found in image", layerDigest); + } + return new LayerContentAssert(this.layers.get(layerDigest)); + } + + private void getLayers() throws IOException { + new DockerApi().image().exportLayers(this.actual, (id, tarArchive) -> { + Layer layer = Layer.fromTarArchive(tarArchive); + this.layers.put(layer.getId().toString(), layer); + }); + } + + /** + * Asserts for image layers. + */ + public static class LayerContentAssert extends AbstractAssert { + + public LayerContentAssert(Layer layer) { + super(layer, LayerContentAssert.class); + } + + public ListAssert entries() throws IOException { + List entryNames = new ArrayList<>(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + this.actual.writeTo(out); + try (TarArchiveInputStream in = new TarArchiveInputStream(new ByteArrayInputStream(out.toByteArray()))) { + TarArchiveEntry entry = in.getNextTarEntry(); + while (entry != null) { + if (!entry.isDirectory()) { + entryNames.add(entry.getName().replaceFirst("^/workspace/", "")); + } + entry = in.getNextTarEntry(); + } + } + return Assertions.assertThat(entryNames); + } + + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssertions.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssertions.java new file mode 100644 index 000000000000..b13083943e33 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/assertions/ImageAssertions.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.assertions; + +import java.io.IOException; + +import com.github.dockerjava.api.model.ContainerConfig; + +import org.springframework.boot.buildpack.platform.docker.type.ImageReference; + +/** + * Factory class for custom AssertJ {@link org.assertj.core.api.Assert}s related to images + * and containers. + * + * @author Scott Frederick + */ +public final class ImageAssertions { + + private ImageAssertions() { + } + + public static ContainerConfigAssert assertThat(ContainerConfig containerConfig) { + return new ContainerConfigAssert(containerConfig); + } + + public static ImageAssert assertThat(ImageReference imageReference) throws IOException { + return new ImageAssert(imageReference); + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/junit/GradleBuildInjectionExtension.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/junit/GradleBuildInjectionExtension.java new file mode 100644 index 000000000000..4cafc9b3b059 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/junit/GradleBuildInjectionExtension.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.junit; + +import java.lang.reflect.Field; + +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleVersions; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * A {@link BeforeEachCallback} to configure and set a test class's {@code gradleBuild} + * field prior to test execution. + * + * @author Scott Frederick + */ +public class GradleBuildInjectionExtension implements BeforeEachCallback { + + private final GradleBuild gradleBuild; + + GradleBuildInjectionExtension() { + this.gradleBuild = new GradleBuild(); + this.gradleBuild.gradleVersion(GradleVersions.currentOrMinimumCompatible()); + String bootVersion = System.getProperty("springBootVersion"); + Assert.notNull(bootVersion, "Property 'springBootVersion' must be set in build environment"); + this.gradleBuild.bootVersion(bootVersion); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Field field = ReflectionUtils.findField(context.getRequiredTestClass(), "gradleBuild"); + field.setAccessible(true); + field.set(context.getRequiredTestInstance(), this.gradleBuild); + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/LayersIndex.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/LayersIndex.java new file mode 100644 index 000000000000..34460dbbe62a --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/LayersIndex.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.paketo; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +/** + * Index file describing the layers in the jar or war file and the files or directories in + * each layer. + * + * @author Scott Frederick + */ +class LayersIndex extends ArrayList>> { + + List getLayer(String layerName) { + return stream().filter((entry) -> entry.containsKey(layerName)).findFirst().map((entry) -> entry.get(layerName)) + .orElse(Collections.emptyList()); + } + + static LayersIndex fromArchiveFile(File archiveFile) throws IOException { + String indexPath = (archiveFile.getName().endsWith(".war") ? "WEB-INF/layers.idx" : "BOOT-INF/layers.idx"); + try (JarFile jarFile = new JarFile(archiveFile)) { + ZipEntry indexEntry = jarFile.getEntry(indexPath); + Yaml yaml = new Yaml(new Constructor(LayersIndex.class)); + return yaml.load(jarFile.getInputStream(indexEntry)); + } + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java new file mode 100644 index 000000000000..a0523919f82b --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/java/org/springframework/boot/image/paketo/PaketoBuilderTests.java @@ -0,0 +1,402 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.image.paketo; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +import com.github.dockerjava.api.model.ContainerConfig; +import org.assertj.core.api.Condition; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import org.springframework.boot.buildpack.platform.docker.DockerApi; +import org.springframework.boot.buildpack.platform.docker.type.ImageName; +import org.springframework.boot.buildpack.platform.docker.type.ImageReference; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension; +import org.springframework.boot.image.assertions.ImageAssertions; +import org.springframework.boot.image.junit.GradleBuildInjectionExtension; +import org.springframework.util.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for the Paketo builder and buildpacks. + * + * See + * https://paketo.io/docs/buildpacks/language-family-buildpacks/java/#additional-metadata + * + * @author Scott Frederick + */ +@ExtendWith({ GradleBuildInjectionExtension.class, GradleBuildExtension.class }) +class PaketoBuilderTests { + + GradleBuild gradleBuild; + + @Test + void executableJarApp() throws Exception { + writeMainClass(); + String imageName = "paketo-integration/" + this.gradleBuild.getProjectDir().getName(); + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + ContainerConfig config = container.getContainerInfo().getConfig(); + assertLabelsMatchManifestAttributes(config); + ImageAssertions.assertThat(config).buildMetadata().buildpacks().containsExactly( + "paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", + "paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); + ImageAssertions.assertThat(config).buildMetadata().bomDependencies().contains("spring-beans", "spring-boot", + "spring-boot-autoconfigure", "spring-boot-jarmode-layertools", "spring-context", "spring-core", + "spring-web"); + ImageAssertions.assertThat(config).buildMetadata().bomJavaVersion("jre").startsWith(javaMajorVersion()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") + .containsExactly("java", Collections.singletonList("org.springframework.boot.loader.JarLauncher")); + ImageAssertions.assertThat(config).buildMetadata().processOfType("executable-jar") + .extracting("command", "args") + .containsExactly("java", Collections.singletonList("org.springframework.boot.loader.JarLauncher")); + assertImageLayersMatchLayersIndex(imageReference, config); + } + finally { + removeImage(imageReference); + } + } + + @Test + void executableJarAppWithAdditionalArgs() throws Exception { + writeMainClass(); + String imageName = "paketo-integration/" + this.gradleBuild.getProjectDir().getName(); + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withCommand("--server.port=9090") + .withExposedPorts(9090)) { + container.waitingFor(Wait.forHttp("/test")).start(); + } + finally { + removeImage(imageReference); + } + } + + @Test + void executableJarAppBuiltTwiceWithCaching() throws Exception { + writeMainClass(); + String imageName = "paketo-integration/" + this.gradleBuild.getProjectDir().getName(); + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + container.stop(); + } + result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + } + finally { + removeImage(imageReference); + } + } + + @Test + void bootDistZipJarApp() throws Exception { + writeMainClass(); + String projectName = this.gradleBuild.getProjectDir().getName(); + String imageName = "paketo-integration/" + projectName; + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName, "assemble", "bootDistZip"); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + ContainerConfig config = container.getContainerInfo().getConfig(); + ImageAssertions.assertThat(config).buildMetadata().buildpacks().containsExactly( + "paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", + "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); + ImageAssertions.assertThat(config).buildMetadata().bomJavaVersion("jre").startsWith(javaMajorVersion()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") + .containsExactly("/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("dist-zip").extracting("command", "args") + .containsExactly("/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList()); + DigestCapturingCondition digests = new DigestCapturingCondition(); + ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries().contains( + projectName + "-boot/bin/" + projectName, projectName + "-boot/lib/" + projectName + ".jar"); + } + finally { + removeImage(imageReference); + } + } + + @Test + void plainDistZipJarApp() throws Exception { + writeMainClass(); + String projectName = this.gradleBuild.getProjectDir().getName(); + String imageName = "paketo-integration/" + projectName; + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName, "assemble", "bootDistZip"); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + ContainerConfig config = container.getContainerInfo().getConfig(); + ImageAssertions.assertThat(config).buildMetadata().buildpacks().containsExactly( + "paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", + "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); + ImageAssertions.assertThat(config).buildMetadata().bomJavaVersion("jre").startsWith(javaMajorVersion()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") + .containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("dist-zip").extracting("command", "args") + .containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList()); + DigestCapturingCondition digests = new DigestCapturingCondition(); + ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() + .contains(projectName + "/bin/" + projectName, projectName + "/lib/" + projectName + "-plain.jar") + .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-boot-")) + .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-core-")) + .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-web-")); + } + finally { + removeImage(imageReference); + } + } + + @Test + void executableWarApp() throws Exception { + writeMainClass(); + writeServletInitializerClass(); + String imageName = "paketo-integration/" + this.gradleBuild.getProjectDir().getName(); + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + ContainerConfig config = container.getContainerInfo().getConfig(); + assertLabelsMatchManifestAttributes(config); + ImageAssertions.assertThat(config).buildMetadata().buildpacks().containsExactly( + "paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", + "paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); + ImageAssertions.assertThat(config).buildMetadata().bomDependencies().contains("spring-beans", "spring-boot", + "spring-boot-autoconfigure", "spring-boot-jarmode-layertools", "spring-context", "spring-core", + "spring-web"); + ImageAssertions.assertThat(config).buildMetadata().bomJavaVersion("jre").startsWith(javaMajorVersion()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") + .containsExactly("java", Collections.singletonList("org.springframework.boot.loader.WarLauncher")); + ImageAssertions.assertThat(config).buildMetadata().processOfType("executable-jar") + .extracting("command", "args") + .containsExactly("java", Collections.singletonList("org.springframework.boot.loader.WarLauncher")); + assertImageLayersMatchLayersIndex(imageReference, config); + } + finally { + removeImage(imageReference); + } + } + + @Test + void plainWarApp() throws Exception { + writeMainClass(); + writeServletInitializerClass(); + String imageName = "paketo-integration/" + this.gradleBuild.getProjectDir().getName(); + ImageReference imageReference = ImageReference.of(ImageName.of(imageName)); + BuildResult result = buildImage(imageName); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { + container.waitingFor(Wait.forHttp("/test")).start(); + ContainerConfig config = container.getContainerInfo().getConfig(); + ImageAssertions.assertThat(config).buildMetadata().buildpacks().containsExactly( + "paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", + "paketo-buildpacks/apache-tomcat", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); + ImageAssertions.assertThat(config).buildMetadata().bomJavaVersion("jre").startsWith(javaMajorVersion()); + ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") + .containsExactly("catalina.sh", Collections.singletonList("run")); + ImageAssertions.assertThat(config).buildMetadata().processOfType("tomcat").extracting("command", "args") + .containsExactly("catalina.sh", Collections.singletonList("run")); + DigestCapturingCondition digests = new DigestCapturingCondition(); + ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() + .contains("WEB-INF/classes/example/ExampleApplication.class", + "WEB-INF/classes/example/HelloController.class", "META-INF/MANIFEST.MF") + .anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-boot-")) + .anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-core-")) + .anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-web-")); + } + finally { + removeImage(imageReference); + } + } + + public BuildResult buildImage(String imageName, String... arguments) { + String[] buildImageArgs = { "bootBuildImage", "--imageName=" + imageName, "--pullPolicy=IF_NOT_PRESENT" }; + String[] args = StringUtils.concatenateStringArrays(arguments, buildImageArgs); + return this.gradleBuild.build(args); + } + + private void writeMainClass() throws IOException { + writeProjectFile("ExampleApplication.java", (writer) -> { + writer.println("package example;"); + writer.println(); + writer.println("import org.springframework.boot.SpringApplication;"); + writer.println("import org.springframework.boot.autoconfigure.SpringBootApplication;"); + writer.println("import org.springframework.stereotype.Controller;"); + writer.println("import org.springframework.web.bind.annotation.RequestMapping;"); + writer.println("import org.springframework.web.bind.annotation.ResponseBody;"); + writer.println(); + writer.println("@SpringBootApplication"); + writer.println("public class ExampleApplication {"); + writer.println(); + writer.println(" public static void main(String[] args) {"); + writer.println(" SpringApplication.run(ExampleApplication.class, args);"); + writer.println(" }"); + writer.println(); + writer.println("}"); + writer.println(); + writer.println("@Controller"); + writer.println("class HelloController {"); + writer.println(); + writer.println(" @RequestMapping(\"/test\")"); + writer.println(" @ResponseBody"); + writer.println(" String home() {"); + writer.println(" return \"Hello, world!\";"); + writer.println(" }"); + writer.println(); + writer.println("}"); + }); + } + + private void writeServletInitializerClass() throws IOException { + writeProjectFile("ServletInitializer.java", (writer) -> { + writer.println("package example;"); + writer.println(); + writer.println("import org.springframework.boot.builder.SpringApplicationBuilder;"); + writer.println("import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;"); + writer.println(); + writer.println("public class ServletInitializer extends SpringBootServletInitializer {"); + writer.println(); + writer.println(" @Override"); + writer.println(" protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {"); + writer.println(" return application.sources(ExampleApplication.class);"); + writer.println(" }"); + writer.println(); + writer.println("}"); + }); + } + + private void writeProjectFile(String fileName, Consumer consumer) throws IOException { + File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example"); + examplePackage.mkdirs(); + File main = new File(examplePackage, fileName); + try (PrintWriter writer = new PrintWriter(new FileWriter(main))) { + consumer.accept(writer); + } + } + + private void assertLabelsMatchManifestAttributes(ContainerConfig config) throws IOException { + JarFile jarFile = new JarFile(projectArchiveFile()); + Attributes attributes = jarFile.getManifest().getMainAttributes(); + ImageAssertions.assertThat(config).label("org.springframework.boot.version") + .isEqualTo(attributes.getValue("Spring-Boot-Version")); + ImageAssertions.assertThat(config).label("org.opencontainers.image.title") + .isEqualTo(attributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE)); + ImageAssertions.assertThat(config).label("org.opencontainers.image.version") + .isEqualTo(attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION)); + } + + private void assertImageLayersMatchLayersIndex(ImageReference imageReference, ContainerConfig config) + throws IOException { + DigestCapturingCondition digests = new DigestCapturingCondition(); + ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(5, digests); + LayersIndex layersIndex = LayersIndex.fromArchiveFile(projectArchiveFile()); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() + .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("dependencies"))); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(1)).entries() + .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("spring-boot-loader"))); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(2)).entries() + .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("snapshot-dependencies"))); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(3)).entries() + .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("application"))); + ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(4)).entries() + .allMatch((entry) -> entry.contains("lib/spring-cloud-bindings-")); + } + + private File projectArchiveFile() { + return new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0]; + } + + private String javaMajorVersion() { + String javaVersion = System.getProperty("java.version"); + if (javaVersion.startsWith("1.")) { + return javaVersion.substring(2, 3); + } + else { + int firstDotIndex = javaVersion.indexOf("."); + if (firstDotIndex != -1) { + return javaVersion.substring(0, firstDotIndex); + } + } + return javaVersion; + } + + private boolean startsWithOneOf(String actual, List expectedPrefixes) { + for (String prefix : expectedPrefixes) { + if (actual.startsWith(prefix)) { + return true; + } + } + return false; + } + + private void removeImage(ImageReference image) throws IOException { + new DockerApi().image().remove(image, false); + } + + private static class DigestCapturingCondition extends Condition { + + private static List digests; + + DigestCapturingCondition() { + super(predicate(), "a value starting with 'sha256:'"); + } + + private static Predicate predicate() { + digests = new ArrayList<>(); + return (sha) -> { + digests.add(sha.toString()); + return sha.toString().startsWith("sha256:"); + }; + } + + String getDigest(int index) { + return digests.get(index); + } + + } + +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle new file mode 100644 index 000000000000..a74b6cb9e875 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-bootDistZipJarApp.gradle @@ -0,0 +1,33 @@ +plugins { + id 'org.springframework.boot' version '{bootVersion}' + id 'io.spring.dependency-management' version '{dependencyManagementPluginVersion}' + id 'java' + id 'application' +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:{bootVersion}") +} + +bootJar { + manifest { + attributes( + 'Implementation-Version': '1.0.0', + 'Implementation-Title': "Paketo Test" + ) + } +} + +application { + mainClass = 'example.ExampleApplication' +} + +bootBuildImage { + archiveFile = new File("${buildDir}/distributions/${project.name}-boot.zip") +} \ No newline at end of file diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-executableWarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-executableWarApp.gradle new file mode 100644 index 000000000000..e7892dd5e770 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-executableWarApp.gradle @@ -0,0 +1,26 @@ +plugins { + id 'org.springframework.boot' version '{bootVersion}' + id 'io.spring.dependency-management' version '{dependencyManagementPluginVersion}' + id 'java' + id 'war' +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:{bootVersion}") + providedRuntime("org.springframework.boot:spring-boot-starter-tomcat:{bootVersion}") +} + +bootWar { + manifest { + attributes( + 'Implementation-Version': '1.0.0', + 'Implementation-Title': "Paketo Test" + ) + } +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle new file mode 100644 index 000000000000..fb331a170432 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainDistZipJarApp.gradle @@ -0,0 +1,33 @@ +plugins { + id 'org.springframework.boot' version '{bootVersion}' + id 'io.spring.dependency-management' version '{dependencyManagementPluginVersion}' + id 'java' + id 'application' +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:{bootVersion}") +} + +bootJar { + manifest { + attributes( + 'Implementation-Version': '1.0.0', + 'Implementation-Title': "Paketo Test" + ) + } +} + +application { + mainClass = 'example.ExampleApplication' +} + +bootBuildImage { + archiveFile = new File("${buildDir}/distributions/${project.name}.zip") +} \ No newline at end of file diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainWarApp.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainWarApp.gradle new file mode 100644 index 000000000000..3a40cc9271b7 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests-plainWarApp.gradle @@ -0,0 +1,26 @@ +plugins { + id 'org.springframework.boot' version '{bootVersion}' + id 'io.spring.dependency-management' version '{dependencyManagementPluginVersion}' + id 'java' + id 'war' +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:{bootVersion}") + providedRuntime("org.springframework.boot:spring-boot-starter-tomcat:{bootVersion}") +} + +war { + enabled = true + archiveClassifier.set('plain') +} + +bootBuildImage { + archiveFile = war.archiveFile +} diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests.gradle new file mode 100644 index 000000000000..305b7f3af2e6 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/PaketoBuilderTests.gradle @@ -0,0 +1,24 @@ +plugins { + id 'org.springframework.boot' version '{bootVersion}' + id 'io.spring.dependency-management' version '{dependencyManagementPluginVersion}' + id 'java' +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:{bootVersion}") +} + +bootJar { + manifest { + attributes( + 'Implementation-Version': '1.0.0', + 'Implementation-Title': "Paketo Test" + ) + } +} \ No newline at end of file diff --git a/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/settings.gradle b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/settings.gradle new file mode 100644 index 000000000000..fb0915aec858 --- /dev/null +++ b/spring-boot-system-tests/spring-boot-image-tests/src/systemTest/resources/org/springframework/boot/image/paketo/settings.gradle @@ -0,0 +1,7 @@ +pluginManagement { + repositories { + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } + gradlePluginPortal() + } +} \ No newline at end of file diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index c9553cb27b28..bdbeedc22d60 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -45,7 +45,9 @@ + +