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 31559c058b..6176fc8b5c 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 0000000000..e0352bbf02 --- /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 c83a76ac4e..63b1f67ff9 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 0000000000..4cb3025da6 --- /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 0000000000..5a1f6e5bd0 --- /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 c75bb113f1..90c976ef51 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 404dba2c72..a1ce4a316e 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.TestTemplate; 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 da30a982fc..748899b2aa 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.TestTemplate; 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 d2608379fa..be33e3567d 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.TestTemplate; 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 6b68af4c5b..cc0330e423 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.TestTemplate; 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 028dfdd109..6bbba73fac 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.condition.JRE; 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 87d597f384..dbf5b95622 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.condition.JRE; 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 3ca1134000..c584b3deda 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.junit.jupiter.api.TestTemplate; 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 19ea8e67cc..7dcf7be83e 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 java.lang.reflect.Field; 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 9617f0d94d..5ab519faee 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.TestTemplate; 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 7d4876e20a..ce30517e0f 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.Arrays; 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.TestTemplateInvocationContext; 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 @@ import org.springframework.util.StringUtils; */ 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 829e220e1b..9cebd90121 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.Arrays; 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 @@ public class GradleMultiDslExtension implements TestTemplateInvocationContextPro @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 5a595258c0..fee3f56d77 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.gradle.testkit.runner.TaskOutcome; 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 73975c1435..1041969f10 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.gradle.testkit.runner.TaskOutcome; 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 9633fc1830..8bfb19ac37 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.gradle.testkit.runner.TaskOutcome; 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 4f3a0732e3..07568b49a5 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 @@ package org.springframework.boot.gradle.plugin; 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 c4672e8071..3c8fc1279b 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.DisabledForJreRange; 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 ad8145a1f8..e9424d9032 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 @@ package org.springframework.boot.gradle.plugin; 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 d51b3f85f0..72361e8f82 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.DisabledForJreRange; 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 73e7cda463..e1030c63d8 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.gradle.testkit.runner.TaskOutcome; 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 e18d9ecee6..bc9e7a4fe7 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.gradle.testkit.runner.TaskOutcome; 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 48f62516f9..aa733804e7 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.BuildResult; 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 14742f09a7..90dc7d3c61 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.ImageName; 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 5fd56b4abb..babc242ed2 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.UpdateListener; 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 975c63c8f4..09f21ebc12 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.DisabledForJreRange; 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 2571e1e256..f39eafce40 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.gradle.testkit.runner.TaskOutcome; 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 67590476fa..c9953c3e41 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.gradle.testkit.runner.TaskOutcome; 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 afcb084d11..922c6530a3 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 ac352bf031..27e2ff2e99 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 d2f3a7be46..605b1b02f6 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 class GradleBuild { 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 class GradleBuild { 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 class GradleBuild { 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 eadf21d445..ee2583cd9a 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 @@ import org.springframework.util.ReflectionUtils; * field named {@code gradleBuild}. * * @author Andy Wilkinson + * @author Scott Frederick */ public class GradleBuildExtension implements BeforeEachCallback, AfterEachCallback { @@ -46,6 +47,10 @@ public class GradleBuildExtension implements BeforeEachCallback, AfterEachCallba if (scriptUrl != null) { gradleBuild.script(scriptUrl.getFile()); } + URL settingsUrl = getSettings(context); + if (settingsUrl != null) { + gradleBuild.settings(settingsUrl.getFile()); + } gradleBuild.before(); } @@ -80,6 +85,11 @@ public class GradleBuildExtension implements BeforeEachCallback, AfterEachCallba 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 0000000000..12280b21bd --- /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 0000000000..a81b650293 --- /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 0000000000..890af544a8 --- /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 0000000000..5baf076491 --- /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 0000000000..b13083943e --- /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 0000000000..4cafc9b3b0 --- /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 0000000000..34460dbbe6 --- /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 0000000000..a0523919f8 --- /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 0000000000..a74b6cb9e8 --- /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 0000000000..e7892dd5e7 --- /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 0000000000..fb331a1704 --- /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 0000000000..3a40cc9271 --- /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 0000000000..305b7f3af2 --- /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 0000000000..fb0915aec8 --- /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 c9553cb27b..bdbeedc22d 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -45,7 +45,9 @@ + +