From c88ea7efd6046f1311f6f12bd8c07c3d387a7954 Mon Sep 17 00:00:00 2001 From: Federico Rispo Date: Thu, 11 May 2023 12:53:03 +0200 Subject: [PATCH 1/3] ci: build and publish javax flavour The 'release' github action has a new job to build the javax flavour of this project. This job has three main steps: - Replace all the jakarta imports and dependencies with the javax ones - Downgrade springframework dependency to the version 5.* - Using jdk 11 to build the project and publish it The cold migration from jakarta to javax is done via a new bash script `.github/replaceJakartaWithJavax.sh` that uses mainly the sed command to perform the replacement operations. In gradle.properties the SOURCE_COMPATIBILITY and the TARGET_COMPATIBILITY constants are downgraded to 11 in order to allow the users to use the latest version of this library even in projects where the jdk11 is mandatory. Updated the README with a brief explanation of the difference between the two flavours of this project. --- .github/add-module-suffix.sh | 2 +- .github/release.sh | 6 ++-- .github/replaceJakartaWithJavax.sh | 28 ++++++++++++++++++ .github/tag-release.sh | 6 ++-- .github/workflows/release.yml | 47 +++++++++++++++++++++++++++--- README.md | 15 +++++++++- build.gradle | 5 ++++ gradle.properties | 8 +++-- graphql-java-servlet/build.gradle | 16 +++++----- 9 files changed, 111 insertions(+), 22 deletions(-) create mode 100755 .github/replaceJakartaWithJavax.sh diff --git a/.github/add-module-suffix.sh b/.github/add-module-suffix.sh index ccba294c..d7da229e 100755 --- a/.github/add-module-suffix.sh +++ b/.github/add-module-suffix.sh @@ -1,6 +1,6 @@ #!/bin/bash -MODULE_SUFFIX="${GITHUB_REF##*/}" +MODULE_SUFFIX="${1}" addSuffix() { local result diff --git a/.github/release.sh b/.github/release.sh index 5303c90b..31cd0d3f 100755 --- a/.github/release.sh +++ b/.github/release.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ev -BRANCH="${GITHUB_REF##*/}" +FLAVOUR="${1}" removeSnapshots() { sed -i 's/-SNAPSHOT//' gradle.properties @@ -10,8 +10,8 @@ removeSnapshots() { echo "Publishing release to Maven Central" removeSnapshots -if [[ "${BRANCH}" != "master" ]]; then - .github/add-module-suffix.sh +if [ -n "${FLAVOUR}" ]; then + .github/add-module-suffix.sh $FLAVOUR fi ./gradlew clean build publishToSonatype closeAndReleaseSonatypeStagingRepository \ No newline at end of file diff --git a/.github/replaceJakartaWithJavax.sh b/.github/replaceJakartaWithJavax.sh new file mode 100755 index 00000000..e6f8d7ee --- /dev/null +++ b/.github/replaceJakartaWithJavax.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Set jdk11 as source and target +sed -i 's/SOURCE_COMPATIBILITY=.*/SOURCE_COMPATIBILITY=11/' gradle.properties +sed -i 's/TARGET_COMPATIBILITY=.*/TARGET_COMPATIBILITY=11/' gradle.properties + +# Replace jakarta imports and dependencies with javax +grep -rl 'import jakarta' ./graphql-java-servlet | xargs sed -i 's/import jakarta/import javax/g' +sed -i 's/.*jakarta.websocket:jakarta.websocket-client-api.*//' graphql-java-servlet/build.gradle +sed -i \ + 's/jakarta.servlet:jakarta.servlet-api.*/javax.servlet:javax.servlet-api:$LIB_JAVAX_SERVLET"/' \ + graphql-java-servlet/build.gradle +sed -i \ + 's/jakarta.websocket.*/javax.websocket:javax.websocket-api:$LIB_JAVAX_WEBSOCKET"/' \ + graphql-java-servlet/build.gradle + +# Final check if there are something else to replace +grep -rl 'jakarta' ./graphql-java-servlet | xargs sed -i 's/jakarta/javax/g' + +# Set the version 5 for spring framework +sed -i \ + 's/org.springframework:spring-test.*/org.springframework:spring-test:$LIB_SPRINGFRAMEWORK_5"/' \ + graphql-java-servlet/build.gradle +sed -i \ + 's/org.springframework:spring-web.*/org.springframework:spring-web:$LIB_SPRINGFRAMEWORK_5"/' \ + graphql-java-servlet/build.gradle + +echo "Replaced jakarta occurrences with javax" \ No newline at end of file diff --git a/.github/tag-release.sh b/.github/tag-release.sh index d80c1699..4c1101d2 100755 --- a/.github/tag-release.sh +++ b/.github/tag-release.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ev -BRANCH="${GITHUB_REF##*/}" +FLAVOUR="${1}" getVersion() { ./gradlew properties -q | grep -E "^version" | awk '{print $2}' | tr -d '[:space:]' @@ -13,10 +13,10 @@ removeSnapshots() { commitRelease() { local APP_VERSION - if "${BRANCH}" == "master"; then + if [ -z "${FLAVOUR}" ]; then APP_VERSION=$(getVersion) else - APP_VERSION=$(getVersion)-"${BRANCH}" + APP_VERSION=$(getVersion)-"${FLAVOUR}" fi git commit -a -m "Update version for release" git tag -a "v${APP_VERSION}" -m "Tag release version" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1a277dd..15d0a5e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,8 +37,8 @@ jobs: - name: Gradle Check run: ./gradlew --info check - build: - name: Publish release + build-jakarta: + name: Publish release jakarta needs: test runs-on: ubuntu-latest steps: @@ -74,9 +74,48 @@ jobs: OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} run: .github/release.sh + build-javax: + name: Publish release javax + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + + - name: Cache Gradle + uses: actions/cache@v3 + env: + java-version: 11 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-${{ env.java-version }}-gradle- + - name: Prepare environment + env: + GPG_KEY_CONTENTS: ${{ secrets.GPG_KEY_CONTENTS }} + SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.GPG_SIGNING_SECRET_KEY_RING_FILE }} + run: sudo bash -c "echo '$GPG_KEY_CONTENTS' | base64 -d > '$SIGNING_SECRET_KEY_RING_FILE'" + - name: Replace jakarta with javax + run: .github/replaceJakartaWithJavax.sh + - name: Publish release + env: + SIGNING_KEY_ID: ${{ secrets.GPG_SIGNING_KEY_ID }} + SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }} + SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.GPG_SIGNING_SECRET_KEY_RING_FILE }} + OSS_USER_TOKEN_KEY: ${{ secrets.OSS_USER_TOKEN_KEY }} + OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} + run: .github/release.sh javax tag: name: Tag release - needs: build + needs: [ build-jakarta, build-javax ] runs-on: ubuntu-latest steps: - name: Checkout @@ -98,4 +137,4 @@ jobs: restore-keys: | ${{ runner.os }}-${{ env.java-version }}-gradle- - name: Tag release - run: .github/tag-release.sh \ No newline at end of file + run: .github/tag-release.sh diff --git a/README.md b/README.md index 8a4d3bff..7eb41698 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,20 @@ This project wraps the Java implementation of GraphQL provided by [GraphQL Java] See [GraphQL Java documentation](https://www.graphql-java.com/documentation/latest/) for more in depth details regarding GraphQL Java itself. -We try to stay up to date with GraphQL Java as much as possible. See [gradle.properties](gradle.properties) to see currently supported versions. +We try to stay up to date with GraphQL Java as much as possible maintaining the retro-compatibility +with javax and Springframework 5. + +On each release we publish two flavours of this project: + - the main one is using `jakarta` and Springframework `6.*` + - the legacy one is using `javax` and Springframework `5.*` + +On maven central you can distinguish them from the version because the `javax` flavor has the +suffix `-javax`. + +Both of them also supports legacy projects that can compile with older JDK versions: the oldest +supported one is the `11`. + +See [gradle.properties](gradle.properties) to see currently supported versions. ## Installation and getting started diff --git a/build.gradle b/build.gradle index ca0f6f45..9e655f65 100644 --- a/build.gradle +++ b/build.gradle @@ -76,6 +76,11 @@ subprojects { targetCompatibility = TARGET_COMPATIBILITY } + compileTestJava { + sourceCompatibility = 17 + targetCompatibility = 17 + } + compileJava.dependsOn(processResources) test { diff --git a/gradle.properties b/gradle.properties index d482aa8a..d2e89c8b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,5 +11,9 @@ LIB_GRAPHQL_JAVA_VER=20.2 LIB_JACKSON_VER=2.15.0 LIB_SLF4J_VER=2.0.7 LIB_LOMBOK_VER=1.18.26 -SOURCE_COMPATIBILITY=17 -TARGET_COMPATIBILITY=17 +# These constants are necessary to the automatic release of javax flavour +LIB_JAVAX_SERVLET=4.0.1 +LIB_JAVAX_WEBSOCKET=1.1 +LIB_SPRINGFRAMEWORK_5=5.3.25 +SOURCE_COMPATIBILITY=11 +TARGET_COMPATIBILITY=11 diff --git a/graphql-java-servlet/build.gradle b/graphql-java-servlet/build.gradle index 3a118f00..08d734e7 100644 --- a/graphql-java-servlet/build.gradle +++ b/graphql-java-servlet/build.gradle @@ -16,9 +16,9 @@ dependencies { api(project(':graphql-java-kickstart')) // Servlet - compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0' - compileOnly 'jakarta.websocket:jakarta.websocket-api:2.1.0' - compileOnly 'jakarta.websocket:jakarta.websocket-client-api:2.1.0' + compileOnly "jakarta.servlet:jakarta.servlet-api:6.0.0" + compileOnly "jakarta.websocket:jakarta.websocket-api:2.1.0" + compileOnly "jakarta.websocket:jakarta.websocket-client-api:2.1.0" implementation "org.slf4j:slf4j-api:$LIB_SLF4J_VER" // OSGi @@ -37,10 +37,10 @@ dependencies { testRuntimeOnly "cglib:cglib-nodep:3.3.0" testRuntimeOnly "org.objenesis:objenesis:3.3" testImplementation "org.slf4j:slf4j-simple:$LIB_SLF4J_VER" - testImplementation 'org.springframework:spring-test:6.0.9' - testRuntimeOnly 'org.springframework:spring-web:6.0.9' + testImplementation "org.springframework:spring-test:6.0.9" + testRuntimeOnly "org.springframework:spring-web:6.0.9" testImplementation 'com.google.guava:guava:31.1-jre' - testImplementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' - testImplementation 'jakarta.websocket:jakarta.websocket-api:2.1.0' - testImplementation 'jakarta.websocket:jakarta.websocket-client-api:2.1.0' + testImplementation "jakarta.servlet:jakarta.servlet-api:6.0.0" + testImplementation "jakarta.websocket:jakarta.websocket-api:2.1.0" + testImplementation "jakarta.websocket:jakarta.websocket-client-api:2.1.0" } From 74099ef7c3e9cc90057215663e2c3e95b0354111 Mon Sep 17 00:00:00 2001 From: Federico Rispo Date: Sat, 13 May 2023 22:12:54 +0200 Subject: [PATCH 2/3] ci: merge publishing snapshot actions - Refactored bash scripts to accept only javax flavour - Removed snapshot-suffix.yml action - Added test-javax jobs in release action --- ...d-module-suffix.sh => add-javax-suffix.sh} | 12 ++- .github/release.sh | 4 +- .github/tag-release.sh | 8 +- .github/workflows/release.yml | 38 +++++++++- .github/workflows/snapshot-suffix.yml | 74 ------------------ .github/workflows/snapshot.yml | 76 +++++++++++++++++-- 6 files changed, 113 insertions(+), 99 deletions(-) rename .github/{add-module-suffix.sh => add-javax-suffix.sh} (60%) delete mode 100644 .github/workflows/snapshot-suffix.yml diff --git a/.github/add-module-suffix.sh b/.github/add-javax-suffix.sh similarity index 60% rename from .github/add-module-suffix.sh rename to .github/add-javax-suffix.sh index d7da229e..447b060f 100755 --- a/.github/add-module-suffix.sh +++ b/.github/add-javax-suffix.sh @@ -1,7 +1,5 @@ #!/bin/bash -MODULE_SUFFIX="${1}" - addSuffix() { local result result=$(grep include settings.gradle | awk '{print $2}' | tr -d "'" | tr -d ':') @@ -13,11 +11,11 @@ addSuffix() { updateLocalDependencies() { for module in "${modules[@]}"; do - cp -rf "$module" "$module"-"$MODULE_SUFFIX" + cp -rf "$module" "$module"-javax rm -rf "$module" for dependency in "${modules[@]}"; do - sed -i -E "s/project\(('|\"):${dependency}('|\")\)/project\(':${dependency}-${MODULE_SUFFIX}'\)/" "$module"-"$MODULE_SUFFIX"/build.gradle + sed -i -E "s/project\(('|\"):${dependency}('|\")\)/project\(':${dependency}-javax'\)/" "$module"-"javax"/build.gradle done done @@ -26,14 +24,14 @@ updateLocalDependencies() { updateGradleSettings() { for module in "${modules[@]}"; do - echo "Replace ${module} with ${module}-${MODULE_SUFFIX} in settings.gradle" - sed -i -E "s/('|\"):${module}('|\")/':${module}-${MODULE_SUFFIX}'/" settings.gradle + echo "Replace ${module} with ${module}-javax in settings.gradle" + sed -i -E "s/('|\"):${module}('|\")/':${module}-javax'/" settings.gradle done cat settings.gradle } -echo "Add suffix '-$MODULE_SUFFIX' to modules" +echo "Add suffix -javax to modules" addSuffix ls -lh \ No newline at end of file diff --git a/.github/release.sh b/.github/release.sh index 31cd0d3f..008a6d3a 100755 --- a/.github/release.sh +++ b/.github/release.sh @@ -10,8 +10,8 @@ removeSnapshots() { echo "Publishing release to Maven Central" removeSnapshots -if [ -n "${FLAVOUR}" ]; then - .github/add-module-suffix.sh $FLAVOUR +if [ "${FLAVOUR}" == 'javax' ]; then + .github/add-javax-suffix.sh fi ./gradlew clean build publishToSonatype closeAndReleaseSonatypeStagingRepository \ No newline at end of file diff --git a/.github/tag-release.sh b/.github/tag-release.sh index 4c1101d2..a0628629 100755 --- a/.github/tag-release.sh +++ b/.github/tag-release.sh @@ -1,8 +1,6 @@ #!/bin/bash set -ev -FLAVOUR="${1}" - getVersion() { ./gradlew properties -q | grep -E "^version" | awk '{print $2}' | tr -d '[:space:]' } @@ -13,11 +11,7 @@ removeSnapshots() { commitRelease() { local APP_VERSION - if [ -z "${FLAVOUR}" ]; then - APP_VERSION=$(getVersion) - else - APP_VERSION=$(getVersion)-"${FLAVOUR}" - fi + APP_VERSION=$(getVersion) git commit -a -m "Update version for release" git tag -a "v${APP_VERSION}" -m "Tag release version" } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15d0a5e1..386a307a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,8 @@ jobs: - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - test: - name: Test run + test-jakarta: + name: Test run jakarta needs: validation runs-on: ubuntu-latest steps: @@ -39,7 +39,7 @@ jobs: build-jakarta: name: Publish release jakarta - needs: test + needs: test-jakarta runs-on: ubuntu-latest steps: - name: Checkout @@ -74,9 +74,39 @@ jobs: OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} run: .github/release.sh + test-javax: + name: Test run javax + needs: validation + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + - name: Cache Gradle + uses: actions/cache@v3 + env: + java-version: 11 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-${{ env.java-version }}-gradle- + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Replace jakarta with javax + run: .github/replaceJakartaWithJavax.sh + - name: Gradle Check + run: ./gradlew --info check + build-javax: name: Publish release javax - needs: test + needs: test-javax runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/snapshot-suffix.yml b/.github/workflows/snapshot-suffix.yml deleted file mode 100644 index ad9646d7..00000000 --- a/.github/workflows/snapshot-suffix.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: "Publish snapshot with suffix" -on: - push: - branches: - - javax - - jakarta - -jobs: - validation: - name: Gradle Wrapper Validation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 - - test: - name: Test run - needs: validation - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: 11 - - name: Cache Gradle - uses: actions/cache@v3 - env: - java-version: 11 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-${{ env.java-version }}-gradle- - - name: Make gradlew executable - run: chmod +x ./gradlew - - name: Gradle Check - run: ./gradlew --info check - - build: - name: Publish snapshot - needs: test - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: 11 - - name: Cache Gradle - uses: actions/cache@v3 - env: - java-version: 11 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-${{ env.java-version }}-gradle- - - name: Add suffix to modules - run: .github/add-module-suffix.sh - - name: Gradle Publish Snapshot - if: env.OSS_USER_TOKEN_KEY != null - env: - OSS_USER_TOKEN_KEY: ${{ secrets.OSS_USER_TOKEN_KEY }} - OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} - run: ./gradlew clean build publish -x test \ No newline at end of file diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 8993506c..01009342 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -12,8 +12,8 @@ jobs: - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - test: - name: Test run + test-jakarta: + name: Test run jakarta needs: validation runs-on: ubuntu-latest steps: @@ -40,9 +40,9 @@ jobs: - name: Gradle Check run: ./gradlew --info check - build: - name: Publish snapshot - needs: test + build-jakarta: + name: Publish snapshot jakarta + needs: test-jakarta runs-on: ubuntu-latest steps: - name: Checkout @@ -72,6 +72,72 @@ jobs: OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} run: ./gradlew clean build publish -x test + test-javax: + name: Test run javax + needs: validation + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + - name: Cache Gradle + uses: actions/cache@v3 + env: + java-version: 11 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-${{ env.java-version }}-gradle- + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Replace jakarta with javax + run: .github/replaceJakartaWithJavax.sh + - name: Gradle Check + run: ./gradlew --info check + + build-javax: + name: Publish snapshot javax + needs: test-javax + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + - name: Cache Gradle + uses: actions/cache@v3 + env: + java-version: 11 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-${{ env.java-version }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-${{ env.java-version }}-gradle- + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Replace jakarta with javax + run: .github/replaceJakartaWithJavax.sh + - name: Add suffix to modules + run: .github/add-javax-suffix.sh + - name: Gradle Publish Snapshot + if: env.OSS_USER_TOKEN_KEY != null + env: + OSS_USER_TOKEN_KEY: ${{ secrets.OSS_USER_TOKEN_KEY }} + OSS_USER_TOKEN_PASS: ${{ secrets.OSS_USER_TOKEN_PASS }} + run: ./gradlew clean build publish -x test + sonar: name: Sonar analysis needs: validation From 8688d964666d89f569073a886b7e6e2ef992340d Mon Sep 17 00:00:00 2001 From: Federico Rispo Date: Sat, 13 May 2023 23:52:21 +0200 Subject: [PATCH 3/3] chore: use constants for test source and target compatibily --- build.gradle | 4 ++-- gradle.properties | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9e655f65..6e30d917 100644 --- a/build.gradle +++ b/build.gradle @@ -77,8 +77,8 @@ subprojects { } compileTestJava { - sourceCompatibility = 17 - targetCompatibility = 17 + sourceCompatibility = SOURCE_COMPATIBILITY_TEST + targetCompatibility = TARGET_COMPATIBILITY_TEST } compileJava.dependsOn(processResources) diff --git a/gradle.properties b/gradle.properties index d2e89c8b..dce5ce06 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,3 +17,5 @@ LIB_JAVAX_WEBSOCKET=1.1 LIB_SPRINGFRAMEWORK_5=5.3.25 SOURCE_COMPATIBILITY=11 TARGET_COMPATIBILITY=11 +SOURCE_COMPATIBILITY_TEST=17 +TARGET_COMPATIBILITY_TEST=17