From a35ea43b8f81fe275d352820cee9d02244ff70d8 Mon Sep 17 00:00:00 2001 From: Violeta Georgieva Date: Wed, 13 Sep 2023 14:09:31 +0300 Subject: [PATCH] Add GraalVM smoke tests (#2899) Temporary add the missing GraalVM metadata to the test as the PRs below are still in progress https://github.com/oracle/graalvm-reachability-metadata/pull/379 https://github.com/oracle/graalvm-reachability-metadata/pull/382 --- .github/workflows/check_graalvm.yml | 39 ++++++ build.gradle | 5 +- gradle/javadoc.gradle | 4 +- gradle/setup.gradle | 2 +- .../build.gradle | 68 ++++++++++ .../java/reactor/netty/http/HttpTests.java | 76 +++++++++++ .../META-INF/native-image/jni-config.json | 126 ++++++++++++++++++ .../META-INF/native-image/reflect-config.json | 42 ++++++ .../native-image/resource-config.json | 19 +++ settings.gradle | 3 +- 10 files changed, 378 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/check_graalvm.yml create mode 100644 reactor-netty-graalvm-smoke-tests/build.gradle create mode 100644 reactor-netty-graalvm-smoke-tests/src/test/java/reactor/netty/http/HttpTests.java create mode 100644 reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/jni-config.json create mode 100644 reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/reflect-config.json create mode 100644 reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/resource-config.json diff --git a/.github/workflows/check_graalvm.yml b/.github/workflows/check_graalvm.yml new file mode 100644 index 0000000000..af63c5c747 --- /dev/null +++ b/.github/workflows/check_graalvm.yml @@ -0,0 +1,39 @@ +name: GraalVM smoke tests + +on: + pull_request: {} +permissions: read-all +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, macos-11] + transport: [native, nio] + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 1.8 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '8' + - name: Download GraalVM 17 + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + download_url="https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-17.0.8/graalvm-community-jdk-17.0.8_linux-x64_bin.tar.gz" + else + download_url="https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-17.0.8/graalvm-community-jdk-17.0.8_macos-x64_bin.tar.gz" + fi + curl -L $download_url --output $RUNNER_TEMP/java_package.tar.gz + shell: bash + - name: Set up GraalVM 17 + uses: actions/setup-java@v3 + with: + distribution: 'jdkfile' + jdkFile: ${{ runner.temp }}/java_package.tar.gz + java-version: '17' + - name: Build with Gradle + run: ./gradlew :reactor-netty-graalvm-smoke-tests:nativeTest --no-daemon -PforceTransport=${{ matrix.transport }} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 492f57904e..f503ba2c7a 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,7 @@ plugins { id 'io.spring.javadoc' version '0.0.1' apply false id 'io.spring.javadoc-aggregate' version '0.0.1' apply false id 'biz.aQute.bnd.builder' version '6.4.0' apply false + id 'org.graalvm.buildtools.native' version '0.9.25' apply false } description = 'Reactive Streams Netty driver' @@ -167,9 +168,9 @@ spotless { format 'gradle', { target '**/*.gradle' // find start of gradle files by looking for either `import`, `apply` - // or start of blocks like `javadoc{` or `configure(rootProject) {`... + // or start of blocks like `javadoc {` or `configure(rootProject) {`... licenseHeaderFile('codequality/spotless/licenseSlashstarStyle.txt', - "^\\w+(\\(\\w+\\))?\\s?\\{\$|import|apply") + "^\\w+(\\(\\w+\\))?\\s?\\{?|import|apply") } } diff --git a/gradle/javadoc.gradle b/gradle/javadoc.gradle index ba9a9ffa76..333e11b304 100644 --- a/gradle/javadoc.gradle +++ b/gradle/javadoc.gradle @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2020-2023 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -if (project.name == 'reactor-netty-examples') { +if (project.name == 'reactor-netty-examples' || project.name == 'reactor-netty-graalvm-smoke-tests') { return } diff --git a/gradle/setup.gradle b/gradle/setup.gradle index 1341c45223..65d0ce2e09 100644 --- a/gradle/setup.gradle +++ b/gradle/setup.gradle @@ -15,7 +15,7 @@ */ import org.gradle.util.VersionNumber -if (project.name == 'reactor-netty-examples') { +if (project.name == 'reactor-netty-examples' || project.name == 'reactor-netty-graalvm-smoke-tests') { return } diff --git a/reactor-netty-graalvm-smoke-tests/build.gradle b/reactor-netty-graalvm-smoke-tests/build.gradle new file mode 100644 index 0000000000..ec960bf470 --- /dev/null +++ b/reactor-netty-graalvm-smoke-tests/build.gradle @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 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. + */ +apply plugin: 'org.graalvm.buildtools.native' + +dependencies { + // MacOS binaries are not available for Netty SNAPSHOT version + if (!"$nettyVersion".endsWithAny("SNAPSHOT")) { + if (osdetector.classifier == "osx-x86_64" || osdetector.classifier == "osx-aarch_64") { + testRuntimeOnly "io.netty:netty-resolver-dns-native-macos:$nettyVersion$os_suffix" + } else { + testRuntimeOnly "io.netty:netty-resolver-dns-native-macos:$nettyVersion:osx-x86_64" + } + } else { + // MacOS binaries are not available for Netty SNAPSHOT version + testRuntimeOnly "io.netty:netty-resolver-dns-native-macos:$nettyVersion" + } + + testImplementation "io.netty:netty-transport-native-epoll:$nettyVersion" + testImplementation "io.netty:netty-transport-native-kqueue:$nettyVersion" + testImplementation "io.netty.incubator:netty-incubator-transport-native-io_uring:$nettyIoUringVersion" + if (project.hasProperty("forceTransport")) { + //now we explicitly add correctly qualified native, or do nothing if we want to test NIO + if (forceTransport == "native") { + if (osdetector.os == "osx") { + testRuntimeOnly "io.netty:netty-transport-native-kqueue:$nettyVersion$os_suffix" + } else if (osdetector.os == "linux") { + testRuntimeOnly "io.netty:netty-transport-native-epoll:$nettyVersion$os_suffix" + } + } else if (forceTransport == "io_uring" && osdetector.os == "linux") { + testRuntimeOnly "io.netty.incubator:netty-incubator-transport-native-io_uring:$nettyIoUringVersion$os_suffix" + } else if (forceTransport != "nio") { + throw new InvalidUserDataException("invalid -PforceTranport option " + forceTransport + ", should be native|nio") + } + } + + testImplementation project(':reactor-netty-http') + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testImplementation "io.projectreactor:reactor-test:$testAddonVersion" + testImplementation "org.assertj:assertj-core:$assertJVersion" +} + +graalvmNative { + binaries { + test { + if (project.hasProperty("forceTransport")) { + runtimeArgs.add("-DforceTransport=$forceTransport") + } + } + } + metadataRepository { + enabled = true + } +} + +description = "GraalVM smoke tests for the Reactor Netty library" \ No newline at end of file diff --git a/reactor-netty-graalvm-smoke-tests/src/test/java/reactor/netty/http/HttpTests.java b/reactor-netty-graalvm-smoke-tests/src/test/java/reactor/netty/http/HttpTests.java new file mode 100644 index 0000000000..5cb15e3119 --- /dev/null +++ b/reactor-netty-graalvm-smoke-tests/src/test/java/reactor/netty/http/HttpTests.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 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.netty.http; + +import io.netty.channel.epoll.Epoll; +import io.netty.channel.kqueue.KQueue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.netty.DisposableServer; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.server.HttpServer; +import reactor.test.StepVerifier; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +class HttpTests { + DisposableServer disposableServer; + + @AfterEach + void tearDown() { + if (disposableServer != null) { + disposableServer.disposeNow(); + } + } + + @Test + void smokeTest() { + String transport = System.getProperty("forceTransport"); + String osName = System.getProperty("os.name"); + if ("native".equals(transport)) { + if ("Linux".equals(osName)) { + assertThat(Epoll.isAvailable()).isTrue(); + } + else if ("Mac OS X".equals(osName)) { + assertThat(KQueue.isAvailable()).isTrue(); + } + } + else { + assertThat(Epoll.isAvailable()).isFalse(); + assertThat(KQueue.isAvailable()).isFalse(); + } + + disposableServer = + HttpServer.create() + .handle((req, res) -> res.sendString(Mono.just("Hello World!"))) + .bindNow(); + + HttpClient.create() + .port(disposableServer.port()) + .get() + .uri("/") + .responseContent() + .aggregate() + .asString() + .as(StepVerifier::create) + .expectNext("Hello World!") + .expectComplete() + .verify(Duration.ofSeconds(5)); + } +} diff --git a/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/jni-config.json b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/jni-config.json new file mode 100644 index 0000000000..dcc03d69ea --- /dev/null +++ b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/jni-config.json @@ -0,0 +1,126 @@ +[ + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.ChannelException" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.Buffer" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.DatagramSocketAddress", + "methods": [ + { + "name": "", + "parameterTypes": [ + "byte[]", + "int", + "int", + "int", + "io.netty.channel.unix.DatagramSocketAddress" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.DomainDatagramSocketAddress", + "methods": [ + { + "name": "", + "parameterTypes": [ + "byte[]", + "int", + "io.netty.channel.unix.DomainDatagramSocketAddress" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.FileDescriptor" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.LimitsStaticallyReferencedJniMethods" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.Socket" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.DefaultFileRegion", + "fields": [ + { + "name": "file" + }, + { + "name": "transferred" + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.kqueue.BsdSocket" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.kqueue.KQueueEventArray" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.kqueue.Native" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryUtil" + }, + "name": "io.netty.channel.unix.PeerCredentials", + "methods": [ + { + "name": "", + "parameterTypes": [ + "int", + "int", + "int[]" + ] + } + ] + } +] diff --git a/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/reflect-config.json b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 0000000000..a1a6a801ad --- /dev/null +++ b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,42 @@ +[ + { + "condition": { + "typeReachable": "io.netty.channel.socket.nio.NioServerSocketChannel" + }, + "name": "java.nio.channels.spi.SelectorProvider", + "methods": [ + { + "name": "openServerSocketChannel", + "parameterTypes": [ + "java.net.ProtocolFamily" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.socket.nio.NioSocketChannel" + }, + "name": "java.nio.channels.spi.SelectorProvider", + "methods": [ + { + "name": "openSocketChannel", + "parameterTypes": [ + "java.net.ProtocolFamily" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.DefaultFileRegion" + }, + { + "condition": { + "typeReachable": "io.netty.channel.kqueue.Native" + }, + "name": "io.netty.channel.unix.PeerCredentials" + } +] diff --git a/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/resource-config.json b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/resource-config.json new file mode 100644 index 0000000000..5117c744f6 --- /dev/null +++ b/reactor-netty-graalvm-smoke-tests/src/test/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,19 @@ +{ + "resources": { + "includes": [ + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryLoader" + }, + "pattern": "\\QMETA-INF/native/libnetty_transport_native_kqueue_x86_64.jnilib\\E" + }, + { + "condition": { + "typeReachable": "io.netty.util.internal.NativeLibraryLoader" + }, + "pattern": "\\QMETA-INF/native/libnetty_transport_native_kqueue_aarch_64.jnilib\\E" + } + ] + }, + "bundles": [] +} diff --git a/settings.gradle b/settings.gradle index a792c71fc9..b45b2f4f17 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2021 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2023 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. @@ -19,4 +19,5 @@ include 'reactor-netty-http' include 'reactor-netty-http-brave' include 'reactor-netty-incubator-quic' include 'reactor-netty-examples' +include 'reactor-netty-graalvm-smoke-tests' include 'reactor-netty' \ No newline at end of file