From f117f59c8a3af14a9f862f09884be6ad2801e59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Mon, 4 Oct 2021 16:00:11 +0200 Subject: [PATCH] Bump ByteBuddy to 1.11.16, use TestKit in agent integration test (#2776) This commit bumps Byte-Buddy to 1.11.16, which makes the ad-hoc groovy integration testing of the Byte Buddy gradle plugin feature non functional due to breaking changes. After much trial-and-error, the best way to fix that integration test I found was to completely separate the test, as a inlined project in resources. The entire project can then be copied in the `buildDir` and executed using Gradle TestKit. In order to get access to the snapshot of code under test, some token replacement is used with the `files(@TOKEN@)` type of dependency. This allowed to resurface the importance of using the `reactor-tools` `-original.jar` version, as otherwise the ByteBuddy gradle plugin will refuse to use the reactor Plugin (which implements the shaded interface). See https://github.com/raphw/byte-buddy/issues/833 for the original issue that lead to the previous code. --- build.gradle | 2 +- reactor-tools/build.gradle | 47 +++++++++----- .../ApplyingByteBuddyPluginGradleTest.java | 61 ++++++++++++++++++ .../resources/mock-gradle/build.gradle | 63 +++++++++++++++++++ .../resources/mock-gradle/settings.gradle | 17 +++++ .../src/main/java/demo/SomeClass.java | 27 ++++++++ .../src/test/java/demo/SomeClassTest.java} | 24 ++++--- 7 files changed, 215 insertions(+), 26 deletions(-) create mode 100644 reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ApplyingByteBuddyPluginGradleTest.java create mode 100644 reactor-tools/src/buildPluginTest/resources/mock-gradle/build.gradle create mode 100644 reactor-tools/src/buildPluginTest/resources/mock-gradle/settings.gradle create mode 100644 reactor-tools/src/buildPluginTest/resources/mock-gradle/src/main/java/demo/SomeClass.java rename reactor-tools/src/buildPluginTest/{java/reactor/tools/agent/ReactorDebugBuildPluginTest.java => resources/mock-gradle/src/test/java/demo/SomeClassTest.java} (58%) diff --git a/build.gradle b/build.gradle index e33e355098..b07e163bbe 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ ext { archUnitVersion = '0.21.0' // For reactor-tools - byteBuddyVersion = '1.10.9' //sync with plugin version above //TODO bump to 1.11.16 with breaking changes + byteBuddyVersion = '1.11.16' //dependency, but plugin usage is now in a mock build done via TestKit cgLibVersion = '3.3.0' // JMH diff --git a/reactor-tools/build.gradle b/reactor-tools/build.gradle index 2947650e74..4f3408695f 100644 --- a/reactor-tools/build.gradle +++ b/reactor-tools/build.gradle @@ -14,9 +14,7 @@ * limitations under the License. */ -plugins { - id 'net.bytebuddy.byte-buddy-gradle-plugin' version "$byteBuddyVersion" apply false -} +import org.apache.tools.ant.filters.ReplaceTokens apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'org.unbroken-dome.test-sets' @@ -53,6 +51,11 @@ dependencies { testImplementation "cglib:cglib:$cgLibVersion" jarFileTestImplementation "org.assertj:assertj-core:$assertJVersion" + + buildPluginTestImplementation gradleTestKit() + buildPluginTestImplementation platform("org.junit:junit-bom:${jUnitPlatformVersion}") + buildPluginTestImplementation "org.junit.jupiter:junit-jupiter-api" + buildPluginTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" } test { @@ -157,18 +160,32 @@ javaAgentTest { } project.tasks.check.dependsOn(javaAgentTest) -// See https://github.com/raphw/byte-buddy/issues/833 -compileBuildPluginTestJava.doLast( - new net.bytebuddy.build.gradle.TransformationAction( - project, - new net.bytebuddy.build.gradle.ByteBuddyExtension(project).tap { - transformation { - plugin = "reactor.tools.agent.ReactorDebugByteBuddyPlugin" - } - }, - compileBuildPluginTestJava - ) -) +project.tasks.buildPluginTest.mustRunAfter(shadowJar) +project.tasks.buildPluginTest.dependsOn(shadowJar) + +task generateMockGradle(type: Copy) { + def coreJar = rootProject.findProject("reactor-core").buildDir.toString() + "/libs/reactor-core-" + version + ".jar" + def agentJar = buildDir.toString() + "/libs/reactor-tools-" + version + "-original.jar" + def mockGradleDir = buildDir.toString() + "/mock-gradle" + + from "$projectDir/src/buildPluginTest/resources/mock-gradle" + into "$mockGradleDir" + filter(ReplaceTokens, tokens: [ + CORE: coreJar, + AGENT: agentJar, + REACTIVE_STREAMS_VERSION: rootProject.ext.reactiveStreamsVersion, + JUNIT_BOM_VERSION: rootProject.ext.jUnitPlatformVersion, + BYTE_BUDDY_VERSION: rootProject.ext.byteBuddyVersion + ]) +} + +buildPluginTest { + def mockGradleDir = "$buildDir/mock-gradle" + println "will run mock gradle build from $mockGradleDir" + systemProperty "mock-gradle-dir", mockGradleDir +} + +project.tasks.buildPluginTest.dependsOn(generateMockGradle) project.tasks.check.dependsOn(buildPluginTest) tasks.withType(Test).all { diff --git a/reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ApplyingByteBuddyPluginGradleTest.java b/reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ApplyingByteBuddyPluginGradleTest.java new file mode 100644 index 0000000000..f421ed7ee3 --- /dev/null +++ b/reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ApplyingByteBuddyPluginGradleTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. + * + * 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 reactor.tools.agent; + +import java.io.File; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.BuildTask; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Simon Baslé + */ +class ApplyingByteBuddyPluginGradleTest { + + static File testProjectDir; + + @BeforeEach + void setup() { + String testProjectPath = System.getProperty("mock-gradle-dir"); + assertNotNull(testProjectPath, "Cannot find testProjectPath, set or verify -Dmock-gradle-dir"); + testProjectDir = new File(testProjectPath); + assertTrue(testProjectDir.exists() && testProjectDir.isDirectory(), "testProjectDir not created correctly"); + } + + @Test + void applyingByteBuddyPluginDuringGradleBuild() { + BuildResult result = GradleRunner.create() + .withProjectDir(testProjectDir) + .withDebug(true) + .withArguments("test", "--info", "--stacktrace") + .build(); + + //the test task in reactor-tools/src/buildPluginTest/resources/mock-gradle/src/test/java/demo/SomeClassTest.java + //checks that applying the reactor-tool ByteBuddy plugin in Gradle instruments prod code but not test code. + + assertTrue(result.getOutput().contains("test")); + final BuildTask task = result.task(":test"); + assertNotNull(task); + assertEquals(TaskOutcome.SUCCESS, task.getOutcome()); + } +} diff --git a/reactor-tools/src/buildPluginTest/resources/mock-gradle/build.gradle b/reactor-tools/src/buildPluginTest/resources/mock-gradle/build.gradle new file mode 100644 index 0000000000..4ef82fb863 --- /dev/null +++ b/reactor-tools/src/buildPluginTest/resources/mock-gradle/build.gradle @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. + * + * 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. + */ + +buildscript { + repositories { + mavenCentral() + } + dependencies { + //the plugin feature only works with the -original jar !! + //otherwise implemented Plugin interface is the shaded one + classpath files("@AGENT@") + } +} + +plugins { + id "net.bytebuddy.byte-buddy-gradle-plugin" version "@BYTE_BUDDY_VERSION@" + id 'java' +} + +repositories { + mavenCentral() +} + +ext { + //smoke test to fail the mock gradle if it is wrongly configured with the shaded jar rather than the original + if (!"@AGENT@".endsWith("-original.jar")) { + throw new GradleException("The build file must be configured with reactor-tools' -original.jar version !!") + } +} + +dependencies { + implementation "org.reactivestreams:reactive-streams:@REACTIVE_STREAMS_VERSION@" + implementation files("@CORE@") + + testImplementation platform("org.junit:junit-bom:@JUNIT_BOM_VERSION@") + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + + testImplementation "org.assertj:assertj-core:3.20.2" +} + +test { + useJUnitPlatform() +} + +byteBuddy { + transformation { + plugin = reactor.tools.agent.ReactorDebugByteBuddyPlugin.class + } +} diff --git a/reactor-tools/src/buildPluginTest/resources/mock-gradle/settings.gradle b/reactor-tools/src/buildPluginTest/resources/mock-gradle/settings.gradle new file mode 100644 index 0000000000..409bf5f022 --- /dev/null +++ b/reactor-tools/src/buildPluginTest/resources/mock-gradle/settings.gradle @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. + * + * 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. + */ + +rootProject.name = 'bytebuddy-and-reactoragent' \ No newline at end of file diff --git a/reactor-tools/src/buildPluginTest/resources/mock-gradle/src/main/java/demo/SomeClass.java b/reactor-tools/src/buildPluginTest/resources/mock-gradle/src/main/java/demo/SomeClass.java new file mode 100644 index 0000000000..d9a0f273c6 --- /dev/null +++ b/reactor-tools/src/buildPluginTest/resources/mock-gradle/src/main/java/demo/SomeClass.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. + * + * 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 demo; + +import reactor.core.publisher.Flux; + +public class SomeClass { + + public Flux obtainFlux() { + return Flux.just(1); + } + +} \ No newline at end of file diff --git a/reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ReactorDebugBuildPluginTest.java b/reactor-tools/src/buildPluginTest/resources/mock-gradle/src/test/java/demo/SomeClassTest.java similarity index 58% rename from reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ReactorDebugBuildPluginTest.java rename to reactor-tools/src/buildPluginTest/resources/mock-gradle/src/test/java/demo/SomeClassTest.java index 79f96dfc94..07445709ed 100644 --- a/reactor-tools/src/buildPluginTest/java/reactor/tools/agent/ReactorDebugBuildPluginTest.java +++ b/reactor-tools/src/buildPluginTest/resources/mock-gradle/src/test/java/demo/SomeClassTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021 VMware Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,30 +14,34 @@ * limitations under the License. */ -package reactor.tools.agent; +package demo; import org.junit.jupiter.api.Test; + import reactor.core.Scannable; import reactor.core.publisher.Flux; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.STRING; /** * This test assumes that the transformation is applied at build time * and does not manually install it. */ -public class ReactorDebugBuildPluginTest { +public class SomeClassTest { @Test public void shouldAddAssemblyInfo() { - int baseline = getBaseline(); - Flux flux = Flux.just(1); + //the first level is instantiated from the production code, which is instrumented + Flux flux = new SomeClass().obtainFlux(); + //the second level is instantiated here in test code, which isn't instrumented + flux = flux.map(i -> i); - assertThat(Scannable.from(flux).stepName()) - .startsWith("Flux.just ⇢ at reactor.tools.agent.ReactorDebugBuildPluginTest.shouldAddAssemblyInfo(ReactorDebugBuildPluginTest.java:" + (baseline + 1)); - } + Scannable scannable = Scannable.from(flux); - private static int getBaseline() { - return new Exception().getStackTrace()[1].getLineNumber(); + assertThat(scannable.steps()) + .hasSize(2) + .endsWith("map") + .first(STRING).startsWith("Flux.just ⇢ at demo.SomeClass.obtainFlux(SomeClass.java:"); } }