diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..6dade98 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,80 @@ +name: Build & test +on: + push: + branches-ignore: [ main ] + pull_request: + branches: [ develop ] + workflow_dispatch: + repository_dispatch: + types: [ utPLSQL-build ] + +defaults: + run: + shell: bash + +jobs: + build: + + runs-on: ubuntu-latest + + services: + oracle: + image: gvenzl/oracle-xe:21-slim + env: + ORACLE_PASSWORD: oracle + ports: + - 1521:1521 + options: >- + --health-cmd healthcheck.sh + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Install utPLSQL + run: sh ${{ github.workspace }}/scripts/1_install_utplsql.sh + + - name: Install demo project + run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh + + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Maven unit and integration tests + run: mvn clean verify sonar:sonar -Pcoverage -Dsonar.projectKey=utPLSQL_utPLSQL-java-api + env: + GITHUB_TOKEN: ${{ secrets.API_TOKEN_GITHUB }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Maven deploy snapshot + run: mvn deploy -DskipTests + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Publish unit test results + uses: EnricoMi/publish-unit-test-result-action@v1.24 + if: always() + with: + files: target/**/TEST**.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9cee803 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +name: Build and deploy release + +on: + push: + branches: [ main ] + +defaults: + run: + shell: bash + +jobs: + build: + + runs-on: ubuntu-latest + + services: + oracle: + image: gvenzl/oracle-xe:21-slim + env: + ORACLE_PASSWORD: oracle + ports: + - 1521:1521 + options: >- + --health-cmd healthcheck.sh + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Install utPLSQL + run: sh ${{ github.workspace }}/scripts/1_install_utplsql.sh + + - name: Install demo project + run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh + + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Maven deploy release + run: mvn clean deploy -Prelease + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Publish unit test results + uses: EnricoMi/publish-unit-test-result-action@v1.24 + if: always() + with: + files: target/**/TEST**.xml diff --git a/.gitignore b/.gitignore index 58e31d2..9ec7c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # IntelliJ *.iml -.idea/ + +.idea +!/.idea/codeStyles +!/.idea/dictionaries # Maven target/ @@ -21,3 +24,4 @@ buildNumber.properties #Exclude CoverageHTMLReporter resources as they are managed by maven src/main/resources/CoverageHTMLReporter/ /gradle.properties + diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..cb28b0e Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c257d25 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bfb554f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,85 +0,0 @@ -sudo: required -language: java - -services: - - docker - -jdk: - - oraclejdk8 - -env: - global: - - DOCKER_CFG=$HOME/.docker - - DOCKER_REPO="utplsqlv3/oracledb" - - CACHE_DIR=$HOME/.cache - - DB_URL="127.0.0.1:1521:XE" - - DB_USER=app - - DB_PASS=app - - ORACLE_VERSION="11g-r2-xe" - - DOCKER_OPTIONS="--shm-size=1g" - - UTPLSQL_FILE="utPLSQL" - matrix: - - UTPLSQL_VERSION="v3.0.0" - UTPLSQL_FILE="utPLSQLv3.0.0" - - UTPLSQL_VERSION="v3.0.1" - - UTPLSQL_VERSION="v3.0.2" - - UTPLSQL_VERSION="v3.0.3" - - UTPLSQL_VERSION="v3.0.4" - - UTPLSQL_VERSION="v3.1.1" - - UTPLSQL_VERSION="v3.1.2" - - UTPLSQL_VERSION="v3.1.3" - - UTPLSQL_VERSION="develop" - UTPLSQL_FILE="utPLSQL" - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $DOCKER_CFG - - $CACHE_DIR - -install: - - bash .travis/start_db.sh - - bash .travis/install_utplsql.sh - - bash .travis/install_demo_project.sh - -before_script: - - echo JAVA_HOME = ${JAVA_HOME} - - echo PATH = ${PATH} - - ls ${JAVA_HOME} - - java -version - - echo $JAVA_OPTS - - echo $GRADLE_OPTS - - echo JAVA_HOME = ${GRADLE_HOME} - -script: - - ./gradlew check - -deploy: - - provider: script - script: ./gradlew uploadArchives - skip_cleanup: true - on: - repository: utPLSQL/utPLSQL-java-api - tags: true - # Use only first job "#xxx.1" to publish artifacts - condition: "${TRAVIS_JOB_NUMBER} =~ \\.1$" - - - provider: script - script: ./gradlew uploadArchives - skip_cleanup: true - on: - repository: utPLSQL/utPLSQL-java-api - branch: develop - # Use only first job "#xxx.1" to publish artifacts - condition: "${TRAVIS_JOB_NUMBER} =~ \\.1$" - -notifications: - slack: - rooms: - - secure: "RTwZxg50LgiDo8/Z0ZGrYP7+gHFXlDjcAlXu7IGne8/O/79B8UVG3KP5j4PHuRtlt86WBflXB/5nhszgwjleEhNVdciuBPTBv3PHtvoYnqEajtoDsBR4fiqpGk4QJREqYo5UwzBVisEtGS2qvhjOVRy5sgfPqdXfKM4Jl6x6EMoPmuEWKPZW80eO1AXD2lhcT35fxKAuEavKDrY9WKB3P8HycySFm2+IOgEvxk7p3qkukd/AMOPW54A52ry5AkwElj7439DV8MGYOHnrK9f5neMGCi6Q8VzUlTf95WbF7yoPWHNOMPt0LFKtnDOEjljwrRDpf8D/TcbLO5Q03kgOcXOB/KJp8WqgViGT8WO963GPBM7JXD4f5h04QYVn9lab8M6nK1PQZVKuzq6qBcjGW06EmczaseKnc5VW0tc/svw0Qhgot1rh3bRMHe9xX1j2wgfNcqeHFkoRX2AtPBtH5tDsWYVY0148wJ3cLXKZf1hxRd7V6gFfE5fVey/rTRVk8eEpNEhudIZCJ/T/ng3DWC271uPne7B/E2jy3jrgQ5p+VfcjC8dSu65Gmu7hWEON8g2cD8YQxCEryqgaCRn5R77FHWi9Gi3a85Kh951qL6mLxMl44VFil4CGdGi0hJpWPaGvSNNbfXx5eNyzHwjjT5fgk0EDOWVyHaO/Ni6jDFM=" - on_success: change - on_failure: always diff --git a/.travis/create_api_user.sh b/.travis/create_api_user.sh deleted file mode 100644 index c898f15..0000000 --- a/.travis/create_api_user.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -ev - -sqlplus -S -L sys/oracle@//127.0.0.1:1521/xe AS SYSDBA < demo_project.sh.tmp < install.sh.tmp < section of your pom.xml. No special plugins or extensions are required. +This is a Maven Library project, you can add on your Java project as a dependency. -```xml - - - utplsql-java-api - - https://packagecloud.io/utplsql/utplsql-java-api/maven2 - - - true - - - true - - - -``` +*Notice: You no longer need to configure an additional repository. The library is available in Maven Central since version 3.1.15.* -To use the java-api library, add this to the `` section of your `pom.xml`. ```xml org.utplsql - java-api - 3.1.2 - compile + utplsql-java-api + 3.1.16 ``` ## Compatibility The latest Java-API is always compatible with all database frameworks of the same major version. -For example API-3.0.4 is compatible with database framework 3.0.0-3.1.2 but not with database framework 2.x. +For example API-3.0.4 is compatible with database framework 3.0.0-3.1.* but not with database framework 2.x. It is although recommended to always use the latest release of the API to build your tools for utPLSQL. @@ -100,7 +76,7 @@ You can also ask it for the database-version. ```java try (Connection conn = DriverManager.getConnection(url)) { CompatiblityProxy proxy = new CompatibilityProxy( conn ); - Version version = proxy.getDatabaseVersion(); + Version version = proxy.getUtPlsqlVersion(); } catch (SQLException e) { e.printStackTrace(); } @@ -113,7 +89,7 @@ It also provides a more generic approach to Reporter-handling. If you request the Reporter-Factory for a Reporter it has no specific implementation for it will just return an instance of a `DefaultReporter` with the given name as SQL-Type, assuming -that it exists in the database. Therefore you can address custom reporters without the need +that it exists in the database. Therefore, you can address custom reporters without the need of a specific java-side implementation. ```java @@ -130,4 +106,4 @@ try (Connection conn = DriverManager.getConnection(url)) { ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) \ No newline at end of file +See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index c61fb21..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,165 +0,0 @@ -import de.undercouch.gradle.tasks.download.Download -import org.gradle.api.tasks.testing.logging.TestExceptionFormat - -val deployerJars by configurations.creating - -group = "org.utplsql" -val mavenArtifactId = "java-api" -version = "3.1.3-SNAPSHOT" - -val coverageResourcesVersion = "1.0.1" -val ojdbcVersion = "12.2.0.1" - -plugins { - `java-library` - `maven-publish` - maven - id("de.undercouch.download") version "3.4.3" -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -// In this section you declare where to find the dependencies of your project -repositories { - maven { - url = uri("https://www.oracle.com/content/secure/maven/content") - credentials { - // you may set this properties using gradle.properties file in the root of the project or in your GRADLE_HOME - username = if (project.hasProperty("ORACLE_OTN_USER")) project.property("ORACLE_OTN_USER") as String? else System.getenv("ORACLE_OTN_USER") - password = if (project.hasProperty("ORACLE_OTN_PASSWORD")) project.property("ORACLE_OTN_PASSWORD") as String? else System.getenv("ORACLE_OTN_PASSWORD") - } - } - mavenCentral() -} - -dependencies { - // This dependency is exported to consumers, that is to say found on their compile classpath. - api("com.google.code.findbugs:jsr305:3.0.2") - - // This dependency is used internally, and not exposed to consumers on their own compile classpath. - implementation("org.slf4j:slf4j-api:1.7.25") - implementation("com.oracle.jdbc:ojdbc8:$ojdbcVersion") { - exclude(group = "com.oracle.jdbc") - } - implementation("com.oracle.jdbc:orai18n:$ojdbcVersion") - - // Use Jupiter test framework - testImplementation("org.junit.jupiter:junit-jupiter:5.4.0") - testImplementation("org.hamcrest:hamcrest:2.1") - deployerJars("io.packagecloud.maven.wagon:maven-packagecloud-wagon:0.0.6") -} - -tasks { - test { - useJUnitPlatform() - exclude("**/*IT.class") - testLogging { - events("passed", "skipped", "failed") - exceptionFormat = TestExceptionFormat.FULL - showStackTraces = true - } - } - - val intTest = create("intTest") { - dependsOn(test) - doFirst { - environment("DB_URL", System.getenv("DB_URL") ?: "localhost:1521/XE") - environment("DB_USER", System.getenv("DB_USER") ?: "app") - environment("DB_PASS", System.getenv("DB_PASS") ?: "app") - } - useJUnitPlatform() - include("**/*IT.class") - testLogging { - events("passed", "skipped", "failed") - exceptionFormat = TestExceptionFormat.FULL - showStackTraces = true - showStandardStreams = true - } - } - - // add integration tests to the whole check - named("check") { - dependsOn(intTest) - } - - val coverageResourcesDirectory = "${project.buildDir}/resources/main/CoverageHTMLReporter" - val coverageResourcesZip = "${project.buildDir}/utPLSQL-coverage-html-$coverageResourcesVersion.zip" - // download Coverage Resources from web - val downloadResources = create("downloadCoverageResources") { - src("https://codeload.github.com/utPLSQL/utPLSQL-coverage-html/zip/$coverageResourcesVersion") - dest(File(coverageResourcesZip)) - overwrite(true) - } - - withType { - dependsOn(downloadResources) - - val properties = project.properties.toMutableMap() - properties.putIfAbsent("travisBuildNumber", System.getenv("TRAVIS_BUILD_NUMBER") ?: "local") - expand(properties) - - doLast { - copy { - // extract assets folder only from downloaded archive - // https://github.com/gradle/gradle/pull/8494 - from(zipTree(coverageResourcesZip)) { - include("*/assets/**") - eachFile { - relativePath = RelativePath(true, *relativePath.segments.drop(2).toTypedArray()) // <2> - } - includeEmptyDirs = false - } - into(coverageResourcesDirectory) - } - } - } - - withType { - dependsOn("generatePomFileForMavenPublication") - manifest { - attributes( - "Built-By" to System.getProperty("user.name"), - "Created-By" to "Gradle ${gradle.gradleVersion}", - "Build-Jdk" to "${System.getProperty("os.name")} ${System.getProperty("os.arch")} ${System.getProperty("os.version")}" - ) - } - into("META-INF/maven/${project.group}/$mavenArtifactId") { - from("$buildDir/publications/maven") - rename(".*", "pom.xml") - } - archiveBaseName.set("java-api") - } - - named("uploadArchives") { - repositories.withGroovyBuilder { - "mavenDeployer" { - setProperty("configuration", deployerJars) - "repository"("url" to "packagecloud+https://packagecloud.io/utPLSQL/utPLSQL-java-api") { - "authentication"("password" to System.getenv("PACKAGECLOUD_TOKEN")) - } - } - } - } -} - -publishing { - publications { - create("maven") { - artifactId = mavenArtifactId - pom { - name.set("utPLSQL-java-api") - url.set("https://github.com/utPLSQL/utPLSQL-java-api") - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - } - from(components["java"]) - } - } -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100755 index f6b961f..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 1b2b07c..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index af6708f..0000000 --- a/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 6d57edc..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8d937f4 --- /dev/null +++ b/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..c4586b5 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..189ef45 --- /dev/null +++ b/pom.xml @@ -0,0 +1,225 @@ + + 4.0.0 + + org.utplsql + utplsql-java-api + 3.1.17-SNAPSHOT + + utPLSQL Java API + Java API for running Unit Tests with utPLSQL v3+. + https://github.com/utPLSQL/utPLSQL-java-api + + + UTF-8 + 1.8 + 1.8 + 5.5.2 + 19.3.0.0 + + utplsql + https://sonarcloud.io + + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Simon Martinelli + utPLSQL.org + http://utplsql.org + simon@martineli.ch + https://martinelli.ch + + + + + scm:git@github.com:utPLSQL/utPLSQL-java-api.git + https://github.com/utPLSQL/utPLSQL-java-api + + + + + org.slf4j + slf4j-api + 1.7.36 + + + com.oracle.database.jdbc + ojdbc8 + 19.3.0.0 + + + com.oracle.database.nls + orai18n + 19.3.0.0 + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.hamcrest + hamcrest + 2.1 + + + org.mockito + mockito-core + 3.0.0 + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${dbUrl} + ${dbUser} + ${dbPass} + + + + + com.amashchenko.maven.plugin + gitflow-maven-plugin + 1.18.0 + + true + + main + + + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + 0.8.7 + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + + + + release + + + release + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + verify + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + diff --git a/scripts/0_start_db.sh b/scripts/0_start_db.sh new file mode 100644 index 0000000..d319407 --- /dev/null +++ b/scripts/0_start_db.sh @@ -0,0 +1 @@ +docker run -d --name ora-utplsql -p 1521:1521 -e ORACLE_PASSWORD=oracle gvenzl/oracle-xe:21-slim diff --git a/scripts/1_install_utplsql.sh b/scripts/1_install_utplsql.sh new file mode 100644 index 0000000..bb6577c --- /dev/null +++ b/scripts/1_install_utplsql.sh @@ -0,0 +1,8 @@ +UTPLSQL_DOWNLOAD_URL=$(curl --silent https://api.github.com/repos/utPLSQL/utPLSQL/releases/latest | awk '/browser_download_url/ { print $2 }' | grep ".zip\"" | sed 's/"//g') + +curl -Lk "${UTPLSQL_DOWNLOAD_URL}" -o utPLSQL.zip + +unzip -q utPLSQL.zip + +docker run --rm -v $(pwd)/utPLSQL:/utPLSQL -w /utPLSQL/source --network host \ + --entrypoint sqlplus truemark/sqlplus:19.8 sys/oracle@//127.0.0.1:1521/XE as sysdba @install_headless.sql UT3 UT3 users diff --git a/scripts/2_install_demo_project.sh b/scripts/2_install_demo_project.sh new file mode 100644 index 0000000..a4089b1 --- /dev/null +++ b/scripts/2_install_demo_project.sh @@ -0,0 +1,11 @@ +docker run --rm -v $(pwd):/work -w /work/ --network host --entrypoint sqlplus truemark/sqlplus:19.8 \ + sys/oracle@//127.0.0.1:1521/XE as sysdba @scripts/sql/create_users.sql + +docker run --rm -v $(pwd):/work -w /work/ --network host --entrypoint sqlplus truemark/sqlplus:19.8 \ + app/pass@//127.0.0.1:1521/XE @scripts/sql/create_app_objects.sql + +docker run --rm -v $(pwd):/work -w /work/ --network host --entrypoint sqlplus truemark/sqlplus:19.8 \ + code_owner/pass@//127.0.0.1:1521/XE @scripts/sql/create_source_owner_objects.sql + +docker run --rm -v $(pwd):/work -w /work/ --network host --entrypoint sqlplus truemark/sqlplus:19.8 \ + tests_owner/pass@//127.0.0.1:1521/XE @scripts/sql/create_tests_owner_objects.sql diff --git a/scripts/sql/create_app_objects.sql b/scripts/sql/create_app_objects.sql new file mode 100644 index 0000000..2ad509f --- /dev/null +++ b/scripts/sql/create_app_objects.sql @@ -0,0 +1,9 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback + +@scripts/sql/simple/scripts/sources/TO_TEST_ME.sql +@scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks +@scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb + +@scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks +@scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb diff --git a/scripts/sql/create_source_owner_objects.sql b/scripts/sql/create_source_owner_objects.sql new file mode 100644 index 0000000..bc10482 --- /dev/null +++ b/scripts/sql/create_source_owner_objects.sql @@ -0,0 +1,6 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback + +@scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql +@scripts/sql/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks +@scripts/sql/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb diff --git a/scripts/sql/create_tests_owner_objects.sql b/scripts/sql/create_tests_owner_objects.sql new file mode 100644 index 0000000..cb244db --- /dev/null +++ b/scripts/sql/create_tests_owner_objects.sql @@ -0,0 +1,8 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback + +create synonym TO_TEST_ME for CODE_OWNER.TO_TEST_ME; +create synonym PKG_TEST_ME for CODE_OWNER.PKG_TEST_ME; + +@scripts/sql/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks +@scripts/sql/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb diff --git a/scripts/sql/create_users.sql b/scripts/sql/create_users.sql new file mode 100644 index 0000000..fe64882 --- /dev/null +++ b/scripts/sql/create_users.sql @@ -0,0 +1,30 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback +set echo off +set verify off + +define UTPLSQL_USER = 'UT3'; +define APP_USER = 'APP'; +define CODE_OWNER = 'CODE_OWNER'; +define TESTS_OWNER = 'TESTS_OWNER'; +define DB_PASS = 'pass'; + +grant execute any procedure to &UTPLSQL_USER; +grant create any procedure to &UTPLSQL_USER; +grant execute on dbms_lob to &UTPLSQL_USER; +grant execute on dbms_sql to &UTPLSQL_USER; +grant execute on dbms_xmlgen to &UTPLSQL_USER; +grant execute on dbms_lock to &UTPLSQL_USER; + +create user &APP_USER identified by &DB_PASS quota unlimited on USERS default tablespace USERS; +grant create session, create procedure, create type, create table, create sequence, create view to &APP_USER; +grant select any dictionary to &APP_USER; + +create user &CODE_OWNER identified by &DB_PASS quota unlimited on USERS default tablespace USERS; +grant create session, create procedure, create type, create table, create sequence, create view to &CODE_OWNER; + +create user &TESTS_OWNER identified by &DB_PASS quota unlimited on USERS default tablespace USERS; +grant create session, create procedure, create type, create table, create sequence, create view, create synonym to &TESTS_OWNER; +grant select any dictionary to &TESTS_OWNER; +grant select any table, delete any table, drop any table to &TESTS_OWNER; +grant execute any procedure to &TESTS_OWNER; diff --git a/scripts/sql/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb b/scripts/sql/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb new file mode 100644 index 0000000..d6846b2 --- /dev/null +++ b/scripts/sql/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb @@ -0,0 +1,27 @@ +CREATE OR REPLACE PACKAGE BODY CODE_OWNER.PKG_TEST_ME IS + -- + -- This + -- + FUNCTION FC_TEST_ME(PPARAM1 IN VARCHAR2) RETURN NUMBER IS + BEGIN + IF PPARAM1 IS NULL THEN + RETURN NULL; + ELSIF PPARAM1 = '1' THEN + RETURN 1; + ELSE + RETURN 0; + END IF; + END FC_TEST_ME; + + PROCEDURE PR_TEST_ME(PSNAME IN VARCHAR2) IS + BEGIN + IF PSNAME IS NULL THEN + NULL; + ELSE + INSERT INTO TO_TEST_ME (SNAME) VALUES (PSNAME); + COMMIT; + END IF; + END PR_TEST_ME; + +END PKG_TEST_ME; +/ diff --git a/scripts/sql/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks b/scripts/sql/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks new file mode 100644 index 0000000..959b122 --- /dev/null +++ b/scripts/sql/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks @@ -0,0 +1,8 @@ +-- +-- This package is used TO demonstrate the utPL/SQL possibilities +-- +CREATE OR REPLACE PACKAGE CODE_OWNER.PKG_TEST_ME AS + FUNCTION FC_TEST_ME(PPARAM1 IN VARCHAR2) RETURN NUMBER; + PROCEDURE PR_TEST_ME(PSNAME IN VARCHAR2); +END PKG_TEST_ME; +/ \ No newline at end of file diff --git a/scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql b/scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql new file mode 100644 index 0000000..b2e90d9 --- /dev/null +++ b/scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql @@ -0,0 +1,8 @@ +-- +-- This is a table used to demonstrate the UNIT test framework. +-- +CREATE TABLE TO_TEST_ME +( + SNAME VARCHAR2(10) +) +/ diff --git a/scripts/sql/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb b/scripts/sql/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb new file mode 100644 index 0000000..1b34f08 --- /dev/null +++ b/scripts/sql/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb @@ -0,0 +1,126 @@ +CREATE OR REPLACE PACKAGE BODY TESTS_OWNER.TEST_PKG_TEST_ME AS + + --------------------------------------------------------------------------- + PROCEDURE SETUP_GLOBAL IS + BEGIN + -- Put here the code which is valid for all tests and that should be + -- executed once. + NULL; + END SETUP_GLOBAL; + + --------------------------------------------------------------------------- + PROCEDURE TEARDOWN_GLOBAL IS + BEGIN + -- Put here the code that should be called only once after all the test + -- have executed + NULL; + END TEARDOWN_GLOBAL; + + --------------------------------------------------------------------------- + PROCEDURE SETUP_TEST IS + BEGIN + -- Nothing to clean up globally + NULL; + END SETUP_TEST; + + PROCEDURE TEARDOWN_TEST IS + BEGIN + -- Nothing to clean up globally + NULL; + END TEARDOWN_TEST; + + PROCEDURE TEST_FC_INPUT_1 IS + BEGIN + -- Ok this is a real test where I check that the function return 1 + -- when called with a '1' parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME('1')).TO_EQUAL(1); + END; + + PROCEDURE SETUP_TEST_FC_INPUT_1 IS + BEGIN + -- Nothing to be done really + NULL; + END; + + PROCEDURE TEARDOWN_TEST_FC_INPUT_1 IS + BEGIN + -- Nothing to be done really + NULL; + END; + + PROCEDURE TEST_FC_INPUT_0 IS + BEGIN + -- Ok this is a real test where I check that the function return 0 + -- when called with a '0' parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME('0')).TO_EQUAL(0); + END; + + PROCEDURE TEST_FC_INPUT_NULL IS + BEGIN + -- Ok I check that the function return NULL + -- when called with a NULL parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME(NULL)).TO_BE_NULL; + END TEST_FC_INPUT_NULL; + + PROCEDURE TEST_PR_TEST_ME_NULL IS + VNCOUNT1 PLS_INTEGER; + VNCOUNT2 PLS_INTEGER; + BEGIN + -- In this example I check that the procedure does + -- not insert anything when passing it a NULL parameter + SELECT COUNT(1) INTO VNCOUNT1 FROM TO_TEST_ME; + PKG_TEST_ME.PR_TEST_ME(NULL); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME; + UT.EXPECT(VNCOUNT1).TO_EQUAL(VNCOUNT2); + END; + + PROCEDURE TEST_PR_TEST_ME_NOT_NULL IS + VNCOUNT1 PLS_INTEGER; + VNCOUNT2 PLS_INTEGER; + VSNAME TO_TEST_ME.SNAME%TYPE; + BEGIN + -- In this test I will check that I do insert a value + -- when the parameter is not null. I futher check that + -- the procedure has inserted the value I specified. + SELECT COUNT(1) INTO VNCOUNT1 FROM TO_TEST_ME; + VSNAME := TO_CHAR(VNCOUNT1); + PKG_TEST_ME.PR_TEST_ME(VSNAME); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME; + + -- Check that I have inserted the value + UT.EXPECT(VNCOUNT1 + 1).TO_EQUAL(VNCOUNT2); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME T WHERE T.SNAME = VSNAME; + + -- Check that I inserted the one I said I would insert + UT.EXPECT(VNCOUNT2).TO_EQUAL(1); + DELETE FROM TO_TEST_ME T WHERE T.SNAME = VSNAME; + COMMIT; + END; + + PROCEDURE TEST_PR_TEST_ME_EXISTS IS + BEGIN + -- In case the value exists the procedure should fail with an exception. + BEGIN + PKG_TEST_ME.PR_TEST_ME('EXISTS'); + PKG_TEST_ME.PR_TEST_ME('EXISTS'); + EXCEPTION + WHEN OTHERS THEN + UT.FAIL('Unexpected exception raised'); + END; + END; + + PROCEDURE TEST_PR_TEST_ME_CURSOR IS + TYPE REF_CURSOR IS REF CURSOR; + VEXPECTED REF_CURSOR; + VACTUAL REF_CURSOR; + BEGIN + EXECUTE IMMEDIATE 'TRUNCATE TABLE CODE_OWNER.TO_TEST_ME'; + OPEN VEXPECTED FOR + SELECT T.SNAME FROM TO_TEST_ME T; + OPEN VACTUAL FOR + SELECT T.SNAME FROM TO_TEST_ME T; + UT.EXPECT(VEXPECTED).TO_(EQUAL(VACTUAL)); + END; + +END; +/ diff --git a/scripts/sql/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks b/scripts/sql/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks new file mode 100644 index 0000000..b0cdf54 --- /dev/null +++ b/scripts/sql/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks @@ -0,0 +1,86 @@ +CREATE OR REPLACE PACKAGE TESTS_OWNER.TEST_PKG_TEST_ME AS + -- %suite(TEST_PKG_TEST_ME) + -- %suitepath(plsql.examples) + -- + -- This package shows all the possibilities to unit test + -- your PL/SQL package. NOTE that it is not limited to + -- testing your package. You can do that on all your + -- procedure/functions... + -- + + /** + * This two parameters are used by the test framework in + * order to identify the test suite to run + */ + + /* + * This method is invoked once before any other method. + * It should contain all the setup code that is relevant + * for all your test. It might be inserting a register, + * creating a type, etc... + */ + -- %beforeall + PROCEDURE SETUP_GLOBAL; + + /* + * This method is invoked once after all other method. + * It can be used to clean up all the resources that + * you created in your script + */ + -- %afterall + PROCEDURE TEARDOWN_GLOBAL; + + /* + * This method is called once before each test. + */ + -- %beforeeach + PROCEDURE SETUP_TEST; + + /* + * This method is called once after each test. + */ + -- %aftereach + PROCEDURE TEARDOWN_TEST; + + /** + * This is a real test. The main test can declare a setup + * and teardown method in order to setup and cleanup things + * for that specific test. + */ + -- %test + -- %displayname(Checking if function ('1') returns 1) + -- %beforetest(SETUP_TEST_FC_INPUT_1) + -- %aftertest(TEARDOWN_TEST_FC_INPUT_1) + PROCEDURE TEST_FC_INPUT_1; + PROCEDURE SETUP_TEST_FC_INPUT_1; + PROCEDURE TEARDOWN_TEST_FC_INPUT_1; + + -- %test + -- %displayname(Checking if function ('0') returns 0) + PROCEDURE TEST_FC_INPUT_0; + + -- %test + -- %displayname(Checking if function (NULL) returns NULL) + PROCEDURE TEST_FC_INPUT_NULL; + + -- %test + -- %displayname(Checking if procedure (NULL) insert) + PROCEDURE TEST_PR_TEST_ME_NULL; + + -- %test + -- %displayname(Checking if procedure (NOT NULL) insert) + -- %rollback(manual) + PROCEDURE TEST_PR_TEST_ME_NOT_NULL; + + -- %test + -- %displayname(Checking if procedure (NOT NULL) insert while existing) + -- %rollback(manual) + PROCEDURE TEST_PR_TEST_ME_EXISTS; + + -- %test + -- %displayname(Demonstrating the use of cursor) + -- %rollback(manual) + PROCEDURE TEST_PR_TEST_ME_CURSOR; + +END; +/ diff --git a/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb new file mode 100644 index 0000000..331959d --- /dev/null +++ b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb @@ -0,0 +1,27 @@ +CREATE OR REPLACE PACKAGE BODY PKG_TEST_ME IS + -- + -- This + -- + FUNCTION FC_TEST_ME(PPARAM1 IN VARCHAR2) RETURN NUMBER IS + BEGIN + IF PPARAM1 IS NULL THEN + RETURN NULL; + ELSIF PPARAM1 = '1' THEN + RETURN 1; + ELSE + RETURN 0; + END IF; + END FC_TEST_ME; + + PROCEDURE PR_TEST_ME(PSNAME IN VARCHAR2) IS + BEGIN + IF PSNAME IS NULL THEN + NULL; + ELSE + INSERT INTO TO_TEST_ME (SNAME) VALUES (PSNAME); + COMMIT; + END IF; + END PR_TEST_ME; + +END PKG_TEST_ME; +/ diff --git a/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks new file mode 100644 index 0000000..26b2589 --- /dev/null +++ b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks @@ -0,0 +1,8 @@ +-- +-- This package is used TO demonstrate the utPL/SQL possibilities +-- +CREATE OR REPLACE PACKAGE PKG_TEST_ME AS + FUNCTION FC_TEST_ME(PPARAM1 IN VARCHAR2) RETURN NUMBER; + PROCEDURE PR_TEST_ME(PSNAME IN VARCHAR2); +END PKG_TEST_ME; +/ \ No newline at end of file diff --git a/scripts/sql/simple/scripts/sources/TO_TEST_ME.sql b/scripts/sql/simple/scripts/sources/TO_TEST_ME.sql new file mode 100644 index 0000000..b2e90d9 --- /dev/null +++ b/scripts/sql/simple/scripts/sources/TO_TEST_ME.sql @@ -0,0 +1,8 @@ +-- +-- This is a table used to demonstrate the UNIT test framework. +-- +CREATE TABLE TO_TEST_ME +( + SNAME VARCHAR2(10) +) +/ diff --git a/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb new file mode 100644 index 0000000..115bc15 --- /dev/null +++ b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb @@ -0,0 +1,126 @@ +CREATE OR REPLACE PACKAGE BODY TEST_PKG_TEST_ME AS + + --------------------------------------------------------------------------- + PROCEDURE SETUP_GLOBAL IS + BEGIN + -- Put here the code which is valid for all tests and that should be + -- executed once. + NULL; + END SETUP_GLOBAL; + + --------------------------------------------------------------------------- + PROCEDURE TEARDOWN_GLOBAL IS + BEGIN + -- Put here the code that should be called only once after all the test + -- have executed + NULL; + END TEARDOWN_GLOBAL; + + --------------------------------------------------------------------------- + PROCEDURE SETUP_TEST IS + BEGIN + -- Nothing to clean up globally + NULL; + END SETUP_TEST; + + PROCEDURE TEARDOWN_TEST IS + BEGIN + -- Nothing to clean up globally + NULL; + END TEARDOWN_TEST; + + PROCEDURE TEST_FC_INPUT_1 IS + BEGIN + -- Ok this is a real test where I check that the function return 1 + -- when called with a '1' parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME('1')).TO_EQUAL(1); + END; + + PROCEDURE SETUP_TEST_FC_INPUT_1 IS + BEGIN + -- Nothing to be done really + NULL; + END; + + PROCEDURE TEARDOWN_TEST_FC_INPUT_1 IS + BEGIN + -- Nothing to be done really + NULL; + END; + + PROCEDURE TEST_FC_INPUT_0 IS + BEGIN + -- Ok this is a real test where I check that the function return 0 + -- when called with a '0' parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME('0')).TO_EQUAL(0); + END; + + PROCEDURE TEST_FC_INPUT_NULL IS + BEGIN + -- Ok I check that the function return NULL + -- when called with a NULL parameter + UT.EXPECT(PKG_TEST_ME.FC_TEST_ME(NULL)).TO_BE_NULL; + END TEST_FC_INPUT_NULL; + + PROCEDURE TEST_PR_TEST_ME_NULL IS + VNCOUNT1 PLS_INTEGER; + VNCOUNT2 PLS_INTEGER; + BEGIN + -- In this example I check that the procedure does + -- not insert anything when passing it a NULL parameter + SELECT COUNT(1) INTO VNCOUNT1 FROM TO_TEST_ME; + PKG_TEST_ME.PR_TEST_ME(NULL); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME; + UT.EXPECT(VNCOUNT1).TO_EQUAL(VNCOUNT2); + END; + + PROCEDURE TEST_PR_TEST_ME_NOT_NULL IS + VNCOUNT1 PLS_INTEGER; + VNCOUNT2 PLS_INTEGER; + VSNAME TO_TEST_ME.SNAME%TYPE; + BEGIN + -- In this test I will check that I do insert a value + -- when the parameter is not null. I futher check that + -- the procedure has inserted the value I specified. + SELECT COUNT(1) INTO VNCOUNT1 FROM TO_TEST_ME; + VSNAME := TO_CHAR(VNCOUNT1); + PKG_TEST_ME.PR_TEST_ME(VSNAME); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME; + + -- Check that I have inserted the value + UT.EXPECT(VNCOUNT1 + 1).TO_EQUAL(VNCOUNT2); + SELECT COUNT(1) INTO VNCOUNT2 FROM TO_TEST_ME T WHERE T.SNAME = VSNAME; + + -- Check that I inserted the one I said I would insert + UT.EXPECT(VNCOUNT2).TO_EQUAL(1); + DELETE FROM TO_TEST_ME T WHERE T.SNAME = VSNAME; + COMMIT; + END; + + PROCEDURE TEST_PR_TEST_ME_EXISTS IS + BEGIN + -- In case the value exists the procedure should fail with an exception. + BEGIN + PKG_TEST_ME.PR_TEST_ME('EXISTS'); + PKG_TEST_ME.PR_TEST_ME('EXISTS'); + EXCEPTION + WHEN OTHERS THEN + UT.FAIL('Unexpected exception raised'); + END; + END; + + PROCEDURE TEST_PR_TEST_ME_CURSOR IS + TYPE REF_CURSOR IS REF CURSOR; + VEXPECTED REF_CURSOR; + VACTUAL REF_CURSOR; + BEGIN + EXECUTE IMMEDIATE 'TRUNCATE TABLE TO_TEST_ME'; + OPEN VEXPECTED FOR + SELECT T.SNAME FROM TO_TEST_ME T; + OPEN VACTUAL FOR + SELECT T.SNAME FROM TO_TEST_ME T; + UT.EXPECT(VEXPECTED).TO_(EQUAL(VACTUAL)); + END; + +END; +/ diff --git a/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks new file mode 100644 index 0000000..8a2b852 --- /dev/null +++ b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks @@ -0,0 +1,88 @@ +CREATE OR REPLACE PACKAGE TEST_PKG_TEST_ME AS + -- %suite(TEST_PKG_TEST_ME) + -- %suitepath(plsql.examples) + -- + -- This package shows all the possibilities to unit test + -- your PL/SQL package. NOTE that it is not limited to + -- testing your package. You can do that on all your + -- procedure/functions... + -- + + /** + * This two parameters are used by the test framework in + * order to identify the test suite to run + */ + + /* + * This method is invoked once before any other method. + * It should contain all the setup code that is relevant + * for all your test. It might be inserting a register, + * creating a type, etc... + */ + -- %beforeall + PROCEDURE SETUP_GLOBAL; + + /* + * This method is invoked once after all other method. + * It can be used to clean up all the resources that + * you created in your script + */ + -- %afterall + PROCEDURE TEARDOWN_GLOBAL; + + /* + * This method is called once before each test. + */ + -- %beforeeach + PROCEDURE SETUP_TEST; + + /* + * This method is called once after each test. + */ + -- %aftereach + PROCEDURE TEARDOWN_TEST; + + /** + * This is a real test. The main test can declare a setup + * and teardown method in order to setup and cleanup things + * for that specific test. + */ + -- %test + -- %displayname(Checking if function ('1') returns 1) + -- %beforetest(SETUP_TEST_FC_INPUT_1) + -- %aftertest(TEARDOWN_TEST_FC_INPUT_1) + PROCEDURE TEST_FC_INPUT_1; + PROCEDURE SETUP_TEST_FC_INPUT_1; + PROCEDURE TEARDOWN_TEST_FC_INPUT_1; + + -- %test + -- %displayname(Checking if function ('0') returns 0) + PROCEDURE TEST_FC_INPUT_0; + + -- %test + -- %displayname(Checking if function (NULL) returns NULL) + PROCEDURE TEST_FC_INPUT_NULL; + + -- %test + -- %displayname(Checking if procedure (NULL) insert) + PROCEDURE TEST_PR_TEST_ME_NULL; + + -- %test + -- %displayname(Checking if procedure (NOT NULL) insert) + -- %rollback(manual) + PROCEDURE TEST_PR_TEST_ME_NOT_NULL; + + -- %test + -- %displayname(Checking if procedure (NOT NULL) insert while existing) + -- %rollback(manual) + -- %tags(exists) + PROCEDURE TEST_PR_TEST_ME_EXISTS; + + -- %test + -- %displayname(Demonstrating the use of cursor) + -- %rollback(manual) + -- %tags(cursor) + PROCEDURE TEST_PR_TEST_ME_CURSOR; + +END; +/ diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 2fd8241..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,2 +0,0 @@ - -rootProject.name = "utPLSQL-java-api" diff --git a/src/main/java/org/utplsql/api/CustomTypes.java b/src/main/java/org/utplsql/api/CustomTypes.java index 2d770ce..1c04c23 100644 --- a/src/main/java/org/utplsql/api/CustomTypes.java +++ b/src/main/java/org/utplsql/api/CustomTypes.java @@ -17,6 +17,7 @@ public final class CustomTypes { public static final String UT_KEY_VALUE_PAIR = "UT_KEY_VALUE_PAIR"; public static final String UT_KEY_VALUE_PAIRS = "UT_KEY_VALUE_PAIRS"; - private CustomTypes() {} + private CustomTypes() { + } } diff --git a/src/main/java/org/utplsql/api/DBHelper.java b/src/main/java/org/utplsql/api/DBHelper.java index c9dfa52..09c109c 100644 --- a/src/main/java/org/utplsql/api/DBHelper.java +++ b/src/main/java/org/utplsql/api/DBHelper.java @@ -14,10 +14,12 @@ */ public final class DBHelper { - private DBHelper() {} + private DBHelper() { + } /** * Return a new sys_guid from database. + * * @param conn the connection * @return the new id string * @throws SQLException any database error @@ -47,7 +49,8 @@ public static String getCurrentSchema(Connection conn) throws SQLException { } } - /** Returns the Frameworks version string of the given connection + /** + * Returns the Frameworks version string of the given connection * Deprecated. Use DatabaseInformation-Interface instead. * * @param conn Active db connection @@ -55,13 +58,14 @@ public static String getCurrentSchema(Connection conn) throws SQLException { * @throws SQLException any database error */ @Deprecated - public static Version getDatabaseFrameworkVersion( Connection conn ) throws SQLException { + public static Version getDatabaseFrameworkVersion(Connection conn) throws SQLException { DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); return databaseInformation.getUtPlsqlFrameworkVersion(conn); } - /** Returns the Oracle database Version from a given connection object + /** + * Returns the Oracle database Version from a given connection object * Deprecated. Use DatabaseInformation-Interface instead. * * @param conn Connection-Object @@ -69,13 +73,14 @@ public static Version getDatabaseFrameworkVersion( Connection conn ) throws SQLE * @throws SQLException any database error */ @Deprecated - public static String getOracleDatabaseVersion( Connection conn ) throws SQLException { + public static String getOracleDatabaseVersion(Connection conn) throws SQLException { DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); return databaseInformation.getOracleVersion(conn); } /** * Enable the dbms_output buffer with unlimited size. + * * @param conn the connection */ public static void enableDBMSOutput(Connection conn) { @@ -88,6 +93,7 @@ public static void enableDBMSOutput(Connection conn) { /** * Disable the dbms_output buffer. + * * @param conn the connection */ public static void disableDBMSOutput(Connection conn) { diff --git a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java index 9552fee..90ca49f 100644 --- a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java +++ b/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java @@ -20,7 +20,8 @@ */ public class EnvironmentVariableUtil { - private EnvironmentVariableUtil() {} + private EnvironmentVariableUtil() { + } /** * Returns the value for a given key from environment (see class description) diff --git a/src/main/java/org/utplsql/api/FileMapper.java b/src/main/java/org/utplsql/api/FileMapper.java deleted file mode 100644 index 8fae53f..0000000 --- a/src/main/java/org/utplsql/api/FileMapper.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.utplsql.api; - - -import oracle.jdbc.OracleConnection; -import oracle.jdbc.OracleTypes; - -import java.sql.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public final class FileMapper { - - private FileMapper() {} - - /** - * Call the database api to build the custom file mappings. - */ - public static Array buildFileMappingArray( - Connection conn, FileMapperOptions mapperOptions) throws SQLException { - OracleConnection oraConn = conn.unwrap(OracleConnection.class); - - Map> typeMap = conn.getTypeMap(); - typeMap.put(CustomTypes.UT_FILE_MAPPING, FileMapping.class); - typeMap.put(CustomTypes.UT_KEY_VALUE_PAIR, KeyValuePair.class); - conn.setTypeMap(typeMap); - - CallableStatement callableStatement = conn.prepareCall( - "BEGIN " + - "? := ut_file_mapper.build_file_mappings(" + - "a_object_owner => ?, " + - "a_file_paths => ?, " + - "a_file_to_object_type_mapping => ?, " + - "a_regex_pattern => ?, " + - "a_object_owner_subexpression => ?, " + - "a_object_name_subexpression => ?, " + - "a_object_type_subexpression => ?); " + - "END;"); - - int paramIdx = 0; - callableStatement.registerOutParameter(++paramIdx, OracleTypes.ARRAY, CustomTypes.UT_FILE_MAPPINGS); - - if (mapperOptions.getObjectOwner() == null) { - callableStatement.setNull(++paramIdx, Types.VARCHAR); - } else { - callableStatement.setString(++paramIdx, mapperOptions.getObjectOwner()); - } - - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_VARCHAR2_LIST, mapperOptions.getFilePaths().toArray())); - - if (mapperOptions.getTypeMappings() == null) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_KEY_VALUE_PAIRS); - } else { - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_KEY_VALUE_PAIRS, mapperOptions.getTypeMappings().toArray())); - } - - if (mapperOptions.getRegexPattern() == null) { - callableStatement.setNull(++paramIdx, Types.VARCHAR); - } else { - callableStatement.setString(++paramIdx, mapperOptions.getRegexPattern()); - } - - if (mapperOptions.getOwnerSubExpression() == null) { - callableStatement.setNull(++paramIdx, Types.INTEGER); - } else { - callableStatement.setInt(++paramIdx, mapperOptions.getOwnerSubExpression()); - } - - if (mapperOptions.getNameSubExpression() == null) { - callableStatement.setNull(++paramIdx, Types.INTEGER); - } else { - callableStatement.setInt(++paramIdx, mapperOptions.getNameSubExpression()); - } - - if (mapperOptions.getTypeSubExpression() == null) { - callableStatement.setNull(++paramIdx, Types.INTEGER); - } else { - callableStatement.setInt(++paramIdx, mapperOptions.getTypeSubExpression()); - } - - callableStatement.execute(); - return callableStatement.getArray(1); - } - - public static List buildFileMappingList( - Connection conn, FileMapperOptions mapperOptions) throws SQLException { - java.sql.Array fileMappings = buildFileMappingArray(conn, mapperOptions); - - List mappingList = new ArrayList<>(); - for (Object obj : (Object[]) fileMappings.getArray()) { - mappingList.add((FileMapping) obj); - } - - return mappingList; - } - -} diff --git a/src/main/java/org/utplsql/api/FileMapping.java b/src/main/java/org/utplsql/api/FileMapping.java index e7fea84..be86e2a 100644 --- a/src/main/java/org/utplsql/api/FileMapping.java +++ b/src/main/java/org/utplsql/api/FileMapping.java @@ -15,7 +15,8 @@ public class FileMapping implements SQLData { private String objectName; private String objectType; - public FileMapping() {} + public FileMapping() { + } public FileMapping(String fileName, String objectOwner, String objectName, String objectType) { this.fileName = fileName; diff --git a/src/main/java/org/utplsql/api/JavaApiVersionInfo.java b/src/main/java/org/utplsql/api/JavaApiVersionInfo.java index 03171e4..48e529b 100644 --- a/src/main/java/org/utplsql/api/JavaApiVersionInfo.java +++ b/src/main/java/org/utplsql/api/JavaApiVersionInfo.java @@ -5,33 +5,39 @@ import java.io.InputStream; import java.io.InputStreamReader; -/** This class is getting updated automatically by the build process. - * Please do not update its constants manually cause they will be overwritten. +/** + * This class is getting updated automatically by the build process. + * Please do not update its constants manually because they will be overwritten. * * @author pesse */ public class JavaApiVersionInfo { - private JavaApiVersionInfo() { } - - private static final String MAVEN_PROJECT_NAME = "utPLSQL-java-api"; private static String MAVEN_PROJECT_VERSION = "unknown"; static { try { - - try ( InputStream in = JavaApiVersionInfo.class.getClassLoader().getResourceAsStream("utplsql-api.version"); - BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { - MAVEN_PROJECT_VERSION = reader.readLine(); + try (InputStream in = JavaApiVersionInfo.class.getClassLoader().getResourceAsStream("utplsql-api.version")) { + assert in != null; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { + MAVEN_PROJECT_VERSION = reader.readLine(); + } } - } - catch ( IOException e ) { + } catch (IOException e) { System.out.println("WARNING: Could not get Version information!"); } } - public static String getVersion() { return MAVEN_PROJECT_VERSION; } - public static String getInfo() { return MAVEN_PROJECT_NAME + " " + getVersion(); } + private JavaApiVersionInfo() { + } + + public static String getVersion() { + return MAVEN_PROJECT_VERSION; + } + + public static String getInfo() { + return MAVEN_PROJECT_NAME + " " + getVersion(); + } } diff --git a/src/main/java/org/utplsql/api/ResourceUtil.java b/src/main/java/org/utplsql/api/ResourceUtil.java index e8bbbc4..d4ac0b3 100644 --- a/src/main/java/org/utplsql/api/ResourceUtil.java +++ b/src/main/java/org/utplsql/api/ResourceUtil.java @@ -1,18 +1,11 @@ package org.utplsql.api; -import com.sun.nio.zipfs.ZipPath; -import org.utplsql.api.reporter.CoverageHTMLReporter; - import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.*; -import java.util.ArrayList; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; /** * Helper class for dealing with Resources @@ -21,65 +14,52 @@ */ public class ResourceUtil { - private ResourceUtil() {} + private ResourceUtil() { + } /** - * Returns the Path to a resource so it is walkable no matter if it's inside a jar or on the file system + * Copy directory from a jar file to the destination folder * - * @param resourceName The name of the resource - * @return Path to the resource, either in JAR or on file system - * @throws IOException - * @throws URISyntaxException + * @param resourceAsPath The resource to get children from + * @param targetDirectory If set to true it will only return files, not directories */ - public static Path getPathToResource(String resourceName) throws IOException, URISyntaxException { - URI uri = CoverageHTMLReporter.class.getResource(resourceName).toURI(); - Path myPath; - if (uri.getScheme().equalsIgnoreCase("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()); - myPath = fileSystem.getPath(resourceName); - } else { - myPath = Paths.get(uri); + public static void copyResources(Path resourceAsPath, Path targetDirectory) { + try { + String resourceName = "/" + resourceAsPath; + Files.createDirectories(targetDirectory); + URI uri = ResourceUtil.class.getResource(resourceName).toURI(); + Path myPath; + if (uri.getScheme().equalsIgnoreCase("jar")) { + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + myPath = fileSystem.getPath(resourceName); + copyRecursive(myPath, targetDirectory); + } + } else { + myPath = Paths.get(uri); + copyRecursive(myPath, targetDirectory); + } + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); } - - return myPath; } - /** - * Returns the relative paths of all children of the given resource. Relative path begins from the first atom of the given path. - * - * @param resourceAsPath The resource to get children from - * @param filesOnly If set to true it will only return files, not directories - * @return List of relative Paths to the children - * @throws IOException - * @throws URISyntaxException - */ - public static List getListOfChildren(Path resourceAsPath, boolean filesOnly) throws IOException, URISyntaxException { - - Path resourcePath = getPathToResource("/" + resourceAsPath.toString()); - int relativeStartIndex = resourcePath.getNameCount() - resourceAsPath.getNameCount(); - - final List result = new ArrayList<>(); - - if (resourcePath instanceof ZipPath) { - try (ZipFile zf = new ZipFile(resourcePath.getFileSystem().toString())) { - - for (Enumeration list = zf.entries(); list.hasMoreElements(); ) { - ZipEntry entry = (ZipEntry) list.nextElement(); - // Get entry-path with root element so we can compare it - Path entryPath = resourcePath.getRoot().resolve(resourcePath.getFileSystem().getPath(entry.toString())); + private static void copyRecursive(Path from, Path targetDirectory) throws IOException { + Files.walkFileTree(from, new SimpleFileVisitor() { - if (entryPath.startsWith(resourcePath) && (!filesOnly || !entry.isDirectory())) - result.add(entryPath.subpath(relativeStartIndex, entryPath.getNameCount())); - } + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + super.preVisitDirectory(dir, attrs); + Path currentTarget = targetDirectory.resolve(from.relativize(dir).toString()); + Files.createDirectories(currentTarget); + return FileVisitResult.CONTINUE; } - resourcePath.getFileSystem().close(); - } else { - Files.walk(resourcePath) - .filter(p -> !filesOnly || p.toFile().isFile()) - .forEach(p -> result.add(p.subpath(relativeStartIndex, p.getNameCount()))); - - } - return result; + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + super.visitFile(file, attrs); + Files.copy(file, targetDirectory.resolve(from.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + }); } -} \ No newline at end of file +} diff --git a/src/main/java/org/utplsql/api/TestRunner.java b/src/main/java/org/utplsql/api/TestRunner.java index b56b772..3a3f6e1 100644 --- a/src/main/java/org/utplsql/api/TestRunner.java +++ b/src/main/java/org/utplsql/api/TestRunner.java @@ -5,6 +5,7 @@ import org.utplsql.api.compatibility.CompatibilityProxy; import org.utplsql.api.db.DatabaseInformation; import org.utplsql.api.db.DefaultDatabaseInformation; +import org.utplsql.api.exception.OracleCreateStatmenetStuckException; import org.utplsql.api.exception.SomeTestsFailedException; import org.utplsql.api.exception.UtPLSQLNotInstalledException; import org.utplsql.api.reporter.DocumentationReporter; @@ -15,7 +16,9 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.concurrent.*; /** * Created by Vinicius Avellar on 12/04/2017. @@ -28,9 +31,9 @@ public class TestRunner { private static final Logger logger = LoggerFactory.getLogger(TestRunner.class); private final TestRunnerOptions options = new TestRunnerOptions(); + private final List reporterNames = new ArrayList<>(); private CompatibilityProxy compatibilityProxy; private ReporterFactory reporterFactory; - private final List reporterNames = new ArrayList<>(); public TestRunner addPath(String path) { options.pathList.add(path); @@ -47,11 +50,12 @@ public TestRunner addReporter(Reporter reporter) { return this; } - public TestRunner addReporter( String reporterName ) { - if ( reporterFactory != null ) + public TestRunner addReporter(String reporterName) { + if (reporterFactory != null) { options.reporterList.add(reporterFactory.createReporter(reporterName)); - else + } else { reporterNames.add(reporterName); + } return this; } @@ -70,6 +74,11 @@ public TestRunner addCoverageScheme(String coverageScheme) { return this; } + public TestRunner addCoverageSchemes(Collection schemaNames) { + this.options.coverageSchemes.addAll(schemaNames); + return this; + } + public TestRunner includeObject(String obj) { options.includeObjects.add(obj); return this; @@ -90,6 +99,26 @@ public TestRunner excludeObjects(List obj) { return this; } + public TestRunner includeSchemaExpr(String expr) { + options.includeSchemaExpr = expr; + return this; + } + + public TestRunner excludeSchemaExpr(String expr) { + options.excludeSchemaExpr = expr; + return this; + } + + public TestRunner includeObjectExpr(String expr) { + options.includeObjectExpr = expr; + return this; + } + + public TestRunner excludeObjectExpr(String expr) { + options.excludeObjectExpr = expr; + return this; + } + public TestRunner sourceMappingOptions(FileMapperOptions mapperOptions) { options.sourceMappingOptions = mapperOptions; return this; @@ -105,22 +134,72 @@ public TestRunner failOnErrors(boolean failOnErrors) { return this; } - public TestRunner skipCompatibilityCheck( boolean skipCompatibilityCheck ) - { + public TestRunner skipCompatibilityCheck(boolean skipCompatibilityCheck) { options.skipCompatibilityCheck = skipCompatibilityCheck; return this; } - public TestRunner setReporterFactory( ReporterFactory reporterFactory ) { + public TestRunner setReporterFactory(ReporterFactory reporterFactory) { this.reporterFactory = reporterFactory; return this; } + public TestRunner randomTestOrder(boolean randomTestOrder) { + this.options.randomTestOrder = randomTestOrder; + return this; + } + + public TestRunner randomTestOrderSeed(Integer seed) { + this.options.randomTestOrderSeed = seed; + if (seed != null) this.options.randomTestOrder = true; + return this; + } + + public TestRunner addTag(String tag) { + this.options.tags.add(tag); + return this; + } + + public TestRunner addTags(Collection tags) { + this.options.tags.addAll(tags); + return this; + } + + public TestRunner oraStuckTimeout(Integer oraStuckTimeout) { + this.options.oraStuckTimeout = oraStuckTimeout; + return this; + } + + public TestRunnerOptions getOptions() { + return options; + } + private void delayedAddReporters() { - if ( reporterFactory != null ) - reporterNames.forEach( this::addReporter ); - else + if (reporterFactory != null) { + reporterNames.forEach(this::addReporter); + } else { throw new IllegalStateException("ReporterFactory must be set to add delayed Reporters!"); + } + } + + private void handleException(Throwable e) throws SQLException { + // Just pass exceptions already categorized + if (e instanceof UtPLSQLNotInstalledException) throw (UtPLSQLNotInstalledException) e; + else if (e instanceof SomeTestsFailedException) throw (SomeTestsFailedException) e; + else if (e instanceof OracleCreateStatmenetStuckException) throw (OracleCreateStatmenetStuckException) e; + // Categorize exceptions + else if (e instanceof SQLException) { + SQLException sqlException = (SQLException) e; + if (sqlException.getErrorCode() == SomeTestsFailedException.ERROR_CODE) { + throw new SomeTestsFailedException(sqlException.getMessage(), e); + } else if (((SQLException) e).getErrorCode() == UtPLSQLNotInstalledException.ERROR_CODE) { + throw new UtPLSQLNotInstalledException(sqlException); + } else { + throw sqlException; + } + } else { + throw new SQLException("Unknown exception, wrapping: " + e.getMessage(), e); + } } public void run(Connection conn) throws SQLException { @@ -129,11 +208,16 @@ public void run(Connection conn) throws SQLException { DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); - compatibilityProxy = new CompatibilityProxy(conn, options.skipCompatibilityCheck, databaseInformation); - logger.info("Running on utPLSQL {}", compatibilityProxy.getDatabaseVersion()); + if (options.skipCompatibilityCheck) { + compatibilityProxy = new CompatibilityProxy(conn, Version.LATEST, databaseInformation); + } else { + compatibilityProxy = new CompatibilityProxy(conn, databaseInformation); + } + logger.info("Running on utPLSQL {}", compatibilityProxy.getVersionDescription()); - if ( reporterFactory == null ) + if (reporterFactory == null) { reporterFactory = ReporterFactory.createDefault(compatibilityProxy); + } delayedAddReporters(); @@ -141,8 +225,9 @@ public void run(Connection conn) throws SQLException { compatibilityProxy.failOnNotCompatible(); logger.info("Initializing reporters"); - for (Reporter r : options.reporterList) + for (Reporter r : options.reporterList) { validateReporter(conn, r); + } if (options.pathList.isEmpty()) { options.pathList.add(databaseInformation.getCurrentSchema(conn)); @@ -153,43 +238,73 @@ public void run(Connection conn) throws SQLException { options.reporterList.add(new DocumentationReporter().init(conn)); } - try(TestRunnerStatement testRunnerStatement = compatibilityProxy.getTestRunnerStatement(options, conn)) { + TestRunnerStatement testRunnerStatement = null; + try { + testRunnerStatement = (options.oraStuckTimeout > 0) ? initStatementWithTimeout(conn, options.oraStuckTimeout) : initStatement(conn); logger.info("Running tests"); testRunnerStatement.execute(); logger.info("Running tests finished."); + testRunnerStatement.close(); + } catch (OracleCreateStatmenetStuckException e) { + // Don't close statement in this case for it will be stuck, too + throw e; } catch (SQLException e) { - if (e.getErrorCode() == SomeTestsFailedException.ERROR_CODE) { - throw new SomeTestsFailedException(e.getMessage(), e); - } - else if (e.getErrorCode() == UtPLSQLNotInstalledException.ERROR_CODE) { - throw new UtPLSQLNotInstalledException(e); - } - else { - throw e; - } + if (testRunnerStatement != null) testRunnerStatement.close(); + handleException(e); + } + } + + private TestRunnerStatement initStatement(Connection conn) throws SQLException { + return compatibilityProxy.getTestRunnerStatement(options, conn); + } + + private TestRunnerStatement initStatementWithTimeout(Connection conn, int timeout) throws SQLException { + TestRunnerStatement testRunnerStatement; + ExecutorService executor = Executors.newSingleThreadExecutor(); + Callable callable = () -> compatibilityProxy.getTestRunnerStatement(options, conn); + Future future = executor.submit(callable); + + // We want to leave the statement open in case of stuck scenario + testRunnerStatement = null; + try { + testRunnerStatement = future.get(timeout, TimeUnit.SECONDS); + } catch (TimeoutException e) { + logger.error("Detected Oracle driver stuck during Statement initialization"); + executor.shutdownNow(); + throw new OracleCreateStatmenetStuckException(e); + } catch (InterruptedException e) { + handleException(e); + } catch (ExecutionException e) { + handleException(e.getCause()); } + + return testRunnerStatement; } /** * Check if the reporter was initialized, if not call reporter.init. - * @param conn the database connection + * + * @param conn the database connection * @param reporter the reporter * @throws SQLException any sql exception */ private void validateReporter(Connection conn, Reporter reporter) throws SQLException { - if (!reporter.isInit() || reporter.getId() == null || reporter.getId().isEmpty()) + if (!reporter.isInit() || reporter.getId() == null || reporter.getId().isEmpty()) { reporter.init(conn, compatibilityProxy, reporterFactory); + } } - /** Returns the databaseVersion the TestRunner was run against + /** + * Returns the databaseVersion the TestRunner was run against * * @return Version of the database the TestRunner was run against */ public Version getUsedDatabaseVersion() { - if ( compatibilityProxy != null ) - return compatibilityProxy.getDatabaseVersion(); - else + if (compatibilityProxy != null) { + return compatibilityProxy.getUtPlsqlVersion(); + } else { return null; + } } } diff --git a/src/main/java/org/utplsql/api/TestRunnerOptions.java b/src/main/java/org/utplsql/api/TestRunnerOptions.java index 936254f..615e308 100644 --- a/src/main/java/org/utplsql/api/TestRunnerOptions.java +++ b/src/main/java/org/utplsql/api/TestRunnerOptions.java @@ -4,24 +4,39 @@ import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; -/** Holds the various possible options of TestRunner +/** + * Holds the various possible options of TestRunner * * @author pesse */ public class TestRunnerOptions { public final List pathList = new ArrayList<>(); public final List reporterList = new ArrayList<>(); - public boolean colorConsole = false; public final List coverageSchemes = new ArrayList<>(); public final List sourceFiles = new ArrayList<>(); public final List testFiles = new ArrayList<>(); public final List includeObjects = new ArrayList<>(); public final List excludeObjects = new ArrayList<>(); + public String includeSchemaExpr; + public String excludeSchemaExpr; + public String includeObjectExpr; + public String excludeObjectExpr; + public boolean colorConsole = false; public FileMapperOptions sourceMappingOptions; public FileMapperOptions testMappingOptions; public boolean failOnErrors = false; public boolean skipCompatibilityCheck = false; public String clientCharacterSet = Charset.defaultCharset().toString(); + public boolean randomTestOrder = false; + public Integer randomTestOrderSeed; + public final Set tags = new LinkedHashSet<>(); + public Integer oraStuckTimeout = 0; + + public String getTagsAsString() { + return String.join(",", tags); + } } diff --git a/src/main/java/org/utplsql/api/Version.java b/src/main/java/org/utplsql/api/Version.java index f7f1cf4..5aaabfa 100644 --- a/src/main/java/org/utplsql/api/Version.java +++ b/src/main/java/org/utplsql/api/Version.java @@ -12,23 +12,37 @@ import static java.util.stream.Collectors.toMap; -/** Simple class to parse utPLSQL Version-information and provide the separate version-numbers +/** + * Simple class to parse utPLSQL Version-information and provide the separate version-numbers * * @author pesse */ public class Version implements Comparable { - public final static Version V3_0_0 = new Version("3.0.0", 3,0,0,null, true); - public final static Version V3_0_1 = new Version("3.0.1", 3,0,1,null, true); - public final static Version V3_0_2 = new Version("3.0.2", 3,0,2,null, true); - public final static Version V3_0_3 = new Version("3.0.3", 3,0,3,null, true); - public final static Version V3_0_4 = new Version("3.0.4", 3,0,4,null, true); - public final static Version V3_1_0 = new Version("3.1.0", 3,1,0,null, true); - public final static Version V3_1_1 = new Version("3.1.1", 3,1,1,null, true); - public final static Version V3_1_2 = new Version("3.1.2", 3,1,2,null, true); + public final static Version V3_0_0 = new Version("3.0.0", 3, 0, 0, null, true); + public final static Version V3_0_1 = new Version("3.0.1", 3, 0, 1, null, true); + public final static Version V3_0_2 = new Version("3.0.2", 3, 0, 2, null, true); + public final static Version V3_0_3 = new Version("3.0.3", 3, 0, 3, null, true); + public final static Version V3_0_4 = new Version("3.0.4", 3, 0, 4, null, true); + public final static Version V3_1_0 = new Version("3.1.0", 3, 1, 0, 1847, true); + public final static Version V3_1_1 = new Version("3.1.1", 3, 1, 1, 1865, true); + public final static Version V3_1_2 = new Version("3.1.2", 3, 1, 2, 2130, true); + public final static Version V3_1_3 = new Version("3.1.3", 3, 1, 3, 2398, true); + public final static Version V3_1_4 = new Version("3.1.4", 3, 1, 4, 2223, true); + public final static Version V3_1_5 = new Version("3.1.5", 3, 1, 5, 2707, true); + public final static Version V3_1_6 = new Version("3.1.6", 3, 1, 6, 2729, true); + public final static Version V3_1_7 = new Version("3.1.7", 3, 1, 7, 3085, true); + public final static Version V3_1_8 = new Version("3.1.8", 3, 1, 8, 3188, true); + public final static Version V3_1_9 = new Version("3.1.9", 3, 1, 9, 3268, true); + public final static Version V3_1_10 = new Version("3.1.10", 3, 1, 10, 3347, true); + public final static Version V3_1_11 = new Version("3.1.11", 3, 1, 11, 3557, true); + public final static Version V3_1_12 = new Version("3.1.12", 3, 1, 12, 3876, true); + public final static Version V3_1_13 = new Version("3.1.13", 3, 1, 13, 3592, true); + private final static Map knownVersions = - Stream.of(V3_0_0, V3_0_1, V3_0_2, V3_0_3, V3_0_4, V3_1_0, V3_1_1, V3_1_2) + Stream.of(V3_0_0, V3_0_1, V3_0_2, V3_0_3, V3_0_4, V3_1_0, V3_1_1, V3_1_2, V3_1_3, V3_1_4, V3_1_5, V3_1_6, V3_1_7, V3_1_8, V3_1_9, V3_1_10, V3_1_11, V3_1_13) .collect(toMap(Version::toString, Function.identity())); + public final static Version LATEST = V3_1_13; private final String origString; private final Integer major; @@ -49,15 +63,18 @@ private Version(String origString, @Nullable Integer major, @Nullable Integer mi /** * Use {@link Version#create} factory method instead * For removal + * + * @param versionString Version as String */ @Deprecated() public Version(String versionString) { - assert versionString != null; + Objects.requireNonNull(versionString); + Version dummy = parseVersionString(versionString); this.origString = dummy.origString; this.major = dummy.major; - this.minor =dummy.minor; + this.minor = dummy.minor; this.bugfix = dummy.bugfix; this.build = dummy.build; this.valid = dummy.valid; @@ -69,8 +86,7 @@ public static Version create(final String versionString) { return version != null ? version : parseVersionString(origString); } - private static Version parseVersionString(String origString) - { + private static Version parseVersionString(String origString) { Integer major = null; Integer minor = null; @@ -83,22 +99,23 @@ private static Version parseVersionString(String origString) try { if (m.find()) { - if (m.group(1) != null ) + if (m.group(1) != null) { major = Integer.valueOf(m.group(1)); - if (m.group(2) != null ) + } + if (m.group(2) != null) { minor = Integer.valueOf(m.group(2)); - if (m.group(3) != null ) + } + if (m.group(3) != null) { bugfix = Integer.valueOf(m.group(3)); - if (m.group(4) != null ) + } + if (m.group(4) != null) { build = Integer.valueOf(m.group(4)); + } - // We need a valid major version as minimum requirement for a Version object to be valid + // We need a valid major version as minimum requirement for a Version object to be valid valid = major != null; } - } - catch ( NumberFormatException e ) - { - valid = false; + } catch (NumberFormatException ignore) { } return new Version(origString, major, minor, bugfix, build, valid); @@ -133,104 +150,147 @@ public boolean isValid() { return valid; } - /** Returns a normalized form of the parsed version information + /** + * Returns a normalized form of the parsed version information * - * @return + * @return normalized string */ - public String getNormalizedString() - { - if ( isValid() ) { + public String getNormalizedString() { + if (isValid()) { StringBuilder sb = new StringBuilder(); sb.append(major); - if ( minor != null ) + if (minor != null) { sb.append(".").append(minor); - if ( bugfix != null ) + } + if (bugfix != null) { sb.append(".").append(bugfix); - if ( build != null ) + } + if (build != null) { sb.append(".").append(build); + } return sb.toString(); - } - else + } else { return "invalid"; + } + } + + private int compareToWithNulls(@Nullable Integer i1, @Nullable Integer i2) { + return compareToWithNulls(i1, i2, false); } - private int compareToWithNulls(@Nullable Integer i1, @Nullable Integer i2 ) { - if ( i1 == null && i2 == null ) + private int compareToWithNulls(@Nullable Integer i1, @Nullable Integer i2, boolean nullMeansEqual) { + if (i1 == null && i2 == null) { return 0; - else if ( i1 == null ) - return -1; - else if ( i2 == null ) + } else if (i1 == null) { + return nullMeansEqual ? 0 : -1; + } else if (i2 == null) { return 1; - else return i1.compareTo(i2); + } else { + return i1.compareTo(i2); + } } @Override public int compareTo(Version o) { + return compareTo(o, false); + } + + public int compareTo(Version o, boolean nullMeansEqual) { int curResult; - if ( isValid() && o.isValid() ) { + if (isValid() && o.isValid()) { - curResult = compareToWithNulls(getMajor(), o.getMajor()); - if ( curResult != 0 ) + curResult = compareToWithNulls(getMajor(), o.getMajor(), nullMeansEqual); + if (curResult != 0) { return curResult; + } - curResult = compareToWithNulls(getMinor(), o.getMinor()); - if ( curResult != 0 ) + curResult = compareToWithNulls(getMinor(), o.getMinor(), nullMeansEqual); + if (curResult != 0) { return curResult; + } - curResult = compareToWithNulls(getBugfix(), o.getBugfix()); - if ( curResult != 0 ) + curResult = compareToWithNulls(getBugfix(), o.getBugfix(), nullMeansEqual); + if (curResult != 0) { return curResult; + } - curResult = compareToWithNulls(getBuild(), o.getBuild()); - if ( curResult != 0 ) - return curResult; + curResult = compareToWithNulls(getBuild(), o.getBuild(), nullMeansEqual); + + return curResult; } return 0; } - private void versionsAreValid( Version v ) throws InvalidVersionException { - if ( !isValid() ) + private void versionsAreValid(Version v) throws InvalidVersionException { + if (!isValid()) { throw new InvalidVersionException(this); + } - if ( !v.isValid() ) + if (!v.isValid()) { throw new InvalidVersionException(v); + } } - /** Compares this version to a given version and returns true if this version is greater or equal than the given one - * Throws an InvalidVersionException if either this or the given version are invalid + /** + * Compares this version to a given version and returns true if this version is greater or equal than the given one + * If one of the version parts of the base version is null, this level is assumed equal no matter the comparing level's version part + * Throws an {@link InvalidVersionException} if either this or the given version are invalid * * @param v Version to compare with - * @return - * @throws InvalidVersionException + * @return true if is greater or equal + * @throws InvalidVersionException If the version does not match */ - public boolean isGreaterOrEqualThan( Version v ) throws InvalidVersionException { + public boolean isGreaterOrEqualThan(Version v) throws InvalidVersionException { versionsAreValid(v); - return compareTo(v) >= 0; + return compareTo(v, true) >= 0; } - - public boolean isGreaterThan( Version v) throws InvalidVersionException - { + /** + * Compares this version to a given version and returns true if this version is greater than the given one + * If one of the version parts of the base version is null, this level is assumed equal no matter the comparing level's version part + * Throws an {@link InvalidVersionException} if either this or the given version are invalid + * + * @param v Version to compare with + * @return true if is greater + * @throws InvalidVersionException If the version does not match + */ + public boolean isGreaterThan(Version v) throws InvalidVersionException { versionsAreValid(v); return compareTo(v) > 0; } - public boolean isLessOrEqualThan( Version v ) throws InvalidVersionException - { + /** + * Compares this version to a given version and returns true if this version is less or equal than the given one + * If one of the version parts of the base version is null, this level is assumed equal no matter the comparing level's version part + * Throws an {@link InvalidVersionException} if either this or the given version are invalid + * + * @param v Version to compare with + * @return if version is less or equal + * @throws InvalidVersionException If version is invalid + */ + public boolean isLessOrEqualThan(Version v) throws InvalidVersionException { versionsAreValid(v); - return compareTo(v) <= 0; + return compareTo(v, true) <= 0; } - public boolean isLessThan( Version v) throws InvalidVersionException - { + /** + * Compares this version to a given version and returns true if this version is less than the given one + * If one of the version parts of the base version is null, this level is assumed equal no matter the comparing level's version part + * Throws an {@link InvalidVersionException} if either this or the given version are invalid + * + * @param v Version to compare with + * @return if version is less + * @throws InvalidVersionException If version is invalid + */ + public boolean isLessThan(Version v) throws InvalidVersionException { versionsAreValid(v); return compareTo(v) < 0; diff --git a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java index 068bd31..eb68f23 100644 --- a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java +++ b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java @@ -11,11 +11,13 @@ import org.utplsql.api.testRunner.TestRunnerStatement; import org.utplsql.api.testRunner.TestRunnerStatementProvider; +import javax.annotation.Nullable; import java.sql.Connection; import java.sql.SQLException; import java.util.Objects; -/** Class to check compatibility with database framework and also to give several specific implementations depending +/** + * Class to check compatibility with database framework and also to give several specific implementations depending * on the version of the connected framework. * If one skips the compatibility check, the Proxy acts as like the framework has the same version as the API * @@ -23,74 +25,81 @@ */ public class CompatibilityProxy { - private static final String UTPLSQL_API_VERSION = "3.1.1"; public static final String UTPLSQL_COMPATIBILITY_VERSION = "3"; - - private Version databaseVersion; - private boolean compatible = false; private final DatabaseInformation databaseInformation; + private Version utPlsqlVersion; + private final Version realDbPlsqlVersion; + private boolean compatible = false; + + public CompatibilityProxy(Connection conn) throws SQLException { + this(conn, null, null); + } - public CompatibilityProxy( Connection conn ) throws SQLException { - this(conn, false, null); + @Deprecated + public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck) throws SQLException { + this(conn, skipCompatibilityCheck, null); } - public CompatibilityProxy( Connection conn, DatabaseInformation databaseInformation ) throws SQLException { - this(conn, false, databaseInformation); + @Deprecated + public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck, @Nullable DatabaseInformation databaseInformation) throws SQLException { + this(conn, skipCompatibilityCheck ? Version.LATEST : null, databaseInformation); } - public CompatibilityProxy( Connection conn, boolean skipCompatibilityCheck ) throws SQLException { - this(conn, skipCompatibilityCheck, null); + public CompatibilityProxy(Connection conn, @Nullable DatabaseInformation databaseInformation) throws SQLException { + this(conn, null, databaseInformation); + } + + public CompatibilityProxy(Connection conn, @Nullable Version assumedUtPlsVersion) throws SQLException { + this(conn, assumedUtPlsVersion, null); } - public CompatibilityProxy( Connection conn, boolean skipCompatibilityCheck, DatabaseInformation databaseInformation ) throws SQLException { - this.databaseInformation = (databaseInformation != null ) - ? databaseInformation - : new DefaultDatabaseInformation(); + public CompatibilityProxy(Connection conn, @Nullable Version assumedUtPlsqlVersion, @Nullable DatabaseInformation databaseInformation) throws SQLException { + this.databaseInformation = (databaseInformation != null) + ? databaseInformation + : new DefaultDatabaseInformation(); - if ( skipCompatibilityCheck ) - doExpectCompatibility(); - else + realDbPlsqlVersion = this.databaseInformation.getUtPlsqlFrameworkVersion(conn); + if (assumedUtPlsqlVersion != null) { + utPlsqlVersion = assumedUtPlsqlVersion; + compatible = utPlsqlVersion.getNormalizedString().startsWith(UTPLSQL_COMPATIBILITY_VERSION); + } else { doCompatibilityCheckWithDatabase(conn); + } } - /** Receives the current framework version from database and checks - depending on the framework version - whether + /** + * Receives the current framework version from database and checks - depending on the framework version - whether * the API version is compatible or not. * - * @param conn - * @throws SQLException + * @param conn {@link Connection} + * @throws DatabaseNotCompatibleException if the database is not compatible */ - private void doCompatibilityCheckWithDatabase( Connection conn ) throws SQLException - { - databaseVersion = databaseInformation.getUtPlsqlFrameworkVersion(conn); + private void doCompatibilityCheckWithDatabase(Connection conn) throws DatabaseNotCompatibleException { + utPlsqlVersion = realDbPlsqlVersion; Version clientVersion = Version.create(UTPLSQL_COMPATIBILITY_VERSION); - if ( databaseVersion == null ) + if (utPlsqlVersion == null) { throw new DatabaseNotCompatibleException("Could not get database version", clientVersion, null, null); + } - if ( databaseVersion.getMajor() == null ) - throw new DatabaseNotCompatibleException("Illegal database version: " + databaseVersion.toString(), clientVersion, databaseVersion, null); + if (utPlsqlVersion.getMajor() == null) { + throw new DatabaseNotCompatibleException("Illegal database version: " + utPlsqlVersion.toString(), clientVersion, utPlsqlVersion, null); + } - if (OptionalFeatures.FRAMEWORK_COMPATIBILITY_CHECK.isAvailableFor(databaseVersion)) { + if (OptionalFeatures.FRAMEWORK_COMPATIBILITY_CHECK.isAvailableFor(utPlsqlVersion)) { try { compatible = versionCompatibilityCheck(conn, UTPLSQL_COMPATIBILITY_VERSION, null); } catch (SQLException e) { throw new DatabaseNotCompatibleException("Compatibility-check failed with error. Aborting. Reason: " + e.getMessage(), clientVersion, Version.create("Unknown"), e); } - } else + } else { compatible = versionCompatibilityCheckPre303(UTPLSQL_COMPATIBILITY_VERSION); - } - - /** Just prepare the proxy to expect compatibility, expecting the database framework to be the same version as the API - * - */ - private void doExpectCompatibility() - { - databaseVersion = Version.create(UTPLSQL_API_VERSION); - compatible = true; + } } /** * Check the utPLSQL version compatibility. + * * @param conn the connection * @return true if the requested utPLSQL version is compatible with the one installed on database * @throws SQLException any database error @@ -100,68 +109,87 @@ private boolean versionCompatibilityCheck(Connection conn, String requested, Str try { return databaseInformation.frameworkCompatibilityCheck(conn, requested, current) == 1; } catch (SQLException e) { - if (e.getErrorCode() == 6550) + if (e.getErrorCode() == 6550) { return false; - else + } else { throw e; + } } } - /** Simple fallback check for compatiblity: Major and Minor version must be equal + /** + * Simple fallback check for compatibility: Major and Minor version must be equal * - * @param requested - * @return + * @param versionRequested Requested version + * @return weather the version is available or not */ - private boolean versionCompatibilityCheckPre303(String requested ) - { - Version requestedVersion = Version.create(requested); + private boolean versionCompatibilityCheckPre303(String versionRequested) { + Version requestedVersion = Version.create(versionRequested); - Objects.requireNonNull(databaseVersion.getMajor(), "Illegal database Version: " + databaseVersion.toString()); - return databaseVersion.getMajor().equals(requestedVersion.getMajor()) + Objects.requireNonNull(utPlsqlVersion.getMajor(), "Illegal database Version: " + utPlsqlVersion.toString()); + return Objects.equals(utPlsqlVersion.getMajor(), requestedVersion.getMajor()) && (requestedVersion.getMinor() == null - || requestedVersion.getMinor().equals(databaseVersion.getMinor())); + || requestedVersion.getMinor().equals(utPlsqlVersion.getMinor())); } - /** Checks if actual API-version is compatible with utPLSQL database version and throws a DatabaseNotCompatibleException if not + /** + * Checks if actual API-version is compatible with utPLSQL database version and throws a DatabaseNotCompatibleException if not * Throws a DatabaseNotCompatibleException if version compatibility can not be checked. * + * @throws DatabaseNotCompatibleException if versions are not compatible */ - public void failOnNotCompatible() throws DatabaseNotCompatibleException - { - if ( !isCompatible() ) - throw new DatabaseNotCompatibleException( databaseVersion ); + public void failOnNotCompatible() throws DatabaseNotCompatibleException { + if (!isCompatible()) { + throw new DatabaseNotCompatibleException(utPlsqlVersion); + } } - public boolean isCompatible() - { + public boolean isCompatible() { return compatible; } - public Version getDatabaseVersion() - { - return databaseVersion; + @Deprecated + public Version getDatabaseVersion() { + return utPlsqlVersion; + } + + public Version getUtPlsqlVersion() { + return utPlsqlVersion; + } + + public Version getRealDbPlsqlVersion() { + return realDbPlsqlVersion; + } + + public String getVersionDescription() { + if (utPlsqlVersion != realDbPlsqlVersion) { + return realDbPlsqlVersion.toString() + " (Assumed: " + utPlsqlVersion.toString() + ")"; + } else { + return utPlsqlVersion.toString(); + } } - /** Returns a TestRunnerStatement compatible with the current framework + /** + * Returns a TestRunnerStatement compatible with the current framework * - * @param options - * @param conn - * @return - * @throws SQLException + * @param options {@link TestRunnerOptions} + * @param conn {@link Connection} + * @return TestRunnerStatement + * @throws SQLException if there are problems with the database access */ - public TestRunnerStatement getTestRunnerStatement(TestRunnerOptions options, Connection conn) throws SQLException - { - return TestRunnerStatementProvider.getCompatibleTestRunnerStatement(databaseVersion, options, conn); + public TestRunnerStatement getTestRunnerStatement(TestRunnerOptions options, Connection conn) throws SQLException { + return TestRunnerStatementProvider.getCompatibleTestRunnerStatement(utPlsqlVersion, options, conn); } - /** Returns an OutputBuffer compatible with the current framework + /** + * Returns an OutputBuffer compatible with the current framework * - * @param reporter - * @param conn - * @return - * @throws SQLException + * @param reporter {@link Reporter} + * @param conn {@link Connection} + * @return OutputBuffer + * @throws SQLException if there are problems with the database access */ public OutputBuffer getOutputBuffer(Reporter reporter, Connection conn) throws SQLException { - return OutputBufferProvider.getCompatibleOutputBuffer(databaseVersion, reporter, conn); + return OutputBufferProvider.getCompatibleOutputBuffer(utPlsqlVersion, reporter, conn); } } diff --git a/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java b/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java index b2fed20..8462b95 100644 --- a/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java +++ b/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java @@ -10,29 +10,36 @@ public enum OptionalFeatures { FAIL_ON_ERROR("3.0.3.1266", null), FRAMEWORK_COMPATIBILITY_CHECK("3.0.3.1266", null), - CUSTOM_REPORTERS("3.1.0.1849", null); + CUSTOM_REPORTERS("3.1.0.1849", null), + CLIENT_CHARACTER_SET("3.1.2.2130", null), + RANDOM_EXECUTION_ORDER("3.1.7.2795", null), + TAGS("3.1.7.3006", null), + EXPR("3.1.13", null); private final Version minVersion; private final Version maxVersion; - OptionalFeatures( String minVersion, String maxVersion ) - { + OptionalFeatures(String minVersion, String maxVersion) { this.minVersion = minVersion != null ? Version.create(minVersion) : null; this.maxVersion = maxVersion != null ? Version.create(maxVersion) : null; } - public boolean isAvailableFor(Version version ) { + public boolean isAvailableFor(Version version) { try { return (minVersion == null || version.isGreaterOrEqualThan(minVersion)) && (maxVersion == null || maxVersion.isGreaterOrEqualThan(version)); - } catch ( InvalidVersionException e ) { + } catch (InvalidVersionException e) { return false; // We have no optional features for invalid versions } } public boolean isAvailableFor(Connection conn) throws SQLException { CompatibilityProxy proxy = new CompatibilityProxy(conn); - return isAvailableFor(proxy.getDatabaseVersion()); + return isAvailableFor(proxy.getUtPlsqlVersion()); } + + public Version getMinVersion() { return minVersion; } + + public Version getMaxVersion() { return maxVersion; } } diff --git a/src/main/java/org/utplsql/api/db/DatabaseInformation.java b/src/main/java/org/utplsql/api/db/DatabaseInformation.java index 8ea058f..6e76cfa 100644 --- a/src/main/java/org/utplsql/api/db/DatabaseInformation.java +++ b/src/main/java/org/utplsql/api/db/DatabaseInformation.java @@ -6,17 +6,18 @@ import java.sql.Connection; import java.sql.SQLException; -/** Abstraction-interface to encapsulate Database-Calls (and potentially mock them) +/** + * Abstraction-interface to encapsulate Database-Calls (and potentially mock them) * * @author pesse */ public interface DatabaseInformation { - Version getUtPlsqlFrameworkVersion(Connection conn ) throws SQLException; + Version getUtPlsqlFrameworkVersion(Connection conn) throws SQLException; - String getOracleVersion( Connection conn ) throws SQLException; + String getOracleVersion(Connection conn) throws SQLException; - String getCurrentSchema( Connection conn ) throws SQLException; + String getCurrentSchema(Connection conn) throws SQLException; int frameworkCompatibilityCheck(Connection conn, String requested, @Nullable String current) throws SQLException; } diff --git a/src/main/java/org/utplsql/api/db/DefaultDatabaseInformation.java b/src/main/java/org/utplsql/api/db/DefaultDatabaseInformation.java index 4fabee7..181ffa6 100644 --- a/src/main/java/org/utplsql/api/db/DefaultDatabaseInformation.java +++ b/src/main/java/org/utplsql/api/db/DefaultDatabaseInformation.java @@ -11,19 +11,20 @@ public class DefaultDatabaseInformation implements DatabaseInformation { @Override public Version getUtPlsqlFrameworkVersion(Connection conn) throws SQLException { Version result = Version.create(""); - try (PreparedStatement stmt = conn.prepareStatement("select ut_runner.version() from dual")) - { + try (PreparedStatement stmt = conn.prepareStatement("select ut_runner.version() from dual")) { ResultSet rs = stmt.executeQuery(); - if ( rs.next() ) + if (rs.next()) { result = Version.create(rs.getString(1)); + } rs.close(); - } catch ( SQLException e ) { - if ( e.getErrorCode() == UtPLSQLNotInstalledException.ERROR_CODE ) + } catch (SQLException e) { + if (e.getErrorCode() == UtPLSQLNotInstalledException.ERROR_CODE) { throw new UtPLSQLNotInstalledException(e); - else + } else { throw e; + } } return result; @@ -32,12 +33,12 @@ public Version getUtPlsqlFrameworkVersion(Connection conn) throws SQLException { @Override public String getOracleVersion(Connection conn) throws SQLException { String result = null; - try (PreparedStatement stmt = conn.prepareStatement("select version from product_component_version where product like 'Oracle Database%'")) - { + try (PreparedStatement stmt = conn.prepareStatement("select version from product_component_version where product like 'Oracle Database%'")) { ResultSet rs = stmt.executeQuery(); - if ( rs.next() ) + if (rs.next()) { result = rs.getString(1); + } } return result; @@ -54,14 +55,15 @@ public String getCurrentSchema(Connection conn) throws SQLException { @Override public int frameworkCompatibilityCheck(Connection conn, String requested, @Nullable String current) throws SQLException { - try(CallableStatement callableStatement = conn.prepareCall("BEGIN ? := ut_runner.version_compatibility_check(?, ?); END;")) { + try (CallableStatement callableStatement = conn.prepareCall("BEGIN ? := ut_runner.version_compatibility_check(?, ?); END;")) { callableStatement.registerOutParameter(1, Types.SMALLINT); callableStatement.setString(2, requested); - if (current == null) + if (current == null) { callableStatement.setNull(3, Types.VARCHAR); - else + } else { callableStatement.setString(3, current); + } callableStatement.executeUpdate(); return callableStatement.getInt(1); diff --git a/src/main/java/org/utplsql/api/db/DynamicParameterList.java b/src/main/java/org/utplsql/api/db/DynamicParameterList.java new file mode 100644 index 0000000..c6df04c --- /dev/null +++ b/src/main/java/org/utplsql/api/db/DynamicParameterList.java @@ -0,0 +1,220 @@ +package org.utplsql.api.db; + +import oracle.jdbc.OracleConnection; + +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.LinkedHashMap; +import java.util.stream.Collectors; + +/** Lets you build a list of parameters for a CallableStatement + *
+ * Create it with the Builder (DynamicParameterList.builder()) + * + * @author pesse + */ +public class DynamicParameterList { + + private final LinkedHashMap params; + + interface DynamicParameter { + void setParam( CallableStatement statement, int index ) throws SQLException; + + default String getSql( String key ) { + return key + " => ?"; + } + } + + private DynamicParameterList(LinkedHashMap params) { + this.params = params; + } + + /** Returns the SQL of this ParameterList as comma-separated list of the parameter identifiers:
+ * + * e.g. "a_parameter1 => ?, a_parameter2 => ?" + * + * @return comma-separated list of parameter identifiers + */ + public String getSql() { + return params.entrySet().stream() + .map(e -> e.getValue().getSql(e.getKey())) + .collect(Collectors.joining(", ")); + } + + /** Sets the contained parameters in the order they were added to the given statement by index, starting with the given one + * + * @param statement The statement to set the parameters to + * @param startIndex The index to start with + * @throws SQLException SQLException of the underlying statement.setX methods + */ + public void setParamsStartWithIndex(CallableStatement statement, int startIndex ) throws SQLException { + int index = startIndex; + for ( DynamicParameter param : params.values() ) { + param.setParam(statement, index++); + } + } + + /** Returns a builder to create a DynamicParameterList + * + * @return Builder + */ + public static DynamicParameterListBuilder builder() { + return new DynamicParameterListBuilder(); + } + + /** Builder-class for DynamicParameterList + *
+ * Usage: + *
+     *  DynamicParameterList.builder()
+     *      .add("parameter1", "StringParameter")
+     *      .addIfNotEmpty("parameter2", 123)
+     *      .build();
+     * 
+ * + * @author pesse + */ + public static class DynamicParameterListBuilder { + + private final LinkedHashMap params = new LinkedHashMap<>(); + + private DynamicParameterListBuilder() { + + } + + public DynamicParameterListBuilder add(String identifier, String value ) { + params.put(identifier, new DynamicParameterList.DynamicStringParameter(value)); + return this; + } + + public DynamicParameterListBuilder addIfNotEmpty(String identifier, String value ) { + if ( value != null && !value.isEmpty() ) { + add(identifier, value); + } + return this; + } + + public DynamicParameterListBuilder add(String identifier, Integer value ) { + params.put(identifier, new DynamicParameterList.DynamicIntegerParameter(value)); + return this; + } + + public DynamicParameterListBuilder addIfNotEmpty(String identifier, Integer value ) { + if ( value != null) { + add(identifier, value); + } + return this; + } + + public DynamicParameterListBuilder add(String identifier, Object[] value, String customTypeName, OracleConnection oraConnection ) { + params.put(identifier, new DynamicParameterList.DynamicArrayParameter(value, customTypeName, oraConnection)); + return this; + } + + public DynamicParameterListBuilder addIfNotEmpty(String identifier, Object[] value, String customTypeName, OracleConnection oraConnection ) { + if ( value != null && value.length > 0 ) { + add(identifier, value, customTypeName, oraConnection); + } + return this; + } + + public DynamicParameterListBuilder add(String identifier, Boolean value) { + params.put(identifier, new DynamicBoolParameter(value)); + return this; + } + + public DynamicParameterListBuilder addIfNotEmpty(String identifier, Boolean value) { + if ( value != null ) { + add(identifier, value); + } + return this; + } + + public DynamicParameterList build() { + return new DynamicParameterList(params); + } + } + + /* Implementations of DynamicStringParameter */ + private static class DynamicStringParameter implements DynamicParameter { + private final String value; + + DynamicStringParameter( String value ) { + this.value = value; + } + + @Override + public void setParam(CallableStatement statement, int index) throws SQLException { + if ( value == null ) { + statement.setNull(index, Types.VARCHAR); + } else { + statement.setString(index, value); + } + } + } + + private static class DynamicIntegerParameter implements DynamicParameter { + private final Integer value; + + DynamicIntegerParameter( Integer value ) { + this.value = value; + } + + @Override + public void setParam(CallableStatement statement, int index) throws SQLException { + if ( value == null ) { + statement.setNull(index, Types.INTEGER); + } else { + statement.setInt(index, value); + } + } + } + + private static class DynamicBoolParameter implements DynamicParameter { + private final Boolean value; + + DynamicBoolParameter( Boolean value ) { + this.value = value; + } + + @Override + public void setParam(CallableStatement statement, int index) throws SQLException { + if ( value == null ) { + statement.setNull(index, Types.BOOLEAN); + } else { + statement.setInt(index, (value)?1:0); + } + } + + @Override + public String getSql(String key) { + return key + " => (case ? when 1 then true else false end)"; + } + } + + private static class DynamicArrayParameter implements DynamicParameter { + private final Object[] value; + private final String customTypeName; + private final OracleConnection oraConnection; + + DynamicArrayParameter( Object[] value, String customTypeName, OracleConnection oraConnection ) { + this.value = value; + this.customTypeName = customTypeName; + this.oraConnection = oraConnection; + } + + @Override + public void setParam(CallableStatement statement, int index) throws SQLException { + if ( value == null ) { + statement.setNull(index, Types.ARRAY, customTypeName); + } else { + statement.setArray( + index, + oraConnection.createOracleArray(customTypeName, value) + ); + } + } + } + +} diff --git a/src/main/java/org/utplsql/api/exception/DatabaseNotCompatibleException.java b/src/main/java/org/utplsql/api/exception/DatabaseNotCompatibleException.java index a63e784..b7b7111 100644 --- a/src/main/java/org/utplsql/api/exception/DatabaseNotCompatibleException.java +++ b/src/main/java/org/utplsql/api/exception/DatabaseNotCompatibleException.java @@ -5,50 +5,44 @@ import java.sql.SQLException; -/** Custom exception to indicate API is not compatible with database framework +/** + * Custom exception to indicate API is not compatible with database framework * * @author pesse - * */ public class DatabaseNotCompatibleException extends SQLException { private final Version clientVersion; private final Version databaseVersion; - public DatabaseNotCompatibleException( String message, Version clientVersion, Version databaseVersion, Throwable cause ) - { + public DatabaseNotCompatibleException(String message, Version clientVersion, Version databaseVersion, Throwable cause) { super(message, cause); this.clientVersion = clientVersion; this.databaseVersion = databaseVersion; } - public DatabaseNotCompatibleException( Version clientVersion, Version databaseVersion, Throwable cause ) - { + public DatabaseNotCompatibleException(Version clientVersion, Version databaseVersion, Throwable cause) { this("utPLSQL API (" + clientVersion + ") not compatible with database (" + databaseVersion + ")", clientVersion, databaseVersion, cause); } - public DatabaseNotCompatibleException( Version clientVersion, Version databaseVersion ) - { + public DatabaseNotCompatibleException(Version clientVersion, Version databaseVersion) { this(clientVersion, databaseVersion, null); } - public DatabaseNotCompatibleException( Version databaseVersion, Throwable cause ) - { - this(Version.create(CompatibilityProxy.UTPLSQL_COMPATIBILITY_VERSION), databaseVersion, cause ); + public DatabaseNotCompatibleException(Version databaseVersion, Throwable cause) { + this(Version.create(CompatibilityProxy.UTPLSQL_COMPATIBILITY_VERSION), databaseVersion, cause); } - public DatabaseNotCompatibleException( Version databaseVersion ) - { - this(Version.create(CompatibilityProxy.UTPLSQL_COMPATIBILITY_VERSION), databaseVersion, null ); + public DatabaseNotCompatibleException(Version databaseVersion) { + this(Version.create(CompatibilityProxy.UTPLSQL_COMPATIBILITY_VERSION), databaseVersion, null); } public Version getClientVersion() { return clientVersion; } - public Version getDatabaseVersion() - { + public Version getDatabaseVersion() { return databaseVersion; } } diff --git a/src/main/java/org/utplsql/api/exception/InvalidVersionException.java b/src/main/java/org/utplsql/api/exception/InvalidVersionException.java index 49942ae..3350e7d 100644 --- a/src/main/java/org/utplsql/api/exception/InvalidVersionException.java +++ b/src/main/java/org/utplsql/api/exception/InvalidVersionException.java @@ -2,19 +2,19 @@ import org.utplsql.api.Version; -/** Exception thrown when trying to do stuff which requires a valid version object (like comparing) +/** + * Exception thrown when trying to do stuff which requires a valid version object (like comparing) * * @author pesse */ public class InvalidVersionException extends Exception { private final Version version; - public InvalidVersionException( Version version ) { - this( version, null ); + public InvalidVersionException(Version version) { + this(version, null); } - public InvalidVersionException( Version version, Throwable cause ) - { + public InvalidVersionException(Version version, Throwable cause) { super("Version '" + version.toString() + "' is invalid", cause); this.version = version; diff --git a/src/main/java/org/utplsql/api/exception/OracleCreateStatmenetStuckException.java b/src/main/java/org/utplsql/api/exception/OracleCreateStatmenetStuckException.java new file mode 100644 index 0000000..6183638 --- /dev/null +++ b/src/main/java/org/utplsql/api/exception/OracleCreateStatmenetStuckException.java @@ -0,0 +1,9 @@ +package org.utplsql.api.exception; + +import java.sql.SQLException; + +public class OracleCreateStatmenetStuckException extends SQLException { + public OracleCreateStatmenetStuckException(Throwable cause) { + super("Oracle driver stuck during creating the TestRunner statement. Retry.", cause); + } +} diff --git a/src/main/java/org/utplsql/api/exception/SomeTestsFailedException.java b/src/main/java/org/utplsql/api/exception/SomeTestsFailedException.java index 3fc2379..a7f8ffe 100644 --- a/src/main/java/org/utplsql/api/exception/SomeTestsFailedException.java +++ b/src/main/java/org/utplsql/api/exception/SomeTestsFailedException.java @@ -10,7 +10,7 @@ public class SomeTestsFailedException extends SQLException { public static final int ERROR_CODE = 20213; - public SomeTestsFailedException(String reason, @Nullable Throwable cause) { + public SomeTestsFailedException(String reason, @Nullable Throwable cause) { super(reason, cause); } diff --git a/src/main/java/org/utplsql/api/exception/UtPLSQLNotInstalledException.java b/src/main/java/org/utplsql/api/exception/UtPLSQLNotInstalledException.java index 0e218a3..fa197ef 100644 --- a/src/main/java/org/utplsql/api/exception/UtPLSQLNotInstalledException.java +++ b/src/main/java/org/utplsql/api/exception/UtPLSQLNotInstalledException.java @@ -2,7 +2,8 @@ import java.sql.SQLException; -/** Exception to track when utPLSQL framework is not installed or accessible on the used database +/** + * Exception to track when utPLSQL framework is not installed or accessible on the used database * * @author pesse */ @@ -10,7 +11,7 @@ public class UtPLSQLNotInstalledException extends SQLException { public static final int ERROR_CODE = 904; - public UtPLSQLNotInstalledException( SQLException cause ) { + public UtPLSQLNotInstalledException(SQLException cause) { super("utPLSQL framework is not installed on your database or not accessable to the user you are connected with", cause); } } diff --git a/src/main/java/org/utplsql/api/outputBuffer/AbstractOutputBuffer.java b/src/main/java/org/utplsql/api/outputBuffer/AbstractOutputBuffer.java index 53d4182..5354db6 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/AbstractOutputBuffer.java +++ b/src/main/java/org/utplsql/api/outputBuffer/AbstractOutputBuffer.java @@ -24,6 +24,7 @@ abstract class AbstractOutputBuffer implements OutputBuffer { /** * Creates a new DefaultOutputBuffer. + * * @param reporter the reporter to be used */ AbstractOutputBuffer(Reporter reporter) { @@ -35,6 +36,7 @@ abstract class AbstractOutputBuffer implements OutputBuffer { /** * Returns the reporter used by this buffer. + * * @return the reporter instance */ public Reporter getReporter() { @@ -49,8 +51,9 @@ public OutputBuffer setFetchSize(int fetchSize) { /** * Print the lines as soon as they are produced and write to a PrintStream. + * * @param conn DB connection - * @param ps the PrintStream to be used, e.g: System.out + * @param ps the PrintStream to be used, e.g: System.out * @throws SQLException any sql errors */ public void printAvailable(Connection conn, PrintStream ps) throws SQLException { @@ -61,22 +64,25 @@ public void printAvailable(Connection conn, PrintStream ps) throws SQLException /** * Print the lines as soon as they are produced and write to a list of PrintStreams. - * @param conn DB connection + * + * @param conn DB connection * @param printStreams the PrintStream list to be used, e.g: System.out, new PrintStream(new FileOutputStream) * @throws SQLException any sql errors */ public void printAvailable(Connection conn, List printStreams) throws SQLException { fetchAvailable(conn, s -> { - for (PrintStream ps : printStreams) + for (PrintStream ps : printStreams) { ps.println(s); + } }); } - protected abstract CallableStatement getLinesCursorStatement( Connection conn ) throws SQLException; + protected abstract CallableStatement getLinesCursorStatement(Connection conn) throws SQLException; /** * Print the lines as soon as they are produced and call the callback passing the new line. - * @param conn DB connection + * + * @param conn DB connection * @param onLineFetched the callback to be called * @throws SQLException any sql errors */ @@ -86,15 +92,17 @@ public void fetchAvailable(Connection conn, Consumer onLineFetched) thro cstmt.execute(); cstmt.setFetchSize(fetchSize); - try ( ResultSet resultSet = (ResultSet) cstmt.getObject(1)) { - while (resultSet.next()) + try (ResultSet resultSet = (ResultSet) cstmt.getObject(1)) { + while (resultSet.next()) { onLineFetched.accept(resultSet.getString("text")); + } } } } /** * Get all lines from output buffer and return it as a list of strings. + * * @param conn DB connection * @return the lines * @throws SQLException any sql errors @@ -106,7 +114,7 @@ public List fetchAll(Connection conn) throws SQLException { cstmt.execute(); cstmt.setFetchSize(fetchSize); - try ( ResultSet resultSet = (ResultSet) cstmt.getObject(1)) { + try (ResultSet resultSet = (ResultSet) cstmt.getObject(1)) { List outputLines = new ArrayList<>(); while (resultSet.next()) { @@ -118,5 +126,4 @@ public List fetchAll(Connection conn) throws SQLException { } - } diff --git a/src/main/java/org/utplsql/api/outputBuffer/CompatibilityOutputBufferPre310.java b/src/main/java/org/utplsql/api/outputBuffer/CompatibilityOutputBufferPre310.java index eb96fa8..423ed03 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/CompatibilityOutputBufferPre310.java +++ b/src/main/java/org/utplsql/api/outputBuffer/CompatibilityOutputBufferPre310.java @@ -7,13 +7,14 @@ import java.sql.Connection; import java.sql.SQLException; -/** Compatibility Output-Buffer for 3.0.0 - 3.0.4 +/** + * Compatibility Output-Buffer for 3.0.0 - 3.0.4 * * @author pesse */ class CompatibilityOutputBufferPre310 extends AbstractOutputBuffer { - CompatibilityOutputBufferPre310( Reporter reporter ) { + CompatibilityOutputBufferPre310(Reporter reporter) { super(reporter); } diff --git a/src/main/java/org/utplsql/api/outputBuffer/DefaultOutputBuffer.java b/src/main/java/org/utplsql/api/outputBuffer/DefaultOutputBuffer.java index f3d5761..ccf829c 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/DefaultOutputBuffer.java +++ b/src/main/java/org/utplsql/api/outputBuffer/DefaultOutputBuffer.java @@ -19,6 +19,7 @@ class DefaultOutputBuffer extends AbstractOutputBuffer { /** * Creates a new DefaultOutputBuffer. + * * @param reporter the reporter to be used */ DefaultOutputBuffer(Reporter reporter) { diff --git a/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java b/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java index 3e289e2..971cd63 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java +++ b/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java @@ -4,12 +4,12 @@ import java.io.PrintStream; import java.sql.Connection; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -/** An OutputBuffer replacement which just returns nothing at all. Suitable for Reporters without any output +/** + * An OutputBuffer replacement which just returns nothing at all. Suitable for Reporters without any output * * @author pesse */ @@ -17,7 +17,7 @@ class NonOutputBuffer implements OutputBuffer { private final Reporter reporter; - NonOutputBuffer( Reporter reporter) { + NonOutputBuffer(Reporter reporter) { this.reporter = reporter; } @@ -32,17 +32,18 @@ public OutputBuffer setFetchSize(int fetchSize) { } @Override - public void printAvailable(Connection conn, PrintStream ps) throws SQLException { + public void printAvailable(Connection conn, PrintStream ps) { List printStreams = new ArrayList<>(1); printStreams.add(ps); printAvailable(conn, printStreams); } @Override - public void printAvailable(Connection conn, List printStreams) throws SQLException { + public void printAvailable(Connection conn, List printStreams) { fetchAvailable(conn, s -> { - for (PrintStream ps : printStreams) + for (PrintStream ps : printStreams) { ps.println(s); + } }); } diff --git a/src/main/java/org/utplsql/api/outputBuffer/OutputBuffer.java b/src/main/java/org/utplsql/api/outputBuffer/OutputBuffer.java index 78ef639..6b2bcbd 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/OutputBuffer.java +++ b/src/main/java/org/utplsql/api/outputBuffer/OutputBuffer.java @@ -12,24 +12,27 @@ public interface OutputBuffer { Reporter getReporter(); - /** Override the fetchSize of the OutputBuffer + /** + * Override the fetchSize of the OutputBuffer * * @param fetchSize the ResultSet fetch-size. * @return this Output-Buffer */ - OutputBuffer setFetchSize( int fetchSize ); + OutputBuffer setFetchSize(int fetchSize); /** * Print the lines as soon as they are produced and write to a PrintStream. + * * @param conn DB connection - * @param ps the PrintStream to be used, e.g: System.out + * @param ps the PrintStream to be used, e.g: System.out * @throws SQLException any sql errors */ void printAvailable(Connection conn, PrintStream ps) throws SQLException; /** * Print the lines as soon as they are produced and write to a list of PrintStreams. - * @param conn DB connection + * + * @param conn DB connection * @param printStreams the PrintStream list to be used, e.g: System.out, new PrintStream(new FileOutputStream) * @throws SQLException any sql errors */ @@ -37,7 +40,8 @@ public interface OutputBuffer { /** * Print the lines as soon as they are produced and call the callback passing the new line. - * @param conn DB connection + * + * @param conn DB connection * @param onLineFetched the callback to be called * @throws SQLException any sql errors */ @@ -45,6 +49,7 @@ public interface OutputBuffer { /** * Get all lines from output buffer and return it as a list of strings. + * * @param conn DB connection * @return the lines * @throws SQLException any sql errors diff --git a/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java b/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java index c4b1afe..e29716e 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java +++ b/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java @@ -12,51 +12,54 @@ public class OutputBufferProvider { - /** Returns an OutputBuffer compatible with the given databaseVersion + private OutputBufferProvider() { + } + + /** + * Returns an OutputBuffer compatible with the given databaseVersion * If we are at 3.1.0 or greater, returns an OutputBuffer based upon the information whether the Reporter has Output or not * - * @param databaseVersion - * @param reporter - * @param conn - * @return - * @throws SQLException + * @param databaseVersion {@link Version} + * @param reporter {@link Reporter} + * @param conn {@link Connection} + * @return OutputBuffer + * @throws SQLException if there are problems with the database access */ - public static OutputBuffer getCompatibleOutputBuffer(Version databaseVersion, Reporter reporter, Connection conn ) throws SQLException { - OracleConnection oraConn = conn.unwrap(OracleConnection.class); + public static OutputBuffer getCompatibleOutputBuffer(Version databaseVersion, Reporter reporter, Connection conn) throws SQLException { + OracleConnection oraConn = conn.unwrap(OracleConnection.class); - try { - if (databaseVersion.isGreaterOrEqualThan(Version.V3_1_0)) { - if ( hasOutput(reporter, oraConn) ) { - return new DefaultOutputBuffer(reporter); - } - else { - return new NonOutputBuffer(reporter); - } - } - } - catch ( InvalidVersionException ignored ) { } + try { + if (databaseVersion.isGreaterOrEqualThan(Version.V3_1_0)) { + if (hasOutput(reporter, oraConn)) { + return new DefaultOutputBuffer(reporter); + } else { + return new NonOutputBuffer(reporter); + } + } + } catch (InvalidVersionException ignored) { + } - // If we couldn't find an appropriate OutputBuffer, return the Pre310-Compatibility-Buffer - return new CompatibilityOutputBufferPre310(reporter); + // If we couldn't find an appropriate OutputBuffer, return the Pre310-Compatibility-Buffer + return new CompatibilityOutputBufferPre310(reporter); } - private static boolean hasOutput( Reporter reporter, OracleConnection oraConn ) throws SQLException { + private static boolean hasOutput(Reporter reporter, OracleConnection oraConn) throws SQLException { String sql = "declare " + - " l_result int;" + - "begin " + - " begin " + - " execute immediate '" + - " begin " + - " :x := case ' || dbms_assert.simple_sql_name( ? ) || '() is of (ut_output_reporter_base) when true then 1 else 0 end;" + - " end;'" + - " using out l_result;" + - " end;" + - " ? := l_result;" + - "end;"; + " l_result int;" + + "begin " + + " begin " + + " execute immediate '" + + " begin " + + " :x := case ' || dbms_assert.simple_sql_name( ? ) || '() is of (ut_output_reporter_base) when true then 1 else 0 end;" + + " end;'" + + " using out l_result;" + + " end;" + + " ? := l_result;" + + "end;"; - try ( CallableStatement stmt = oraConn.prepareCall(sql)) { + try (CallableStatement stmt = oraConn.prepareCall(sql)) { stmt.setQueryTimeout(3); stmt.setString(1, reporter.getTypeName()); stmt.registerOutParameter(2, OracleTypes.INTEGER); @@ -66,7 +69,4 @@ private static boolean hasOutput( Reporter reporter, OracleConnection oraConn ) return result == 1; } } - - private OutputBufferProvider() { - } } diff --git a/src/main/java/org/utplsql/api/reporter/CoreReporters.java b/src/main/java/org/utplsql/api/reporter/CoreReporters.java index 3c0f7ff..a2b9f1f 100644 --- a/src/main/java/org/utplsql/api/reporter/CoreReporters.java +++ b/src/main/java/org/utplsql/api/reporter/CoreReporters.java @@ -3,26 +3,32 @@ import org.utplsql.api.Version; import org.utplsql.api.exception.InvalidVersionException; -/** This enum defines default reporters, added and maintained by the utPLSQL team, +/** + * This enum defines default reporters, added and maintained by the utPLSQL team, * and information since (and maybe until) which version they exist * * @author pesse */ public enum CoreReporters { + UT_COVERAGE_COBERTURA_REPORTER(Version.V3_1_0, null), UT_COVERAGE_HTML_REPORTER(Version.V3_0_0, null), - UT_DOCUMENTATION_REPORTER(Version.V3_0_0, null), - UT_TEAMCITY_REPORTER(Version.V3_0_0, null), - UT_XUNIT_REPORTER(Version.V3_0_0, null), - UT_COVERALLS_REPORTER(Version.V3_0_0, null), UT_COVERAGE_SONAR_REPORTER(Version.V3_0_0, null), + UT_COVERALLS_REPORTER(Version.V3_0_0, null), + UT_DEBUG_REPORTER(Version.V3_1_4, null), + UT_DOCUMENTATION_REPORTER(Version.V3_0_0, null), + UT_JUNIT_REPORTER(Version.V3_1_0, null), + UT_REALTIME_REPORTER(Version.V3_1_4, null), UT_SONAR_TEST_REPORTER(Version.V3_0_0, null), - UT_COVERAGE_COBERTURA_REPORTER(Version.V3_1_0, null); + UT_TEAMCITY_REPORTER(Version.V3_0_0, null), + UT_TFS_JUNIT_REPORTER(Version.V3_1_0, null), + @Deprecated + UT_XUNIT_REPORTER(Version.V3_0_0, null); private final Version since; private final Version until; - CoreReporters(Version since, Version until ) { + CoreReporters(Version since, Version until) { this.since = since; this.until = until; } @@ -35,18 +41,20 @@ public Version getUntil() { return until; } - /** Checks whether a CoreReporter is valid for the given databaseVersion + /** + * Checks whether a CoreReporter is valid for the given databaseVersion * * @param databaseVersion Database-Version * @return true or false */ - public boolean isAvailableFor( Version databaseVersion ) { + public boolean isAvailableFor(Version databaseVersion) { try { if ((since == null || databaseVersion.isGreaterOrEqualThan(since)) - && (until == null || databaseVersion.isLessOrEqualThan(until))) + && (until == null || databaseVersion.isLessOrEqualThan(until))) { return true; + } + } catch (InvalidVersionException ignored) { } - catch ( InvalidVersionException ignored ) { } return false; } diff --git a/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java b/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java index 4168332..2b1ebb4 100644 --- a/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java +++ b/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java @@ -2,15 +2,8 @@ import org.utplsql.api.ResourceUtil; -import java.io.IOException; -import java.io.InputStream; -import java.net.URISyntaxException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; -import java.util.function.Consumer; public class CoverageHTMLReporter extends DefaultReporter { @@ -29,21 +22,13 @@ public CoverageHTMLReporter(String selfType, Object[] attributes) { super(selfType, attributes); } - @Override - protected void setAttributes(Object[] attributes) { - super.setAttributes(attributes); - - if ( attributes != null ) { - if ( attributes[3] != null ) - projectName = String.valueOf(attributes[3]); - else - projectName = null; - - if ( attributes[4] != null ) - assetsPath = String.valueOf(attributes[4]); - else - assetsPath = null; - } + /** + * Write the bundled assets necessary for the HTML Coverage report to a given targetPath + * + * @param targetDirectory Directory where the assets should be stored + */ + protected static void writeReportAssetsTo(Path targetDirectory) { + ResourceUtil.copyResources(Paths.get("CoverageHTMLReporter"), targetDirectory); } @Override @@ -56,6 +41,25 @@ protected Object[] getAttributes() { return attributes; } + @Override + protected void setAttributes(Object[] attributes) { + super.setAttributes(attributes); + + if (attributes != null) { + if (attributes[3] != null) { + projectName = String.valueOf(attributes[3]); + } else { + projectName = null; + } + + if (attributes[4] != null) { + assetsPath = String.valueOf(attributes[4]); + } else { + assetsPath = null; + } + } + } + public String getProjectName() { return projectName; } @@ -72,63 +76,4 @@ public void setAssetsPath(String assetsPath) { this.assetsPath = assetsPath; } - /** Copies files from Classpath to a target directory. - * Can omit the first x folders of the asset-path when copying to the target directory - * - * @param assetPath Path of the asset in the classpath - * @param targetDirectory Target directory to copy the asset to - * @param filterNumOfFolders Omits the first x folders of the path when copying the asset to the target directory - * @throws IOException - */ - private static void copyFileFromClasspath( Path assetPath, Path targetDirectory, int filterNumOfFolders ) throws IOException { - - Path assetStartPath = assetPath.subpath(filterNumOfFolders, assetPath.getNameCount()); - Path targetAssetPath = targetDirectory.resolve(Paths.get(assetStartPath.toString())); - - Files.createDirectories(targetAssetPath.getParent()); - - try (InputStream is = CoverageHTMLReporter.class.getClassLoader() - .getResourceAsStream(assetPath.toString()) - ) { - Files.copy( is, targetAssetPath, StandardCopyOption.REPLACE_EXISTING); - } - } - - /** Write the bundled assets necessary for the HTML Coverage report to a given targetPath - * - * @param targetDirectory Directory where the assets should be stored - * @throws RuntimeException - */ - public static void writeReportAssetsTo(Path targetDirectory) throws RuntimeException { - - try { - Files.createDirectories(targetDirectory); - - List paths = ResourceUtil.getListOfChildren(Paths.get("CoverageHTMLReporter"), true); - - paths.forEach((ThrowingConsumer) p -> copyFileFromClasspath(p, targetDirectory, 1) ); - } - catch ( IOException | URISyntaxException e ) { - throw new RuntimeException(e); - } - } - - /** Functional Interface just to throw Exception from Consumer - * - * @param - */ - @FunctionalInterface - public interface ThrowingConsumer extends Consumer { - - @Override - default void accept(final T elem) { - try { - acceptThrows(elem); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - void acceptThrows( T t ) throws IOException; - } } diff --git a/src/main/java/org/utplsql/api/reporter/DefaultReporter.java b/src/main/java/org/utplsql/api/reporter/DefaultReporter.java index 073115d..b478355 100644 --- a/src/main/java/org/utplsql/api/reporter/DefaultReporter.java +++ b/src/main/java/org/utplsql/api/reporter/DefaultReporter.java @@ -5,18 +5,19 @@ import java.sql.SQLException; -/** This is a basic Reporter implementation, using ORAData interface +/** + * This is a basic Reporter implementation, using ORAData interface * * @author pesse */ public class DefaultReporter extends Reporter { - public DefaultReporter(String typeName, Object[] attributes ) { + public DefaultReporter(String typeName, Object[] attributes) { super(typeName, attributes); } @Override - protected void initOutputBuffer( OracleConnection oraConn, CompatibilityProxy compatibilityProxy ) throws SQLException { + protected void initOutputBuffer(OracleConnection oraConn, CompatibilityProxy compatibilityProxy) throws SQLException { outputBuffer = compatibilityProxy.getOutputBuffer(this, oraConn); } } diff --git a/src/main/java/org/utplsql/api/reporter/DefaultReporterFactoryMethodRegistrator.java b/src/main/java/org/utplsql/api/reporter/DefaultReporterFactoryMethodRegistrator.java index 8760f55..f1880df 100644 --- a/src/main/java/org/utplsql/api/reporter/DefaultReporterFactoryMethodRegistrator.java +++ b/src/main/java/org/utplsql/api/reporter/DefaultReporterFactoryMethodRegistrator.java @@ -2,13 +2,14 @@ import org.utplsql.api.compatibility.CompatibilityProxy; -/** Helper-class which registers default ReporterFactoryMethods based on the given databaseVersion +/** + * Helper-class which registers default ReporterFactoryMethods based on the given databaseVersion * * @author pesse */ class DefaultReporterFactoryMethodRegistrator { - public static void registerDefaultReporters(ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy ) { + public static void registerDefaultReporters(ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy) { // At the moment we don't have version-specific reporters which need a special MethodFactory reporterFactory.registerReporterFactoryMethod(CoreReporters.UT_DOCUMENTATION_REPORTER.name(), DocumentationReporter::new, "Provides additional properties lvl and failed"); diff --git a/src/main/java/org/utplsql/api/reporter/DocumentationReporter.java b/src/main/java/org/utplsql/api/reporter/DocumentationReporter.java index 19edd01..6e36613 100644 --- a/src/main/java/org/utplsql/api/reporter/DocumentationReporter.java +++ b/src/main/java/org/utplsql/api/reporter/DocumentationReporter.java @@ -8,23 +8,13 @@ public class DocumentationReporter extends DefaultReporter { private int failed; public DocumentationReporter() { - super( CoreReporters.UT_DOCUMENTATION_REPORTER.name(), null ); + super(CoreReporters.UT_DOCUMENTATION_REPORTER.name(), null); } - public DocumentationReporter(String selfType, Object[] attributes ) { + public DocumentationReporter(String selfType, Object[] attributes) { super(selfType, attributes); } - @Override - protected void setAttributes(Object[] attributes) { - super.setAttributes(attributes); - - if ( attributes != null ) { - lvl = ((BigDecimal)attributes[3]).intValue(); - failed = ((BigDecimal)attributes[4]).intValue(); - } - } - @Override protected Object[] getAttributes() { Object[] attributes = super.getAttributes(); @@ -33,6 +23,16 @@ protected Object[] getAttributes() { return attributes; } + @Override + protected void setAttributes(Object[] attributes) { + super.setAttributes(attributes); + + if (attributes != null) { + lvl = ((BigDecimal) attributes[3]).intValue(); + failed = ((BigDecimal) attributes[4]).intValue(); + } + } + public int getLvl() { return lvl; } diff --git a/src/main/java/org/utplsql/api/reporter/Reporter.java b/src/main/java/org/utplsql/api/reporter/Reporter.java index 639e0c6..6e91062 100644 --- a/src/main/java/org/utplsql/api/reporter/Reporter.java +++ b/src/main/java/org/utplsql/api/reporter/Reporter.java @@ -10,48 +10,44 @@ import org.utplsql.api.compatibility.CompatibilityProxy; import org.utplsql.api.outputBuffer.OutputBuffer; -import javax.xml.bind.DatatypeConverter; import java.sql.Connection; import java.sql.SQLException; -/** This is a basic Reporter implementation, using ORAData interface +/** + * This is a basic Reporter implementation, using ORAData interface * * @author pesse */ public abstract class Reporter implements ORAData { private static final Logger logger = LoggerFactory.getLogger(Reporter.class); - + protected OutputBuffer outputBuffer; private String selfType; private String id; private Object[] attributes; private boolean init = false; - protected OutputBuffer outputBuffer; - public Reporter( String typeName, Object[] attributes ) { + public Reporter(String typeName, Object[] attributes) { setTypeName(typeName); - setAttributes( attributes ); - } - - protected void setTypeName( String typeName ) { - this.selfType = typeName.replaceAll("[^0-9a-zA-Z_\\.]", ""); + setAttributes(attributes); } + public Reporter init(Connection con, CompatibilityProxy compatibilityProxy, ReporterFactory reporterFactory) throws SQLException { - public Reporter init(Connection con, CompatibilityProxy compatibilityProxy, ReporterFactory reporterFactory ) throws SQLException { - - if ( compatibilityProxy == null ) + if (compatibilityProxy == null) { compatibilityProxy = new CompatibilityProxy(con); - if ( reporterFactory == null ) + } + if (reporterFactory == null) { reporterFactory = new ReporterFactory(); + } OracleConnection oraConn = con.unwrap(OracleConnection.class); - initDbReporter( oraConn, reporterFactory ); + initDbReporter(oraConn, reporterFactory); init = true; - initOutputBuffer( oraConn, compatibilityProxy); + initOutputBuffer(oraConn, compatibilityProxy); return this; } @@ -60,16 +56,17 @@ public Reporter init(Connection con) throws SQLException { return init(con, null, null); } - protected abstract void initOutputBuffer( OracleConnection oraConn, CompatibilityProxy compatibilityProxy ) throws SQLException; + protected abstract void initOutputBuffer(OracleConnection oraConn, CompatibilityProxy compatibilityProxy) throws SQLException; - /** Initializes the Reporter from database + /** + * Initializes the Reporter from database * This is necessary because we set up DefaultOutputBuffer (and maybe other stuff) we don't want to know and care about * in the java API. Let's just do the instantiation of the Reporter in the database and map it into this object. * - * @param oraConn - * @throws SQLException + * @param oraConn {@link OracleConnection} + * @throws SQLException if there are problems with the database access */ - private void initDbReporter( OracleConnection oraConn, ReporterFactory reporterFactory ) throws SQLException { + private void initDbReporter(OracleConnection oraConn, ReporterFactory reporterFactory) throws SQLException { OracleCallableStatement callableStatement = (OracleCallableStatement) oraConn.prepareCall("{? = call " + selfType + "()}"); callableStatement.registerOutParameter(1, OracleTypes.STRUCT, "UT_REPORTER_BASE"); callableStatement.execute(); @@ -81,17 +78,17 @@ private void initDbReporter( OracleConnection oraConn, ReporterFactory reporterF logger.debug("Database-reporter initialized, Type: {}, ID: {}", selfType, id); } - protected void setAttributes(Object[] attributes ) { + protected Object[] getAttributes() { + return attributes; + } + + protected void setAttributes(Object[] attributes) { if (attributes != null) { - this.id = DatatypeConverter.printHexBinary((byte[])attributes[1]); + this.id = DatatypeConverter.printHexBinary((byte[]) attributes[1]); } this.attributes = attributes; } - protected Object[] getAttributes() { - return attributes; - } - public boolean isInit() { return init; } @@ -100,17 +97,33 @@ public String getTypeName() { return this.selfType; } + protected void setTypeName(String typeName) { + this.selfType = typeName.replaceAll("[^0-9a-zA-Z_\\.]", ""); + } + public String getId() { return this.id; } - public Datum toDatum(Connection c) throws SQLException - { - return (Datum)c.createStruct(getTypeName(), getAttributes()); + public Datum toDatum(Connection c) throws SQLException { + return (Datum) c.createStruct(getTypeName(), getAttributes()); } public OutputBuffer getOutputBuffer() { return outputBuffer; } + + private static class DatatypeConverter { + private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); + + static String printHexBinary(byte[] data) { + StringBuilder r = new StringBuilder(data.length * 2); + for (byte b : data) { + r.append(hexCode[(b >> 4) & 0xF]); + r.append(hexCode[(b & 0xF)]); + } + return r.toString(); + } + } } diff --git a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java index 510896b..b569127 100644 --- a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java +++ b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java @@ -12,67 +12,87 @@ import java.util.Map; import java.util.function.BiFunction; -/** This class manages the instantiation of reporters. +/** + * This class manages the instantiation of reporters. * One can register a supplier method for a specific name which will then be callable via createReporter(name) - * + *

* Use the static createEmpty or createDefault methods to get a new instance. * We don't allow direct instantiation because we want *

    - *
  • Register default ReporterFactoryMethods for Core-Reporters
  • - *
  • Be able to add more than one ReporterFactory implementation due to backwards-compatibility in future
  • + *
  • Register default ReporterFactoryMethods for Core-Reporters
  • + *
  • Be able to add more than one ReporterFactory implementation due to backwards-compatibility in future
  • *
* * @author pesse */ public final class ReporterFactory implements ORADataFactory { - public static class ReporterFactoryMethodInfo { - public ReporterFactoryMethodInfo(BiFunction factoryMethod, String description) { - this.factoryMethod = factoryMethod; - this.description = description; - } - public final BiFunction factoryMethod; - public final String description; + private final Map reportFactoryMethodMap = new HashMap<>(); + + ReporterFactory() { } - private final Map reportFactoryMethodMap = new HashMap<>(); + /** + * Returns a new instance of an empty ReporterFactory with no registered ReporterFactoryMethods + * Normally, you should be using createDefault-method instead. + * + * @return a new ReporterFactory instance + */ + public static ReporterFactory createEmpty() { + return new ReporterFactory(); + } - ReporterFactory() { } + /** + * Returns a new instance of a ReporterFactory with the default ReporterFactoryMethods registered. + * This can depend on the version of utPLSQL, therefore you have to provide a CompatibilityProxy + * + * @param proxy Compatibility proxy + * @return a new ReporterFactory instance with all default ReporterFactoryMethods registered + */ + public static ReporterFactory createDefault(CompatibilityProxy proxy) { + ReporterFactory reporterFactory = new ReporterFactory(); + DefaultReporterFactoryMethodRegistrator.registerDefaultReporters(reporterFactory, proxy); + return reporterFactory; + } - /** Registers a creation method for a specified reporter name. Overrides eventually existing creation method + /** + * Registers a creation method for a specified reporter name. Overrides eventually existing creation method * - * @param reporterName the reporter's name to register + * @param reporterName the reporter's name to register * @param factoryMethod the method which will return the reporter - * @param description the description of the reporter + * @param description the description of the reporter * @return Object with information about the registered reporter */ public synchronized ReporterFactoryMethodInfo registerReporterFactoryMethod(String reporterName, BiFunction factoryMethod, String description) { return reportFactoryMethodMap.put(reporterName.toUpperCase(), new ReporterFactoryMethodInfo(factoryMethod, description)); } - /** Unregisters a specified reporter name. + /** + * Unregisters a specified reporter name. * * @param reporterName the reporter's name to unregister * @return information about the reporter which was previously registered or null */ - public synchronized ReporterFactoryMethodInfo unregisterReporterFactoryMethod(String reporterName ) { + public synchronized ReporterFactoryMethodInfo unregisterReporterFactoryMethod(String reporterName) { return reportFactoryMethodMap.remove(reporterName.toUpperCase()); } - /** Checks whether a given reporter has a registered FactoryMethod or not + /** + * Checks whether a given reporter has a registered FactoryMethod or not * * @param reporterName the reporter's name * @return true or false */ - public synchronized boolean hasRegisteredFactoryMethodFor( String reporterName ) { + public synchronized boolean hasRegisteredFactoryMethodFor(String reporterName) { return reportFactoryMethodMap.containsKey(reporterName.toUpperCase()); } - /** Returns a new reporter of the given name. + /** + * Returns a new reporter of the given name. * If no specific ReporterFactoryMethod is registered, returns a default {Reporter} * * @param reporterName the reporter's name to create a new instance of - * @param attributes attributes from STRUCT + * @param attributes attributes from STRUCT * @return A reporter */ public Reporter createReporter(String reporterName, @Nullable Object[] attributes) { @@ -80,31 +100,38 @@ public Reporter createReporter(String reporterName, @Nullable Object[] attribute reporterName = reporterName.toUpperCase(); BiFunction supplier = DefaultReporter::new; - if ( reportFactoryMethodMap.containsKey(reporterName)) { + if (reportFactoryMethodMap.containsKey(reporterName)) { ReporterFactoryMethodInfo ri = reportFactoryMethodMap.get(reporterName); - if (ri == null) + if (ri == null) { throw new RuntimeException("ReporterFactoryMethodInfo for " + reporterName + " was null"); + } supplier = ri.factoryMethod; } - if ( supplier == null ) + if (supplier == null) { throw new RuntimeException("No factory method for " + reporterName); + } - return supplier.apply( reporterName, attributes ); + return supplier.apply(reporterName, attributes); } - /** Returns a new reporter of the given name (or should do so). + /** + * Returns a new reporter of the given name (or should do so). * If no specific ReporterFactoryMethod is registered, returns a default {Reporter} + * + * @param reporterName Name of the reporter + * @return Reporter */ - public Reporter createReporter( String reporterName ) { + public Reporter createReporter(String reporterName) { return createReporter(reporterName, null); } - /** Returns a set of all registered reporter's names + /** + * Returns a set of all registered reporter's names * - * @return Set of reporter names + * @return Map of reporter names */ public Map getRegisteredReporterInfo() { Map descMap = new HashMap<>(reportFactoryMethodMap.size()); @@ -118,32 +145,21 @@ public Map getRegisteredReporterInfo() { @Override public ORAData create(Datum d, int sqlType) throws SQLException { if (d == null) return null; - if ( d instanceof Struct) { - String sqlName = ((Struct)d).getSQLTypeName(); - return createReporter(sqlName, ((Struct)d).getAttributes()); + if (d instanceof Struct) { + String sqlName = ((Struct) d).getSQLTypeName(); + return createReporter(sqlName, ((Struct) d).getAttributes()); } return null; } - /** Returns a new instance of an empty ReporterFactory with no registered ReporterFactoryMethods - * Normally, you should be using createDefault-method instead. - * - * @return a new ReporterFactory instance - */ - public static ReporterFactory createEmpty() { - return new ReporterFactory(); - } + public static class ReporterFactoryMethodInfo { + public final BiFunction factoryMethod; + public final String description; - /** Returns a new instance of a ReporterFactory with the default ReporterFactoryMethods registered. - * This can depend on the version of utPLSQL, therefore you have to provide a CompatibilityProxy - * - * @param proxy Compatibility proxy - * @return a new ReporterFactory instance with all default ReporterFactoryMethods registered - */ - public static ReporterFactory createDefault(CompatibilityProxy proxy) { - ReporterFactory reporterFactory = new ReporterFactory(); - DefaultReporterFactoryMethodRegistrator.registerDefaultReporters(reporterFactory, proxy); - return reporterFactory; + public ReporterFactoryMethodInfo(BiFunction factoryMethod, String description) { + this.factoryMethod = factoryMethod; + this.description = description; + } } } diff --git a/src/main/java/org/utplsql/api/reporter/inspect/AbstractReporterInspector.java b/src/main/java/org/utplsql/api/reporter/inspect/AbstractReporterInspector.java index 11a8798..bbab916 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/AbstractReporterInspector.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/AbstractReporterInspector.java @@ -4,12 +4,12 @@ import java.sql.Connection; -abstract class AbstractReporterInspector implements ReporterInspector { +abstract class AbstractReporterInspector implements ReporterInspector { protected final ReporterFactory reporterFactory; protected final Connection connection; - AbstractReporterInspector(ReporterFactory reporterFactory, Connection conn ) { + AbstractReporterInspector(ReporterFactory reporterFactory, Connection conn) { this.reporterFactory = reporterFactory; this.connection = conn; } diff --git a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInfo.java b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInfo.java index a987bdd..fb805d5 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInfo.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInfo.java @@ -1,20 +1,17 @@ package org.utplsql.api.reporter.inspect; -/** Holds information about utPLSQL Reporter-Types +/** + * Holds information about utPLSQL Reporter-Types * * @author pesse */ public class ReporterInfo { - public enum Type { - SQL, JAVA, SQL_WITH_JAVA - } - private final String name; private final Type type; private final String description; - ReporterInfo( String name, Type type, String description ) { + ReporterInfo(String name, Type type, String description) { this.name = name; this.type = type; this.description = description; @@ -31,4 +28,8 @@ public Type getType() { public String getDescription() { return description; } + + public enum Type { + SQL, JAVA, SQL_WITH_JAVA + } } diff --git a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java index 436b939..878e3b0 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java @@ -19,28 +19,30 @@ */ public interface ReporterInspector { - List getReporterInfos(); - - default Map getReporterInfoMap() { - return getReporterInfos().stream().collect(Collectors.toMap(ReporterInfo::getName, Function.identity())); - } - /** * Returns a new instance of a ReporterInspector, based on the utPLSQL version used in the connection * - * @param reporterFactory - * @param conn - * @return - * @throws SQLException + * @param reporterFactory {@link ReporterFactory} + * @param conn {@link Connection} + * @return ReporterInspector + * @throws SQLException if there are problems with the database access + * @throws InvalidVersionException if version is not valid */ static ReporterInspector create(ReporterFactory reporterFactory, Connection conn) throws SQLException, InvalidVersionException { CompatibilityProxy proxy = new CompatibilityProxy(conn); - if (proxy.getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_0)) + if (proxy.getUtPlsqlVersion().isGreaterOrEqualThan(Version.V3_1_0)) { return new ReporterInspector310(reporterFactory, conn); - else + } else { return new ReporterInspectorPre310(reporterFactory, conn); + } + } + + List getReporterInfos(); + + default Map getReporterInfoMap() { + return getReporterInfos().stream().collect(Collectors.toMap(ReporterInfo::getName, Function.identity())); } } diff --git a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector310.java b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector310.java index cd1b8ea..091e0f4 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector310.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector310.java @@ -13,7 +13,8 @@ import java.sql.SQLException; import java.util.*; -/** ReporterInspector for v3.1.0 upwards +/** + * ReporterInspector for v3.1.0 upwards * * @author pesse */ @@ -22,15 +23,16 @@ class ReporterInspector310 extends AbstractReporterInspector { private final Map registeredReporterFactoryMethods; private final Set infos; - ReporterInspector310(ReporterFactory reporterFactory, Connection conn ) throws SQLException { + ReporterInspector310(ReporterFactory reporterFactory, Connection conn) throws SQLException { super(reporterFactory, conn); registeredReporterFactoryMethods = reporterFactory.getRegisteredReporterInfo(); Set reporterInfos = new HashSet<>(); try (PreparedStatement stmt = connection.prepareStatement("select * from table(ut_runner.get_reporters_list) order by 1")) { - try (ResultSet rs = stmt.executeQuery() ) { - while (rs.next()) + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { reporterInfos.add(getReporterInfo(rs.getString(1))); + } } } this.infos = reporterInfos; @@ -42,13 +44,13 @@ public List getReporterInfos() { return new ArrayList<>(infos); } - private ReporterInfo getReporterInfo(String reporterNameWithOwner ) throws SQLException { - String reporterName = reporterNameWithOwner.substring(reporterNameWithOwner.indexOf(".")+1).toUpperCase(); + private ReporterInfo getReporterInfo(String reporterNameWithOwner) throws SQLException { + String reporterName = reporterNameWithOwner.substring(reporterNameWithOwner.indexOf(".") + 1).toUpperCase(); ReporterInfo.Type type = ReporterInfo.Type.SQL; String description = getDescription(reporterName); - if ( registeredReporterFactoryMethods.containsKey(reporterName) ) { + if (registeredReporterFactoryMethods.containsKey(reporterName)) { type = ReporterInfo.Type.SQL_WITH_JAVA; description += "\n" + registeredReporterFactoryMethods.get(reporterName); } @@ -56,7 +58,7 @@ private ReporterInfo getReporterInfo(String reporterNameWithOwner ) throws SQLEx return new ReporterInfo(reporterName, type, description); } - private String getDescription( String reporterName ) throws SQLException { + private String getDescription(String reporterName) throws SQLException { CompatibilityProxy compatibilityProxy = new CompatibilityProxy(connection); Reporter reporter = reporterFactory.createReporter(reporterName).init(connection, compatibilityProxy, reporterFactory); OracleConnection oraCon = connection.unwrap(OracleConnection.class); diff --git a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java index f188ac9..40d85fd 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java @@ -21,7 +21,7 @@ class ReporterInspectorPre310 extends AbstractReporterInspector { registeredReporterFactoryMethods = reporterFactory.getRegisteredReporterInfo(); initDefaultDescriptions(); - Version databaseVersion = new CompatibilityProxy(connection).getDatabaseVersion(); + Version databaseVersion = new CompatibilityProxy(connection).getUtPlsqlVersion(); this.infos = Arrays.stream(CoreReporters.values()) .filter(r -> r.isAvailableFor(databaseVersion)) .map(this::getReporterInfo) @@ -35,7 +35,7 @@ private void initDefaultDescriptions() { descriptions.put(CoreReporters.UT_DOCUMENTATION_REPORTER, ""); descriptions.put(CoreReporters.UT_SONAR_TEST_REPORTER, ""); descriptions.put(CoreReporters.UT_TEAMCITY_REPORTER, ""); - descriptions.put(CoreReporters.UT_XUNIT_REPORTER, ""); + descriptions.put(CoreReporters.UT_JUNIT_REPORTER, ""); } @Override diff --git a/src/main/java/org/utplsql/api/testRunner/AbstractTestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/AbstractTestRunnerStatement.java deleted file mode 100644 index 147065a..0000000 --- a/src/main/java/org/utplsql/api/testRunner/AbstractTestRunnerStatement.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.utplsql.api.testRunner; - -import oracle.jdbc.OracleConnection; -import org.utplsql.api.CustomTypes; -import org.utplsql.api.FileMapper; -import org.utplsql.api.FileMapping; -import org.utplsql.api.TestRunnerOptions; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; - -/** - * Abstract class which creates a callable statement for running tests - * The SQL to be used has to be implemented for there are differences between the Framework-versions - * - * @author pesse - */ -abstract class AbstractTestRunnerStatement implements TestRunnerStatement { - - protected final TestRunnerOptions options; - private final Connection conn; - protected final CallableStatement callableStatement; - - public AbstractTestRunnerStatement(TestRunnerOptions options, Connection conn) throws SQLException { - this.options = options; - this.conn = conn; - - callableStatement = conn.prepareCall(getSql()); - - createStatement(); - } - - protected abstract String getSql(); - - protected int createStatement() throws SQLException { - - OracleConnection oraConn = conn.unwrap(OracleConnection.class); - - int paramIdx = 0; - - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.pathList.toArray())); - - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_REPORTERS, options.reporterList.toArray())); - - if (options.coverageSchemes.isEmpty()) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_VARCHAR2_LIST); - } else { - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.coverageSchemes.toArray())); - } - - if (options.sourceMappingOptions == null) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_FILE_MAPPINGS); - } else { - List sourceMappings = FileMapper.buildFileMappingList(conn, options.sourceMappingOptions); - - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_FILE_MAPPINGS, sourceMappings.toArray())); - } - - if (options.testMappingOptions == null) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_FILE_MAPPINGS); - } else { - List sourceMappings = FileMapper.buildFileMappingList(conn, options.testMappingOptions); - - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_FILE_MAPPINGS, sourceMappings.toArray())); - } - - if (options.includeObjects.isEmpty()) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_VARCHAR2_LIST); - } else { - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.includeObjects.toArray())); - } - - if (options.excludeObjects.isEmpty()) { - callableStatement.setNull(++paramIdx, Types.ARRAY, CustomTypes.UT_VARCHAR2_LIST); - } else { - callableStatement.setArray( - ++paramIdx, oraConn.createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.excludeObjects.toArray())); - } - - return paramIdx; - } - - public void execute() throws SQLException { - callableStatement.execute(); - } - - @Override - public void close() throws SQLException { - if (callableStatement != null) - callableStatement.close(); - } -} diff --git a/src/main/java/org/utplsql/api/testRunner/ActualTestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/ActualTestRunnerStatement.java deleted file mode 100644 index 05a05ce..0000000 --- a/src/main/java/org/utplsql/api/testRunner/ActualTestRunnerStatement.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.utplsql.api.testRunner; - -import org.utplsql.api.TestRunnerOptions; - -import java.sql.Connection; -import java.sql.SQLException; - -/** Provides the call to run tests for the most actual Framework version. - * Includes fail on error - * - * @author pesse - */ -class ActualTestRunnerStatement extends AbstractTestRunnerStatement { - - public ActualTestRunnerStatement(TestRunnerOptions options, Connection connection ) throws SQLException { - super( options, connection); - } - - @Override - protected String getSql() { - // Workaround because Oracle JDBC doesn't support passing boolean to stored procedures. - String colorConsoleStr = Boolean.toString(options.colorConsole); - String failOnErrors = Boolean.toString(options.failOnErrors); - - return - "BEGIN " + - "ut_runner.run(" + - "a_paths => ?, " + - "a_reporters => ?, " + - "a_color_console => " + colorConsoleStr + ", " + - "a_coverage_schemes => ?, " + - "a_source_file_mappings => ?, " + - "a_test_file_mappings => ?, " + - "a_include_objects => ?, " + - "a_exclude_objects => ?, " + - "a_fail_on_errors => " + failOnErrors + ", " + - "a_client_character_set => ?); " + - "END;"; - } - - @Override - protected int createStatement() throws SQLException { - int curParamIdx = super.createStatement(); - - callableStatement.setString(++curParamIdx, options.clientCharacterSet); - - return curParamIdx; - } -} diff --git a/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java new file mode 100644 index 0000000..71fe893 --- /dev/null +++ b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java @@ -0,0 +1,109 @@ +package org.utplsql.api.testRunner; + +import oracle.jdbc.OracleConnection; +import org.utplsql.api.CustomTypes; +import org.utplsql.api.TestRunnerOptions; +import org.utplsql.api.Version; +import org.utplsql.api.compatibility.OptionalFeatures; +import org.utplsql.api.db.DynamicParameterList; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.SQLException; + +public class DynamicTestRunnerStatement implements TestRunnerStatement { + + private CallableStatement stmt; + private final OracleConnection oracleConnection; + private final Version utPlSQlVersion; + private final TestRunnerOptions options; + private final DynamicParameterList dynamicParameterList; + + private DynamicTestRunnerStatement(Version utPlSQlVersion, OracleConnection connection, TestRunnerOptions options, CallableStatement statement) throws SQLException { + this.utPlSQlVersion = utPlSQlVersion; + this.oracleConnection = connection; + this.options = options; + this.stmt = statement; + + this.dynamicParameterList = initParameterList(); + + prepareStatement(); + } + + private DynamicParameterList initParameterList() throws SQLException { + + Object[] sourceMappings = (options.sourceMappingOptions != null) + ? FileMapper.buildFileMappingList(oracleConnection, options.sourceMappingOptions).toArray() + : null; + Object[] testMappings = (options.testMappingOptions != null) + ? FileMapper.buildFileMappingList(oracleConnection, options.testMappingOptions).toArray() + : null; + + DynamicParameterList.DynamicParameterListBuilder builder = DynamicParameterList.builder() + .addIfNotEmpty("a_paths", options.pathList.toArray(), CustomTypes.UT_VARCHAR2_LIST, oracleConnection) + .addIfNotEmpty("a_reporters", options.reporterList.toArray(), CustomTypes.UT_REPORTERS, oracleConnection) + .addIfNotEmpty("a_color_console", options.colorConsole) + .addIfNotEmpty("a_coverage_schemes", options.coverageSchemes.toArray(), CustomTypes.UT_VARCHAR2_LIST, oracleConnection) + .addIfNotEmpty("a_source_file_mappings", sourceMappings, CustomTypes.UT_FILE_MAPPINGS, oracleConnection) + .addIfNotEmpty("a_test_file_mappings", testMappings, CustomTypes.UT_FILE_MAPPINGS, oracleConnection) + .addIfNotEmpty("a_include_objects", options.includeObjects.toArray(), CustomTypes.UT_VARCHAR2_LIST, oracleConnection) + .addIfNotEmpty("a_exclude_objects", options.excludeObjects.toArray(), CustomTypes.UT_VARCHAR2_LIST, oracleConnection); + + if (OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(utPlSQlVersion)) { + builder.addIfNotEmpty("a_fail_on_errors", options.failOnErrors); + } + if (OptionalFeatures.CLIENT_CHARACTER_SET.isAvailableFor(utPlSQlVersion)) { + builder.addIfNotEmpty("a_client_character_set", options.clientCharacterSet); + } + if (OptionalFeatures.RANDOM_EXECUTION_ORDER.isAvailableFor(utPlSQlVersion)) { + builder.addIfNotEmpty("a_random_test_order", options.randomTestOrder) + .addIfNotEmpty("a_random_test_order_seed", options.randomTestOrderSeed); + } + if (OptionalFeatures.TAGS.isAvailableFor(utPlSQlVersion)) { + builder.addIfNotEmpty("a_tags", options.getTagsAsString()); + } + if (OptionalFeatures.EXPR.isAvailableFor(utPlSQlVersion)) { + builder.addIfNotEmpty("a_include_schema_expr", options.includeSchemaExpr) + .addIfNotEmpty("a_include_object_expr", options.includeObjectExpr) + .addIfNotEmpty("a_exclude_schema_expr", options.excludeSchemaExpr) + .addIfNotEmpty("a_exclude_object_expr", options.excludeObjectExpr); + } + + return builder.build(); + } + + private void prepareStatement() throws SQLException { + if (stmt == null) { + String sql = "BEGIN " + + "ut_runner.run(" + + dynamicParameterList.getSql() + + ");" + + "END;"; + stmt = oracleConnection.prepareCall(sql); + } + + dynamicParameterList.setParamsStartWithIndex(stmt, 1); + } + + @Override + public void execute() throws SQLException { + stmt.execute(); + } + + @Override + public String getSql() { + return dynamicParameterList.getSql(); + } + + @Override + public void close() throws SQLException { + if (stmt != null) { + stmt.close(); + } + } + + public static DynamicTestRunnerStatement forVersion(Version version, Connection connection, TestRunnerOptions options, CallableStatement statement) throws SQLException { + OracleConnection oraConn = connection.unwrap(OracleConnection.class); + return new DynamicTestRunnerStatement(version, oraConn, options, statement); + } +} diff --git a/src/main/java/org/utplsql/api/testRunner/FileMapper.java b/src/main/java/org/utplsql/api/testRunner/FileMapper.java new file mode 100644 index 0000000..b599a5a --- /dev/null +++ b/src/main/java/org/utplsql/api/testRunner/FileMapper.java @@ -0,0 +1,84 @@ +package org.utplsql.api.testRunner; + + +import oracle.jdbc.OracleConnection; +import oracle.jdbc.OracleTypes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.utplsql.api.CustomTypes; +import org.utplsql.api.FileMapperOptions; +import org.utplsql.api.FileMapping; +import org.utplsql.api.KeyValuePair; +import org.utplsql.api.db.DynamicParameterList; + +import java.sql.*; +import java.util.*; + +final class FileMapper { + + private static final Logger logger = LoggerFactory.getLogger(FileMapper.class); + + private FileMapper() { + } + + /** + * Call the database api to build the custom file mappings. + */ + private static Array buildFileMappingArray( + Connection conn, FileMapperOptions mapperOptions) throws SQLException { + OracleConnection oraConn = conn.unwrap(OracleConnection.class); + + Map> typeMap = conn.getTypeMap(); + typeMap.put(CustomTypes.UT_FILE_MAPPING, FileMapping.class); + typeMap.put(CustomTypes.UT_KEY_VALUE_PAIR, KeyValuePair.class); + conn.setTypeMap(typeMap); + + logger.debug("Building fileMappingArray"); + final Object[] filePathsArray = mapperOptions.getFilePaths().toArray(); + for ( Object elem : filePathsArray ) { + logger.debug("Path: " + elem); + } + Object[] typeMapArray = null; + if ( mapperOptions.getTypeMappings() != null ) { + typeMapArray = mapperOptions.getTypeMappings().toArray(); + } + + DynamicParameterList parameterList = DynamicParameterList.builder() + .add("a_file_paths", filePathsArray, CustomTypes.UT_VARCHAR2_LIST, oraConn) + .addIfNotEmpty("a_object_owner", mapperOptions.getObjectOwner()) + .addIfNotEmpty("a_file_to_object_type_mapping", typeMapArray, CustomTypes.UT_KEY_VALUE_PAIRS, oraConn) + .addIfNotEmpty("a_regex_pattern", mapperOptions.getRegexPattern()) + .addIfNotEmpty("a_object_owner_subexpression", mapperOptions.getOwnerSubExpression()) + .addIfNotEmpty("a_object_name_subexpression", mapperOptions.getNameSubExpression()) + .addIfNotEmpty("a_object_type_subexpression", mapperOptions.getTypeSubExpression()) + .build(); + + CallableStatement callableStatement = conn.prepareCall( + "BEGIN " + + "? := ut_file_mapper.build_file_mappings(" + + parameterList.getSql() + + "); " + + "END;"); + + int paramIdx = 0; + callableStatement.registerOutParameter(++paramIdx, OracleTypes.ARRAY, CustomTypes.UT_FILE_MAPPINGS); + + parameterList.setParamsStartWithIndex(callableStatement, ++paramIdx); + + callableStatement.execute(); + return callableStatement.getArray(1); + } + + static List buildFileMappingList( + Connection conn, FileMapperOptions mapperOptions) throws SQLException { + java.sql.Array fileMappings = buildFileMappingArray(conn, mapperOptions); + + List mappingList = new ArrayList<>(); + for (Object obj : (Object[]) fileMappings.getArray()) { + mappingList.add((FileMapping) obj); + } + + return mappingList; + } + +} diff --git a/src/main/java/org/utplsql/api/testRunner/Pre303TestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/Pre303TestRunnerStatement.java deleted file mode 100644 index b741459..0000000 --- a/src/main/java/org/utplsql/api/testRunner/Pre303TestRunnerStatement.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.utplsql.api.testRunner; - -import org.utplsql.api.TestRunnerOptions; - -import java.sql.Connection; -import java.sql.SQLException; - -/** TestRunner-Statement for Framework version before 3.0.3 - * Does not know about failOnErrors option - * - * @author pesse - */ -class Pre303TestRunnerStatement extends AbstractTestRunnerStatement { - - public Pre303TestRunnerStatement(TestRunnerOptions options, Connection conn) throws SQLException { - super(options, conn); - } - - @Override - protected String getSql() { - // Workaround because Oracle JDBC doesn't support passing boolean to stored procedures. - String colorConsoleStr = Boolean.toString(options.colorConsole); - - return "BEGIN " + - "ut_runner.run(" + - "a_paths => ?, " + - "a_reporters => ?, " + - "a_color_console => " + colorConsoleStr + ", " + - "a_coverage_schemes => ?, " + - "a_source_file_mappings => ?, " + - "a_test_file_mappings => ?, " + - "a_include_objects => ?, " + - "a_exclude_objects => ?); " + - "END;"; - } -} diff --git a/src/main/java/org/utplsql/api/testRunner/Pre312TestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/Pre312TestRunnerStatement.java deleted file mode 100644 index c8e5f5f..0000000 --- a/src/main/java/org/utplsql/api/testRunner/Pre312TestRunnerStatement.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.utplsql.api.testRunner; - -import org.utplsql.api.TestRunnerOptions; - -import java.sql.Connection; -import java.sql.SQLException; - -/** TestRunner-Statement for Framework version before 3.0.3 - * Does not know about client character set - * - * @author pesse - */ -class Pre312TestRunnerStatement extends AbstractTestRunnerStatement { - - public Pre312TestRunnerStatement(TestRunnerOptions options, Connection connection ) throws SQLException { - super( options, connection); - } - - @Override - protected String getSql() { - // Workaround because Oracle JDBC doesn't support passing boolean to stored procedures. - String colorConsoleStr = Boolean.toString(options.colorConsole); - String failOnErrors = Boolean.toString(options.failOnErrors); - - return - "BEGIN " + - "ut_runner.run(" + - "a_paths => ?, " + - "a_reporters => ?, " + - "a_color_console => " + colorConsoleStr + ", " + - "a_coverage_schemes => ?, " + - "a_source_file_mappings => ?, " + - "a_test_file_mappings => ?, " + - "a_include_objects => ?, " + - "a_exclude_objects => ?, " + - "a_fail_on_errors => " + failOnErrors + "); " + - "END;"; - } -} diff --git a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatement.java index 9a0bb48..dbc5b71 100644 --- a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatement.java +++ b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatement.java @@ -11,6 +11,8 @@ public interface TestRunnerStatement extends AutoCloseable { void execute() throws SQLException; + String getSql(); + @Override void close() throws SQLException; } diff --git a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java index eb8722f..f263237 100644 --- a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java +++ b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java @@ -2,43 +2,31 @@ import org.utplsql.api.TestRunnerOptions; import org.utplsql.api.Version; -import org.utplsql.api.exception.InvalidVersionException; import java.sql.Connection; import java.sql.SQLException; -/** Provides different implementations of TestRunnerStatement based on the version of the database framework +/** + * Provides different implementations of TestRunnerStatement based on the version of the database framework * * @author pesse */ public class TestRunnerStatementProvider { - /** Returns the TestRunnerStatement-implementation compatible with the given databaseVersion. + private TestRunnerStatementProvider() { + throw new UnsupportedOperationException(); + } + + /** + * Returns the TestRunnerStatement-implementation compatible with the given databaseVersion. * * @param databaseVersion Version of the database framework - * @param options TestRunnerOptions to be used - * @param conn Active Connection + * @param options TestRunnerOptions to be used + * @param conn Active Connection * @return TestRunnerStatment compatible with the database framework - * @throws SQLException + * @throws SQLException if there are problems with the database access */ - public static TestRunnerStatement getCompatibleTestRunnerStatement(Version databaseVersion, TestRunnerOptions options, Connection conn ) throws SQLException { - AbstractTestRunnerStatement stmt = null; - - try { - if (databaseVersion.isLessThan(Version.V3_0_3)) - stmt = new Pre303TestRunnerStatement(options, conn); - else if (databaseVersion.isLessThan(Version.V3_1_2)) - stmt = new Pre312TestRunnerStatement(options, conn); - - } catch ( InvalidVersionException ignored ) {} - - if ( stmt == null ) - stmt = new ActualTestRunnerStatement(options, conn); - - return stmt; - } - - private TestRunnerStatementProvider() { - throw new UnsupportedOperationException(); + public static TestRunnerStatement getCompatibleTestRunnerStatement(Version databaseVersion, TestRunnerOptions options, Connection conn) throws SQLException { + return DynamicTestRunnerStatement.forVersion(databaseVersion, conn, options, null); } } diff --git a/src/main/resources/utplsql-api.version b/src/main/resources/utplsql-api.version index 3fb16e7..ad96e7c 100644 --- a/src/main/resources/utplsql-api.version +++ b/src/main/resources/utplsql-api.version @@ -1 +1 @@ -${project.version}.${travisBuildNumber} \ No newline at end of file +${project.version} diff --git a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index 4332721..31ac56c 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -11,17 +11,22 @@ public abstract class AbstractDatabaseTest { - private static String sUrl; - private static String sUser; - private static String sPass; + private static final String DB_URL; + private static final String DB_USER; + private static final String DB_PASS; + static { - sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "192.168.99.100:1521:XE"); - sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); - sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "app"); + DB_URL = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); + DB_USER = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); + DB_PASS = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); } private Connection conn; - private List connectionList = new ArrayList<>(); + private final List connectionList = new ArrayList<>(); + + public static String getUser() { + return DB_USER; + } @BeforeEach public void setupConnection() throws SQLException { @@ -33,18 +38,18 @@ protected Connection getConnection() { } protected synchronized Connection newConnection() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@" + sUrl, sUser, sPass); + Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@" + DB_URL, DB_USER, DB_PASS); connectionList.add(conn); return conn; } - public static String getUser() { - return sUser; - } - @AfterEach public void teardownConnection() { - for (Connection conn : connectionList) - try { conn.close(); } catch (SQLException ignored) {} + for (Connection conn : connectionList) { + try { + conn.close(); + } catch (SQLException ignored) { + } + } } } diff --git a/src/test/java/org/utplsql/api/CompatibilityIT.java b/src/test/java/org/utplsql/api/CompatibilityIT.java index 118d386..948948a 100644 --- a/src/test/java/org/utplsql/api/CompatibilityIT.java +++ b/src/test/java/org/utplsql/api/CompatibilityIT.java @@ -19,9 +19,8 @@ void compatibleVersion() throws SQLException { @Test void skipCompatibilityCheck() throws SQLException { - CompatibilityProxy proxy = new CompatibilityProxy(getConnection(), true); + CompatibilityProxy proxy = new CompatibilityProxy(getConnection(), Version.LATEST); proxy.failOnNotCompatible(); assertTrue(proxy.isCompatible()); - } } diff --git a/src/test/java/org/utplsql/api/DBHelperIT.java b/src/test/java/org/utplsql/api/DBHelperIT.java index 8b13a5e..41f85c4 100644 --- a/src/test/java/org/utplsql/api/DBHelperIT.java +++ b/src/test/java/org/utplsql/api/DBHelperIT.java @@ -13,7 +13,7 @@ class DBHelperIT extends AbstractDatabaseTest { void getFrameworkVersion() throws SQLException { Version v = DBHelper.getDatabaseFrameworkVersion(getConnection()); assertTrue(v.isValid()); - System.out.println(v.getNormalizedString() + " - " + v.toString()); + System.out.println(v.getNormalizedString() + " - " + v); } @Test diff --git a/src/test/java/org/utplsql/api/DatabaseInformationIT.java b/src/test/java/org/utplsql/api/DatabaseInformationIT.java index 8eb4317..2844625 100644 --- a/src/test/java/org/utplsql/api/DatabaseInformationIT.java +++ b/src/test/java/org/utplsql/api/DatabaseInformationIT.java @@ -17,7 +17,7 @@ void getFrameworkVersion() throws SQLException { Version v = databaseInformation.getUtPlsqlFrameworkVersion(getConnection()); assertTrue(v.isValid()); - System.out.println(v.getNormalizedString() + " - " + v.toString()); + System.out.println(v.getNormalizedString() + " - " + v); } @Test diff --git a/src/test/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/test/java/org/utplsql/api/EnvironmentVariableUtil.java new file mode 100644 index 0000000..90ca49f --- /dev/null +++ b/src/test/java/org/utplsql/api/EnvironmentVariableUtil.java @@ -0,0 +1,53 @@ +package org.utplsql.api; + +import javax.annotation.Nullable; + +/** + * This class provides an easy way to get environmental variables. + * This is mainly to improve testability but also to standardize the way how utPLSQL API and CLI read from + * environment. + *

+ * Variables are obtained from the following scopes in that order (chain breaks as soon as a value is obtained): + *

    + *
  • Properties (System.getProperty())
  • + *
  • Environment (System.getEnv())
  • + *
  • Default value
  • + *
+ *

+ * An empty string is treated the same as null. + * + * @author pesse + */ +public class EnvironmentVariableUtil { + + private EnvironmentVariableUtil() { + } + + /** + * Returns the value for a given key from environment (see class description) + * + * @param key Key of environment or property value + * @return Environment value or null + */ + public static String getEnvValue(String key) { + return getEnvValue(key, null); + } + + /** + * Returns the value for a given key from environment or a default value (see class description) + * + * @param key Key of environment or property value + * @param defaultValue Default value if nothing found + * @return Environment value or defaultValue + */ + public static String getEnvValue(String key, @Nullable String defaultValue) { + + String val = System.getProperty(key); + if (val == null || val.isEmpty()) val = System.getenv(key); + if (val == null || val.isEmpty()) val = defaultValue; + + return val; + } + + +} diff --git a/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java b/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java index 8a0487f..c827232 100644 --- a/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java +++ b/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java @@ -20,8 +20,9 @@ void testGetVariableFromEnvironment() { .filter((e) -> !props.contains(e.getKey()) && e.getValue() != null && !e.getValue().isEmpty()) .findFirst(); - if ( !envVariable.isPresent() ) - fail ("Can't test for there is no environment variable not overridden by property"); + if (!envVariable.isPresent()) { + fail("Can't test for there is no environment variable not overridden by property"); + } assertEquals(envVariable.get().getValue(), EnvironmentVariableUtil.getEnvValue(envVariable.get().getKey())); } @@ -36,7 +37,7 @@ void testGetVariableFromProperty() { @Test void testGetVariableFromDefault() { - assertEquals("defaultValue", EnvironmentVariableUtil.getEnvValue("RANDOM"+String.valueOf(System.currentTimeMillis()), "defaultValue")); + assertEquals("defaultValue", EnvironmentVariableUtil.getEnvValue("RANDOM" + System.currentTimeMillis(), "defaultValue")); } } \ No newline at end of file diff --git a/src/test/java/org/utplsql/api/OptionalFeaturesIT.java b/src/test/java/org/utplsql/api/OptionalFeaturesIT.java index dad6263..f8fe9b3 100644 --- a/src/test/java/org/utplsql/api/OptionalFeaturesIT.java +++ b/src/test/java/org/utplsql/api/OptionalFeaturesIT.java @@ -14,7 +14,7 @@ class OptionalFeaturesIT extends AbstractDatabaseTest { private Version getDatabaseVersion() throws SQLException { - return new CompatibilityProxy(getConnection()).getDatabaseVersion(); + return new CompatibilityProxy(getConnection()).getUtPlsqlVersion(); } @Test @@ -22,10 +22,11 @@ void failOnError() throws SQLException, InvalidVersionException { boolean available = OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(getConnection()); - if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) { assertTrue(available); - else + } else { assertFalse(available); + } } @Test @@ -33,10 +34,11 @@ void frameworkCompatibilityCheck() throws SQLException, InvalidVersionException boolean available = OptionalFeatures.FRAMEWORK_COMPATIBILITY_CHECK.isAvailableFor(getConnection()); - if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) { assertTrue(available); - else + } else { assertFalse(available); + } } @Test @@ -44,9 +46,43 @@ void customReporters() throws SQLException, InvalidVersionException { boolean available = OptionalFeatures.CUSTOM_REPORTERS.isAvailableFor(getConnection()); - if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_0)) + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_0)) { assertTrue(available); - else + } else { assertFalse(available); + } + } + + @Test + void clientCharset() throws SQLException, InvalidVersionException { + boolean available = OptionalFeatures.CLIENT_CHARACTER_SET.isAvailableFor(getConnection()); + + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_2)) { + assertTrue(available); + } else { + assertFalse(available); + } + } + + @Test + void randomExecutionOrder() throws SQLException, InvalidVersionException { + boolean available = OptionalFeatures.RANDOM_EXECUTION_ORDER.isAvailableFor(getConnection()); + + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_7)) { + assertTrue(available); + } else { + assertFalse(available); + } + } + + @Test + void tags() throws SQLException, InvalidVersionException { + boolean available = OptionalFeatures.TAGS.isAvailableFor(getConnection()); + + if (getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_7)) { + assertTrue(available); + } else { + assertFalse(available); + } } } diff --git a/src/test/java/org/utplsql/api/OutputBufferIT.java b/src/test/java/org/utplsql/api/OutputBufferIT.java index 18ee3ee..6dd2742 100644 --- a/src/test/java/org/utplsql/api/OutputBufferIT.java +++ b/src/test/java/org/utplsql/api/OutputBufferIT.java @@ -40,7 +40,6 @@ private Reporter createReporter() throws SQLException { @Test void printAvailableLines() throws SQLException { ExecutorService executorService = Executors.newFixedThreadPool(2); - try { final Reporter reporter = createReporter(); @@ -88,11 +87,13 @@ void printAvailableLines() throws SQLException { Object res1 = task1.get(); Object res2 = task2.get(); - if (res1 instanceof Exception) + if (res1 instanceof Exception) { fail((Exception) res1); + } - if (res2 instanceof Exception) + if (res2 instanceof Exception) { fail((Exception) res2); + } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } @@ -129,7 +130,7 @@ void getOutputFromSonarReporter() throws SQLException { void sonarReporterHasEncodingSet() throws SQLException, InvalidVersionException { CompatibilityProxy proxy = new CompatibilityProxy(newConnection()); - if ( proxy.getDatabaseVersion().isGreaterOrEqualThan(Version.V3_1_2)) { + if (proxy.getUtPlsqlVersion().isGreaterOrEqualThan(Version.V3_1_2)) { Reporter reporter = new DefaultReporter(CoreReporters.UT_SONAR_TEST_REPORTER.name(), null).init(getConnection()); TestRunner tr = new TestRunner() diff --git a/src/test/java/org/utplsql/api/ReporterInspectorIT.java b/src/test/java/org/utplsql/api/ReporterInspectorIT.java index b076351..4f3f13d 100644 --- a/src/test/java/org/utplsql/api/ReporterInspectorIT.java +++ b/src/test/java/org/utplsql/api/ReporterInspectorIT.java @@ -24,22 +24,21 @@ private ReporterFactory getReporterFactory() throws SQLException { @Test void testGetReporterInfo() throws SQLException, InvalidVersionException { - CompatibilityProxy proxy = new CompatibilityProxy(getConnection()); ReporterInspector inspector = ReporterInspector.create(getReporterFactory(), getConnection()); Map infos = inspector.getReporterInfoMap(); - assertEquals( infos.get(CoreReporters.UT_COVERAGE_HTML_REPORTER.name()).getType(), ReporterInfo.Type.SQL_WITH_JAVA ); - assertEquals( infos.get(CoreReporters.UT_COVERAGE_SONAR_REPORTER.name()).getType(), ReporterInfo.Type.SQL ); - assertEquals( infos.get(CoreReporters.UT_COVERALLS_REPORTER.name()).getType(), ReporterInfo.Type.SQL ); - assertEquals( infos.get(CoreReporters.UT_DOCUMENTATION_REPORTER.name()).getType(), ReporterInfo.Type.SQL_WITH_JAVA ); - assertEquals( infos.get(CoreReporters.UT_SONAR_TEST_REPORTER.name()).getType(), ReporterInfo.Type.SQL); - assertEquals( infos.get(CoreReporters.UT_TEAMCITY_REPORTER.name()).getType(), ReporterInfo.Type.SQL); - assertEquals( infos.get(CoreReporters.UT_XUNIT_REPORTER.name()).getType(), ReporterInfo.Type.SQL); + assertEquals(infos.get(CoreReporters.UT_COVERAGE_HTML_REPORTER.name()).getType(), ReporterInfo.Type.SQL_WITH_JAVA); + assertEquals(infos.get(CoreReporters.UT_COVERAGE_SONAR_REPORTER.name()).getType(), ReporterInfo.Type.SQL); + assertEquals(infos.get(CoreReporters.UT_COVERALLS_REPORTER.name()).getType(), ReporterInfo.Type.SQL); + assertEquals(infos.get(CoreReporters.UT_DOCUMENTATION_REPORTER.name()).getType(), ReporterInfo.Type.SQL_WITH_JAVA); + assertEquals(infos.get(CoreReporters.UT_SONAR_TEST_REPORTER.name()).getType(), ReporterInfo.Type.SQL); + assertEquals(infos.get(CoreReporters.UT_TEAMCITY_REPORTER.name()).getType(), ReporterInfo.Type.SQL); + assertEquals(infos.get(CoreReporters.UT_JUNIT_REPORTER.name()).getType(), ReporterInfo.Type.SQL); - if ( CoreReporters.UT_COVERAGE_COBERTURA_REPORTER.isAvailableFor(proxy.getDatabaseVersion())) { + if (CoreReporters.UT_COVERAGE_COBERTURA_REPORTER.isAvailableFor(proxy.getUtPlsqlVersion())) { assertEquals(infos.get(CoreReporters.UT_COVERAGE_COBERTURA_REPORTER.name()).getType(), ReporterInfo.Type.SQL); } } diff --git a/src/test/java/org/utplsql/api/TestRunnerIT.java b/src/test/java/org/utplsql/api/TestRunnerIT.java index 95368ca..4ceb184 100644 --- a/src/test/java/org/utplsql/api/TestRunnerIT.java +++ b/src/test/java/org/utplsql/api/TestRunnerIT.java @@ -1,8 +1,12 @@ package org.utplsql.api; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; import org.utplsql.api.compatibility.CompatibilityProxy; +import org.utplsql.api.compatibility.OptionalFeatures; +import org.utplsql.api.db.DatabaseInformation; +import org.utplsql.api.db.DefaultDatabaseInformation; import org.utplsql.api.exception.InvalidVersionException; import org.utplsql.api.exception.SomeTestsFailedException; import org.utplsql.api.reporter.CoreReporters; @@ -26,13 +30,16 @@ void runWithDefaultParameters() throws SQLException { } - /** This can only be run against versions >= 3.0.3 + /** + * This can only be run against versions >= 3.0.3 */ @Test - void runWithoutCompatibilityCheck() throws SQLException, InvalidVersionException { - CompatibilityProxy proxy = new CompatibilityProxy(getConnection()); + void runWithoutCompatibilityCheck() throws SQLException { + + DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); - if (proxy.getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) { + // We can only test this for the versions of the latest TestRunnerStatement-Change + if ( OptionalFeatures.RANDOM_EXECUTION_ORDER.isAvailableFor(databaseInformation.getUtPlsqlFrameworkVersion(getConnection())) ) { new TestRunner() .skipCompatibilityCheck(true) .run(getConnection()); @@ -50,20 +57,22 @@ void runWithManyReporters() throws SQLException { .addReporter(CoreReporters.UT_COVERALLS_REPORTER.name()) .addReporter(CoreReporters.UT_SONAR_TEST_REPORTER.name()) .addReporter(CoreReporters.UT_TEAMCITY_REPORTER.name()) - .addReporter(CoreReporters.UT_XUNIT_REPORTER.name()) + .addReporter(CoreReporters.UT_JUNIT_REPORTER.name()) .run(conn); } - /** This can only be tested on frameworks >= 3.0.3 + /** + * This can only be tested on frameworks >= 3.0.3 */ + @Disabled @Test void failOnErrors() throws SQLException, InvalidVersionException { Connection conn = getConnection(); CompatibilityProxy proxy = new CompatibilityProxy(conn); - if (proxy.getDatabaseVersion().isGreaterOrEqualThan(Version.V3_0_3)) { + if (proxy.getUtPlsqlVersion().isGreaterOrEqualThan(Version.V3_0_3)) { Executable throwingTestRunner = () -> new TestRunner() .failOnErrors(true) .run(conn); @@ -71,4 +80,21 @@ void failOnErrors() throws SQLException, InvalidVersionException { } } + @Test + void runWithRandomExecutionOrder() throws SQLException { + CompatibilityProxy proxy = new CompatibilityProxy(getConnection()); + + new TestRunner() + .randomTestOrder(true) + .randomTestOrderSeed(123) + .run(getConnection()); + } + + @Test + void runWithTags() throws SQLException { + new TestRunner() + .addTag("none") + .run(getConnection()); + } + } diff --git a/src/test/java/org/utplsql/api/VersionObjectTest.java b/src/test/java/org/utplsql/api/VersionObjectTest.java index d0f4ed4..46cea83 100644 --- a/src/test/java/org/utplsql/api/VersionObjectTest.java +++ b/src/test/java/org/utplsql/api/VersionObjectTest.java @@ -11,10 +11,10 @@ class VersionObjectTest { void versionPatternRecognitionFull() { Version v = Version.create("v3.1.3.1234"); - assertEquals(3, (long)v.getMajor()); - assertEquals(1, (long)v.getMinor()); - assertEquals(3, (long)v.getBugfix()); - assertEquals(1234, (long)v.getBuild()); + assertEquals(3, (long) v.getMajor()); + assertEquals(1, (long) v.getMinor()); + assertEquals(3, (long) v.getBugfix()); + assertEquals(1234, (long) v.getBuild()); assertTrue(v.isValid()); assertEquals("3.1.3.1234", v.getNormalizedString()); } @@ -23,10 +23,10 @@ void versionPatternRecognitionFull() { void versionPatternRecognitionDevelop() { Version v = Version.create("v3.1.3.2140-develop"); - assertEquals(3, (long)v.getMajor()); - assertEquals(1, (long)v.getMinor()); - assertEquals(3, (long)v.getBugfix()); - assertEquals(2140, (long)v.getBuild()); + assertEquals(3, (long) v.getMajor()); + assertEquals(1, (long) v.getMinor()); + assertEquals(3, (long) v.getBugfix()); + assertEquals(2140, (long) v.getBuild()); assertTrue(v.isValid()); assertEquals("3.1.3.2140", v.getNormalizedString()); } @@ -35,8 +35,8 @@ void versionPatternRecognitionDevelop() { void versionPatternRecognitionPartial() { Version v = Version.create("3.1.etc"); - assertEquals(3, (long)v.getMajor()); - assertEquals(1, (long)v.getMinor()); + assertEquals(3, (long) v.getMajor()); + assertEquals(1, (long) v.getMinor()); assertNull(v.getBugfix()); assertNull(v.getBuild()); assertTrue(v.isValid()); @@ -56,8 +56,7 @@ void versionPatternRecognitionInvalid() { } @Test - void versionCompareTo() - { + void versionCompareTo() { Version base = Version.create("2.3.4.5"); // Less than @@ -80,8 +79,30 @@ void versionCompareTo() } @Test - void isGreaterOrEqualThan() throws InvalidVersionException - { + void versionCompareToWithBaseNull() { + Version base = Version.create("2.3.4"); + + // Less than + assertEquals(-1, base.compareTo(Version.create("3"))); + assertEquals(-1, base.compareTo(Version.create("3.2"))); + assertEquals(-1, base.compareTo(Version.create("2.4.1"))); + assertEquals(-1, base.compareTo(Version.create("2.3.9.1"))); + assertEquals(-1, base.compareTo(Version.create("2.3.4.1"))); + assertEquals(-1, base.compareTo(Version.create("2.3.4.5"))); + assertEquals(-1, base.compareTo(Version.create("2.3.4.9"))); + + // Greater than + assertEquals(1, base.compareTo(Version.create("1"))); + assertEquals(1, base.compareTo(Version.create("1.6"))); + assertEquals(1, base.compareTo(Version.create("2.2.4"))); + assertEquals(1, base.compareTo(Version.create("2.3.3"))); + + // Equal + assertEquals(0, base.compareTo(Version.create("2.3.4"))); + } + + @Test + void isGreaterOrEqualThan() throws InvalidVersionException { Version base = Version.create("2.3.4.5"); assertTrue(base.isGreaterOrEqualThan(Version.create("1"))); @@ -101,30 +122,46 @@ void isGreaterOrEqualThan() throws InvalidVersionException } @Test - void isGreaterOrEqualThanFails() - { + void isGreaterOrEqualThanWithBaseNull() throws InvalidVersionException { + Version base = Version.create("2.3.4"); + + assertTrue(base.isGreaterOrEqualThan(Version.create("1"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.2"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3.4"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3.3"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3.4.5"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3.4.4"))); + assertTrue(base.isGreaterOrEqualThan(Version.create("2.3.4.6"))); + + assertFalse(base.isGreaterOrEqualThan(Version.create("2.3.5"))); + assertFalse(base.isGreaterOrEqualThan(Version.create("2.4"))); + assertFalse(base.isGreaterOrEqualThan(Version.create("3"))); + + } + + @Test + void isGreaterOrEqualThanFails() { // Given version is invalid try { Version.create("2.3.4.5").isGreaterOrEqualThan(Version.create("aerfvf")); fail("Given Version is invalid - not recognized"); - } - catch ( InvalidVersionException ignored ) { + } catch (InvalidVersionException ignored) { } // Base version is invalid try { Version.create("erefs").isGreaterOrEqualThan(Version.create("1.2.3")); fail("Base version is invalid - not recognized"); - } - catch ( InvalidVersionException ignored ) { + } catch (InvalidVersionException ignored) { } // Both versions are invalid try { Version.create("erefs").isGreaterOrEqualThan(Version.create("aerfvf")); fail("Both versions are invalid - not recognized"); - } - catch ( InvalidVersionException ignored ) { + } catch (InvalidVersionException ignored) { } } } diff --git a/src/test/java/org/utplsql/api/db/DynamicParameterListTest.java b/src/test/java/org/utplsql/api/db/DynamicParameterListTest.java new file mode 100644 index 0000000..e3e9a17 --- /dev/null +++ b/src/test/java/org/utplsql/api/db/DynamicParameterListTest.java @@ -0,0 +1,144 @@ +package org.utplsql.api.db; + +import oracle.jdbc.OracleConnection; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.sql.CallableStatement; +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class DynamicParameterListTest { + + @Nested + class single_parameters { + @Test + void can_add_string() throws SQLException { + CallableStatement stmt = mock(CallableStatement.class); + + DynamicParameterList paramList = DynamicParameterList.builder() + .add("a_param", "MyString") + .build(); + + assertEquals("a_param => ?", paramList.getSql()); + + paramList.setParamsStartWithIndex(stmt, 5); + verify(stmt).setString(5, "MyString"); + } + + @Test + void can_add_int() throws SQLException { + CallableStatement stmt = mock(CallableStatement.class); + + DynamicParameterList paramList = DynamicParameterList.builder() + .add("a_param", 1234) + .build(); + + assertEquals("a_param => ?", paramList.getSql()); + + paramList.setParamsStartWithIndex(stmt, 10); + verify(stmt).setInt(10, 1234); + } + + @Test + void can_add_array() throws SQLException { + CallableStatement stmt = mock(CallableStatement.class); + OracleConnection conn = mock(OracleConnection.class); + + Object[] numArr = new Object[]{1, 2}; + + DynamicParameterList paramList = DynamicParameterList.builder() + .add("a_param", numArr, "MY_TYPE", conn) + .build(); + + assertEquals("a_param => ?", paramList.getSql()); + + paramList.setParamsStartWithIndex(stmt, 3); + verify(conn).createOracleArray("MY_TYPE", numArr); + verify(stmt).setArray(3, null); + } + + @Test + void can_add_boolean() throws SQLException { + CallableStatement stmt = mock(CallableStatement.class); + + DynamicParameterList paramList = DynamicParameterList.builder() + .add("a_bool", true) + .build(); + + assertEquals("a_bool => (case ? when 1 then true else false end)", paramList.getSql()); + + paramList.setParamsStartWithIndex(stmt, 3); + verify(stmt).setInt(3, 1); + } + } + + @Nested + class mutliple_parameters { + + @Test + void several_parameters_are_issued_in_the_correct_order() throws SQLException { + CallableStatement mockedStatement = mock(CallableStatement.class); + + DynamicParameterList parameterList = DynamicParameterList.builder() + .add("a_param1", "Param1") + .add("a_param2", "Param2") + .add("a_param3", "Param3") + .build(); + + assertEquals("a_param1 => ?, a_param2 => ?, a_param3 => ?", parameterList.getSql()); + + parameterList.setParamsStartWithIndex(mockedStatement, 10); + + verify(mockedStatement).setString(10, "Param1"); + verify(mockedStatement).setString(11, "Param2"); + verify(mockedStatement).setString(12, "Param3"); + } + + @Test + void call_with_three_different_types() throws SQLException { + + CallableStatement mockedStatement = mock(CallableStatement.class); + OracleConnection mockedConn = mock(OracleConnection.class); + + Object[] numArr = new Object[]{1, 2}; + + DynamicParameterList parameterList = DynamicParameterList.builder() + .add("a_object_owner", "MyOwner") + .add("a_num_param", 123) + .add("a_num_array", numArr, "MY_NUM_ARR", mockedConn) + .build(); + + assertEquals("a_object_owner => ?, a_num_param => ?, a_num_array => ?", parameterList.getSql()); + + parameterList.setParamsStartWithIndex(mockedStatement, 5); + + verify(mockedStatement).setString(5, "MyOwner"); + verify(mockedStatement).setInt(6, 123); + verify(mockedConn).createOracleArray("MY_NUM_ARR", numArr); + verify(mockedStatement).setArray(7, null); + } + + @Test + void when_not_accept_empty_filter_empty_elements() throws SQLException { + + CallableStatement mockedStatement = mock(CallableStatement.class); + OracleConnection mockedConn = mock(OracleConnection.class); + + DynamicParameterList parameterList = DynamicParameterList.builder() + .addIfNotEmpty("a_object_owner", (String) null) + .addIfNotEmpty("a_num_param", (Integer) null) + .addIfNotEmpty("a_num_array", new Object[]{}, "MY_NUM_ARR", mockedConn) + .build(); + + assertEquals("", parameterList.getSql()); + + parameterList.setParamsStartWithIndex(mockedStatement, 2); + + verifyNoMoreInteractions(mockedStatement); + verifyNoMoreInteractions(mockedConn); + } + } +} diff --git a/src/test/java/org/utplsql/api/outputBuffer/OutputBufferProviderIT.java b/src/test/java/org/utplsql/api/outputBuffer/OutputBufferProviderIT.java new file mode 100644 index 0000000..463e0be --- /dev/null +++ b/src/test/java/org/utplsql/api/outputBuffer/OutputBufferProviderIT.java @@ -0,0 +1,49 @@ +package org.utplsql.api.outputBuffer; + +import org.junit.jupiter.api.Test; +import org.utplsql.api.AbstractDatabaseTest; +import org.utplsql.api.Version; +import org.utplsql.api.compatibility.CompatibilityProxy; +import org.utplsql.api.exception.InvalidVersionException; +import org.utplsql.api.reporter.CoreReporters; +import org.utplsql.api.reporter.Reporter; +import org.utplsql.api.reporter.ReporterFactory; + +import java.sql.SQLException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +public class OutputBufferProviderIT extends AbstractDatabaseTest { + + @Test + void testGettingPre310Version() throws SQLException { + + CompatibilityProxy proxy = new CompatibilityProxy(getConnection(), Version.V3_0_4); + ReporterFactory reporterFactory = ReporterFactory.createDefault(proxy); + + Reporter r = reporterFactory.createReporter(CoreReporters.UT_DOCUMENTATION_REPORTER.name()); + r.init(getConnection(), proxy, reporterFactory); + + OutputBuffer buffer = proxy.getOutputBuffer(r, getConnection()); + + assertThat(buffer, instanceOf(CompatibilityOutputBufferPre310.class)); + } + + @Test + void testGettingActualVersion() throws SQLException, InvalidVersionException { + CompatibilityProxy proxy = new CompatibilityProxy(getConnection(), Version.LATEST); + + // We can only test new behaviour with DB-Version >= 3.1.0 + if ( proxy.getRealDbPlsqlVersion().isGreaterOrEqualThan(Version.V3_1_0)) { + ReporterFactory reporterFactory = ReporterFactory.createDefault(proxy); + + Reporter r = reporterFactory.createReporter(CoreReporters.UT_DOCUMENTATION_REPORTER.name()); + r.init(getConnection(), proxy, reporterFactory); + + OutputBuffer buffer = proxy.getOutputBuffer(r, getConnection()); + + assertThat(buffer, instanceOf(DefaultOutputBuffer.class)); + } + } +} diff --git a/src/test/java/org/utplsql/api/outputBuffer/PLSQLOutputBufferIT.java b/src/test/java/org/utplsql/api/outputBuffer/PLSQLOutputBufferIT.java deleted file mode 100644 index b4b6aa9..0000000 --- a/src/test/java/org/utplsql/api/outputBuffer/PLSQLOutputBufferIT.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.utplsql.api.outputBuffer; - -import org.junit.jupiter.api.Test; -import org.utplsql.api.AbstractDatabaseTest; - -class PLSQLOutputBufferIT extends AbstractDatabaseTest { - - @Test - void getLines() { - - } -} diff --git a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java index 31abc01..b94cc32 100644 --- a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java +++ b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java @@ -1,80 +1,65 @@ package org.utplsql.api.reporter; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import java.io.File; -import java.io.IOException; -import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Path; +import java.nio.file.Paths; import static org.junit.jupiter.api.Assertions.assertTrue; +@Tag("binary") class CoverageHTMLReporterAssetTest { private static final String TEST_FOLDER = "__testAssets"; + @TempDir + Path tempDir; + private void testFileExists(Path filePath) { - File f = new File(filePath.toUri()); + File f = new File(tempDir.resolve(TEST_FOLDER).resolve(filePath).toUri()); - assertTrue(f.exists(), () -> "File " + f.toString() + " does not exist"); + assertTrue(f.exists(), () -> "File " + f + " does not exist"); } + @Disabled("No idea why this ever worked") @Test void writeReporterAssetsTo() throws RuntimeException { - Path targetPath = Paths.get(TEST_FOLDER); + Path targetPath = tempDir.resolve(TEST_FOLDER); // Act CoverageHTMLReporter.writeReportAssetsTo(targetPath); - testFileExists(targetPath.resolve(Paths.get("colorbox", "border.png"))); - testFileExists(targetPath.resolve(Paths.get("colorbox", "controls.png"))); - testFileExists(targetPath.resolve(Paths.get("colorbox", "loading.gif"))); - testFileExists(targetPath.resolve(Paths.get("colorbox", "loading_background.png"))); - - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_flat_0_aaaaaa_40x100.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_flat_75_ffffff_40x100.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_glass_55_fbf9ee_1x400.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_glass_65_ffffff_1x400.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_glass_75_dadada_1x400.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_glass_75_e6e6e6_1x400.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_glass_95_fef1ec_1x400.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-bg_highlight-soft_75_cccccc_1x100.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-icons_2e83ff_256x240.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-icons_222222_256x240.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-icons_454545_256x240.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-icons_888888_256x240.png"))); - testFileExists(targetPath.resolve(Paths.get("images", "ui-icons_cd0a0a_256x240.png"))); - - testFileExists(targetPath.resolve(Paths.get("application.css"))); - testFileExists(targetPath.resolve(Paths.get("application.js"))); - testFileExists(targetPath.resolve(Paths.get("favicon_green.png"))); - testFileExists(targetPath.resolve(Paths.get("favicon_red.png"))); - testFileExists(targetPath.resolve(Paths.get("favicon_yellow.png"))); - testFileExists(targetPath.resolve(Paths.get("loading.gif"))); - testFileExists(targetPath.resolve(Paths.get("magnify.png"))); - - } - - @AfterAll - static void clearTestAssetsFolder() { - try { - Files.walkFileTree(Paths.get(TEST_FOLDER), new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } + testFileExists(Paths.get("colorbox", "border.png")); + testFileExists(Paths.get("colorbox", "controls.png")); + testFileExists(Paths.get("colorbox", "loading.gif")); + testFileExists(Paths.get("colorbox", "loading_background.png")); + + testFileExists(Paths.get("images", "ui-bg_flat_0_aaaaaa_40x100.png")); + testFileExists(Paths.get("images", "ui-bg_flat_75_ffffff_40x100.png")); + testFileExists(Paths.get("images", "ui-bg_glass_55_fbf9ee_1x400.png")); + testFileExists(Paths.get("images", "ui-bg_glass_65_ffffff_1x400.png")); + testFileExists(Paths.get("images", "ui-bg_glass_75_dadada_1x400.png")); + testFileExists(Paths.get("images", "ui-bg_glass_75_e6e6e6_1x400.png")); + testFileExists(Paths.get("images", "ui-bg_glass_95_fef1ec_1x400.png")); + testFileExists(Paths.get("images", "ui-bg_highlight-soft_75_cccccc_1x100.png")); + testFileExists(Paths.get("images", "ui-icons_2e83ff_256x240.png")); + testFileExists(Paths.get("images", "ui-icons_222222_256x240.png")); + testFileExists(Paths.get("images", "ui-icons_454545_256x240.png")); + testFileExists(Paths.get("images", "ui-icons_888888_256x240.png")); + testFileExists(Paths.get("images", "ui-icons_cd0a0a_256x240.png")); + + testFileExists(Paths.get("application.css")); + testFileExists(Paths.get("application.js")); + testFileExists(Paths.get("favicon_green.png")); + testFileExists(Paths.get("favicon_red.png")); + testFileExists(Paths.get("favicon_yellow.png")); + testFileExists(Paths.get("loading.gif")); + testFileExists(Paths.get("magnify.png")); - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - e.printStackTrace(); - } } } diff --git a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java new file mode 100644 index 0000000..739dbcf --- /dev/null +++ b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java @@ -0,0 +1,314 @@ +package org.utplsql.api.testRunner; + +import oracle.jdbc.OracleConnection; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.verification.VerificationMode; +import org.utplsql.api.CustomTypes; +import org.utplsql.api.FileMapping; +import org.utplsql.api.TestRunnerOptions; +import org.utplsql.api.Version; + +import java.sql.Array; +import java.sql.CallableStatement; +import java.sql.SQLException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.not; +import static org.mockito.Mockito.*; + +public class DynamicTestRunnerStatementTest { + + private DynamicTestRunnerStatement testRunnerStatement; + private CallableStatement callableStatement; + private OracleConnection oracleConnection; + private TestRunnerOptions options; + private Object[] expectedFileMapping; + + @BeforeEach + void initParameters() throws SQLException { + expectedFileMapping = new Object[]{new FileMapping("someFile", "owner", "object", "PACKAGE")}; + + // Mock some internals. This is not pretty, but a first step + oracleConnection = getMockedOracleConnection(expectedFileMapping); + callableStatement = mock(CallableStatement.class); + + // Act + options = TestRunnerStatementProviderIT.getCompletelyFilledOptions(); + } + + @Test + void version_3_0_2_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_0_2); + + checkBaseParameters(); + checkFailOnError(false); + checkClientCharacterSet(false); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_0_3_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_0_3); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(false); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_0_4_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_0_4); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(false); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_0_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_0); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(false); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_1_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_1); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(false); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_2_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_2); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_3_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_3); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_4_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_4); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_5_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_5); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_6_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_6); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(false); + checkTags(false); + checkExpr(false); + } + + @Test + void version_3_1_7_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_7); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(true); + checkTags(true); + checkExpr(false); + } + + @Test + void version_3_1_8_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_8); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(true); + checkTags(true); + checkExpr(false); + } + + @Test + void version_3_1_13_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_13); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(true); + checkTags(true); + checkExpr(true); + } + + private OracleConnection getMockedOracleConnection(Object[] expectedFileMapping) throws SQLException { + OracleConnection oracleConnection = mock(OracleConnection.class); + when(oracleConnection.unwrap(OracleConnection.class)) + .thenReturn(oracleConnection); + mockFileMapper(oracleConnection, expectedFileMapping); + return oracleConnection; + } + + private void mockFileMapper(OracleConnection mockedOracleConnection, Object[] expectedFileMapping) throws SQLException { + Array fileMapperArray = mock(Array.class); + CallableStatement fileMapperStatement = mock(CallableStatement.class); + + when(fileMapperArray.getArray()) + .thenReturn(expectedFileMapping); + when(fileMapperStatement.getArray(1)) + .thenReturn(fileMapperArray); + when( + mockedOracleConnection.prepareCall(argThat( + a -> a.startsWith("BEGIN ? := ut_file_mapper.build_file_mappings(")) + )) + .thenReturn(fileMapperStatement); + } + + private Matcher doesOrDoesNotContainString(String string, boolean shouldBeThere) { + return (shouldBeThere) + ? containsString(string) + : not(containsString(string)); + } + + private VerificationMode doesOrDoesNotGetCalled(boolean shouldBeThere) { + return (shouldBeThere) + ? times(1) + : never(); + } + + private void initTestRunnerStatementForVersion(Version version) throws SQLException { + testRunnerStatement = DynamicTestRunnerStatement + .forVersion(version, oracleConnection, options, callableStatement); + } + + private void checkBaseParameters() throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, containsString("a_paths => ?")); + verify(callableStatement).setArray(1, null); + verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.pathList.toArray()); + + assertThat(sql, containsString("a_reporters => ?")); + verify(callableStatement).setArray(2, null); + verify(oracleConnection).createOracleArray(CustomTypes.UT_REPORTERS, options.reporterList.toArray()); + + assertThat(sql, containsString("a_color_console => (case ? when 1 then true else false end)")); + verify(callableStatement).setInt(3, 0); + + assertThat(sql, containsString("a_coverage_schemes => ?")); + verify(callableStatement).setArray(4, null); + verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.coverageSchemes.toArray()); + + assertThat(sql, containsString("a_source_file_mappings => ?")); + verify(callableStatement).setArray(5, null); + + assertThat(sql, containsString("a_test_file_mappings => ?")); + verify(callableStatement).setArray(6, null); + verify(oracleConnection, times(2)).createOracleArray(CustomTypes.UT_FILE_MAPPINGS, expectedFileMapping); + + assertThat(sql, containsString("a_include_objects => ?")); + verify(callableStatement).setArray(7, null); + verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.includeObjects.toArray()); + + assertThat(sql, containsString("a_exclude_objects => ?")); + verify(callableStatement).setArray(8, null); + verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.includeObjects.toArray()); + } + + private void checkFailOnError(boolean shouldBeThere) throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, doesOrDoesNotContainString("a_fail_on_errors => (case ? when 1 then true else false end)", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(9, 1); + } + + private void checkClientCharacterSet(boolean shouldBeThere) throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, doesOrDoesNotContainString("a_client_character_set => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(10, "UTF8"); + } + + private void checkRandomTestOrder(boolean shouldBeThere) throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, doesOrDoesNotContainString("a_random_test_order => (case ? when 1 then true else false end)", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(11, 1); + assertThat(sql, doesOrDoesNotContainString("a_random_test_order_seed => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(12, 123); + } + + private void checkTags(boolean shouldBeThere) throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, doesOrDoesNotContainString("a_tags => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(13, "WIP,long_running"); + } + + private void checkExpr(boolean shouldBeThere) throws SQLException { + String sql = testRunnerStatement.getSql(); + + assertThat(sql, doesOrDoesNotContainString("a_include_schema_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(14, "a_*"); + assertThat(sql, doesOrDoesNotContainString("a_include_object_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(15, "a_*"); + assertThat(sql, doesOrDoesNotContainString("a_exclude_schema_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(16, "ut3:*_package*"); + assertThat(sql, doesOrDoesNotContainString("a_exclude_object_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(17, "ut3:*_package*"); + } +} diff --git a/src/test/java/org/utplsql/api/FileMapperIT.java b/src/test/java/org/utplsql/api/testRunner/FileMapperIT.java similarity index 51% rename from src/test/java/org/utplsql/api/FileMapperIT.java rename to src/test/java/org/utplsql/api/testRunner/FileMapperIT.java index 0db0163..5c7a306 100644 --- a/src/test/java/org/utplsql/api/FileMapperIT.java +++ b/src/test/java/org/utplsql/api/testRunner/FileMapperIT.java @@ -1,6 +1,12 @@ -package org.utplsql.api; +package org.utplsql.api.testRunner; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.utplsql.api.AbstractDatabaseTest; +import org.utplsql.api.FileMapperOptions; +import org.utplsql.api.FileMapping; +import org.utplsql.api.KeyValuePair; +import org.utplsql.api.testRunner.FileMapper; import java.sql.SQLException; import java.util.ArrayList; @@ -31,8 +37,9 @@ void testFileMapper() throws SQLException { List fileMappings = FileMapper.buildFileMappingList(getConnection(), mapperOptions); - if (fileMappings.size() != 2) + if (fileMappings.size() != 2) { fail("Wrong mapping list size."); + } assertMapping(fileMappings.get(0), "APP", "AWARD_BONUS", "PROCEDURE"); assertMapping(fileMappings.get(1), "APP", "BETWNSTR", "FUNCTION"); @@ -44,4 +51,37 @@ private void assertMapping(FileMapping fileMapping, String owner, String name, S assertEquals(type, fileMapping.getObjectType()); } + @Nested + class Default_type_mapping { + + void checkTypeMapping( List typeMappings ) throws SQLException { + List filePaths = java.util.Arrays.asList( + "/award_bonus.prc", + "/betwnstr.fnc", + "/package_body.pkb", + "/type_body.tpb", + "/trigger.trg"); + FileMapperOptions mapperOptions = new FileMapperOptions(filePaths); + mapperOptions.setTypeMappings(typeMappings); + + List fileMappings = FileMapper.buildFileMappingList(getConnection(), mapperOptions); + + assertEquals("PROCEDURE", fileMappings.get(0).getObjectType()); + assertEquals("FUNCTION", fileMappings.get(1).getObjectType()); + assertEquals("PACKAGE BODY", fileMappings.get(2).getObjectType()); + assertEquals("TYPE BODY", fileMappings.get(3).getObjectType()); + assertEquals("TRIGGER", fileMappings.get(4).getObjectType()); + } + + @Test + void is_used_on_null_parameter() throws SQLException { + checkTypeMapping(null); + } + + @Test + void is_used_on_empty_parameter() throws SQLException { + checkTypeMapping(new ArrayList<>()); + } + } + } diff --git a/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java index e811102..2676c38 100644 --- a/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java +++ b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java @@ -2,37 +2,105 @@ import org.junit.jupiter.api.Test; import org.utplsql.api.AbstractDatabaseTest; +import org.utplsql.api.FileMapperOptions; import org.utplsql.api.TestRunnerOptions; import org.utplsql.api.Version; +import org.utplsql.api.reporter.CoreReporters; +import org.utplsql.api.reporter.ReporterFactory; import java.sql.SQLException; +import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; class TestRunnerStatementProviderIT extends AbstractDatabaseTest { + public static TestRunnerOptions getCompletelyFilledOptions() { + TestRunnerOptions options = new TestRunnerOptions(); + options.pathList.add("path"); + options.reporterList.add(ReporterFactory.createEmpty().createReporter(CoreReporters.UT_DOCUMENTATION_REPORTER.name())); + options.coverageSchemes.add("APP"); + options.sourceMappingOptions = new FileMapperOptions(Arrays.asList("sourcePath")); + options.testMappingOptions = new FileMapperOptions(Arrays.asList("testPath")); + options.includeObjects.add("include1"); + options.excludeObjects.add("exclude1"); + options.failOnErrors = true; + options.clientCharacterSet = "UTF8"; + options.randomTestOrder = true; + options.randomTestOrderSeed = 123; + options.tags.add("WIP"); + options.tags.add("long_running"); + options.includeSchemaExpr = "a_*"; + options.includeObjectExpr = "a_*"; + options.excludeSchemaExpr = "ut3:*_package*"; + options.excludeObjectExpr = "ut3:*_package*"; + return options; + } + + TestRunnerStatement getTestRunnerStatementForVersion(Version version) throws SQLException { + return TestRunnerStatementProvider.getCompatibleTestRunnerStatement(version, getCompletelyFilledOptions(), getConnection()); + } + @Test void testGettingPre303Version() throws SQLException { - TestRunnerStatement stmt = TestRunnerStatementProvider.getCompatibleTestRunnerStatement(Version.V3_0_2, new TestRunnerOptions(), getConnection()); - assertEquals(Pre303TestRunnerStatement.class, stmt.getClass()); + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.V3_0_2); + assertThat(stmt.getSql(), not(containsString("a_fail_on_errors"))); + assertThat(stmt.getSql(), not(containsString("a_client_character_set"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order_seed"))); + assertThat(stmt.getSql(), not(containsString("a_tags"))); } @Test void testGettingPre312Version_from_303() throws SQLException { - TestRunnerStatement stmt = TestRunnerStatementProvider.getCompatibleTestRunnerStatement(Version.V3_0_3, new TestRunnerOptions(), getConnection()); - assertEquals(Pre312TestRunnerStatement.class, stmt.getClass()); + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.V3_0_3); + assertThat(stmt.getSql(), containsString("a_fail_on_errors")); + assertThat(stmt.getSql(), not(containsString("a_client_character_set"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order_seed"))); + assertThat(stmt.getSql(), not(containsString("a_tags"))); } @Test void testGettingPre312Version_from_311() throws SQLException { - TestRunnerStatement stmt = TestRunnerStatementProvider.getCompatibleTestRunnerStatement(Version.V3_1_1, new TestRunnerOptions(), getConnection()); - assertEquals(Pre312TestRunnerStatement.class, stmt.getClass()); + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.V3_1_1); + assertThat(stmt.getSql(), containsString("a_fail_on_errors")); + assertThat(stmt.getSql(), not(containsString("a_client_character_set"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order_seed"))); + assertThat(stmt.getSql(), not(containsString("a_tags"))); + } + + @Test + void testGettingPre317Version_from_312() throws SQLException { + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.V3_1_2); + assertThat(stmt.getSql(), containsString("a_fail_on_errors")); + assertThat(stmt.getSql(), containsString("a_client_character_set")); + assertThat(stmt.getSql(), not(containsString("a_random_test_order"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order_seed"))); + assertThat(stmt.getSql(), not(containsString("a_tags"))); + } + + @Test + void testGettingPre317Version_from_316() throws SQLException { + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.V3_1_6); + assertThat(stmt.getSql(), containsString("a_fail_on_errors")); + assertThat(stmt.getSql(), containsString("a_client_character_set")); + assertThat(stmt.getSql(), not(containsString("a_random_test_order"))); + assertThat(stmt.getSql(), not(containsString("a_random_test_order_seed"))); + assertThat(stmt.getSql(), not(containsString("a_tags"))); } @Test - void testGettingActualVersion() throws SQLException { - TestRunnerStatement stmt = TestRunnerStatementProvider.getCompatibleTestRunnerStatement(Version.V3_1_2, new TestRunnerOptions(), getConnection()); - assertEquals(ActualTestRunnerStatement.class, stmt.getClass()); + void testGettingActualVersion_from_latest() throws SQLException { + TestRunnerStatement stmt = getTestRunnerStatementForVersion(Version.LATEST); + assertThat(stmt.getSql(), containsString("a_fail_on_errors")); + assertThat(stmt.getSql(), containsString("a_client_character_set")); + assertThat(stmt.getSql(), containsString("a_random_test_order")); + assertThat(stmt.getSql(), containsString("a_random_test_order_seed")); + assertThat(stmt.getSql(), containsString("a_tags")); } }