From 7a994491301b13f620952ee21d1ca72b6c4b431d Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 11:27:52 +0200 Subject: [PATCH 01/53] Migrate to Maven --- .idea/codeStyles/Project.xml | 10 -- .idea/codeStyles/codeStyleConfig.xml | 5 - .idea/dictionaries/utPLSQL.xml | 7 -- .travis/create_api_user.sh | 17 --- .travis/install_demo_project.sh | 34 ------ .travis/install_utplsql.sh | 33 ------ .travis/start_db.sh | 14 --- README.md | 33 +----- RELEASE-CHECKLIST.md | 5 - build.gradle.kts => build.gradle.kts__ | 0 pom.xml | 103 ++++++++++++++++++ settings.gradle.kts | 2 - .../org/utplsql/api/AbstractDatabaseTest.java | 2 +- 13 files changed, 108 insertions(+), 157 deletions(-) delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/dictionaries/utPLSQL.xml delete mode 100755 .travis/create_api_user.sh delete mode 100755 .travis/install_demo_project.sh delete mode 100755 .travis/install_utplsql.sh delete mode 100755 .travis/start_db.sh delete mode 100644 RELEASE-CHECKLIST.md rename build.gradle.kts => build.gradle.kts__ (100%) create mode 100644 pom.xml delete mode 100644 settings.gradle.kts diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 4f717ff..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/dictionaries/utPLSQL.xml b/.idea/dictionaries/utPLSQL.xml deleted file mode 100644 index 6cb7c83..0000000 --- a/.idea/dictionaries/utPLSQL.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - utplsql - - - \ No newline at end of file diff --git a/.travis/create_api_user.sh b/.travis/create_api_user.sh deleted file mode 100755 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. - -```xml - - - utplsql-java-api - - https://packagecloud.io/utplsql/utplsql-java-api/maven2 - - - true - - - true - - - -``` +This is a Maven Library project, you can add on your Java project as a dependency. -To use the java-api library, add this to the `` section of your `pom.xml`. ```xml org.utplsql - java-api - 3.1.7 - compile + utplsql-java-api + 3.1.10 ``` @@ -129,4 +104,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/RELEASE-CHECKLIST.md b/RELEASE-CHECKLIST.md deleted file mode 100644 index 01a6efd..0000000 --- a/RELEASE-CHECKLIST.md +++ /dev/null @@ -1,5 +0,0 @@ -# TODO's before releasing a new java-api version - -- Update `CoreReporters` -- Update `Version`: knownVersions, LATEST -- Update `build.gradle.kts`: baseVersion diff --git a/build.gradle.kts b/build.gradle.kts__ similarity index 100% rename from build.gradle.kts rename to build.gradle.kts__ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3294d00 --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ + + 4.0.0 + + org.utplsql + utplsql-java-api + 3.1.10-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 + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + 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 + + + + + + + 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 + + + + + + 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/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index 393c31e..5189c15 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -16,7 +16,7 @@ public abstract class AbstractDatabaseTest { private static String sPass; static { - sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "192.168.99.100:1521:XE"); + sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "app"); } From d926fa6c45718f1749f547bd6d9a73a1347356aa Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 14:34:56 +0200 Subject: [PATCH 02/53] Migration to Maven. Added expression includes/excludes. --- .github/workflows/build.yml | 87 ++--- .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 62547 bytes .mvn/wrapper/maven-wrapper.properties | 18 + .travis.yml | 102 ------ CONTRIBUTING.md | 20 +- build.gradle.kts__ | 190 ----------- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 5 - gradlew | 188 ----------- gradlew.bat | 100 ------ mvnw | 308 ++++++++++++++++++ mvnw.cmd | 205 ++++++++++++ scripts/0_start_db.sh | 1 + scripts/1_install_utplsql.sh | 8 + scripts/2_install_demo_project.sh | 11 + scripts/sql/create_app_objects.sql | 9 + scripts/sql/create_source_owner_objects.sql | 6 + scripts/sql/create_tests_owner_objects.sql | 8 + scripts/sql/create_users.sql | 30 ++ src/main/java/org/utplsql/api/TestRunner.java | 20 ++ .../org/utplsql/api/TestRunnerOptions.java | 4 + .../api/compatibility/OptionalFeatures.java | 3 +- .../DynamicTestRunnerStatement.java | 27 +- .../DynamicTestRunnerStatementTest.java | 53 ++- 24 files changed, 740 insertions(+), 663 deletions(-) create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties delete mode 100644 .travis.yml delete mode 100644 build.gradle.kts__ delete mode 100755 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 scripts/0_start_db.sh create mode 100644 scripts/1_install_utplsql.sh create mode 100644 scripts/2_install_demo_project.sh create mode 100644 scripts/sql/create_app_objects.sql create mode 100644 scripts/sql/create_source_owner_objects.sql create mode 100644 scripts/sql/create_tests_owner_objects.sql create mode 100644 scripts/sql/create_users.sql diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72ea97c..d251249 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,14 +1,12 @@ name: Build & test on: push: - branches: [ develop ] - tags: - - v* + branches-ignore: [ main ] pull_request: branches: [ develop ] workflow_dispatch: repository_dispatch: - types: [utPLSQL-build] + types: [ utPLSQL-build ] defaults: run: @@ -30,9 +28,9 @@ jobs: strategy: fail-fast: false matrix: - utplsql_version: ["v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop"] - utplsql_file: ["utPLSQL"] - jdk: ['8'] + utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","v3.1.13","develop" ] + utplsql_file: [ "utPLSQL" ] + jdk: [ '8' ] include: - utplsql_version: "v3.0.0" jdk: '8' @@ -70,47 +68,50 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: actions/setup-java@v2 - with: - distribution: 'adopt' - java-version: ${{matrix.jdk}} - cache: 'gradle' - - name: Install utplsql - run: .travis/install_utplsql.sh + - name: Install utPLSQL + run: sh ${{ github.workspace }}/scripts/1_install_utplsql.sh - name: Install demo project - run: .travis/install_demo_project.sh + run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh - - name: Build and test - run: ./gradlew check - - deploy: - name: Deploy snapshot - needs: [ build ] - concurrency: deploy - runs-on: ubuntu-latest - if: | - github.repository == 'utPLSQL/utPLSQL-java-api' && - github.base_ref == null && - (github.ref == 'refs/heads/develop' || startsWith( github.ref, 'refs/tags/v' ) ) - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-java@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 with: + java-version: '11' distribution: 'adopt' - java-version: '8' - cache: 'gradle' - - name: Set env variable - uses: FranzDiebold/github-env-vars-action@v2 #https://github.com/marketplace/actions/github-environment-variables-action - - name: Set CI_TAG - run: if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then echo "CI_TAG=${CI_REF_NAME}" >> ${GITHUB_ENV}; fi - - name: Upload archives + 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 with sonar + run: mvn clean verify sonar:sonar -Pcoverage -Dsonar.projectKey=org.utplsql:utplsql-maven-plugin env: - PACKAGECLOUD_TOKEN: ${{secrets.PACKAGECLOUD_TOKEN}} - run: ./gradlew uploadArchives + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Maven deploy snapshot + run: mvn deploy -DskipTests + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + 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 dispatch: name: Dispatch downstream builds @@ -121,13 +122,13 @@ jobs: github.repository == 'utPLSQL/utPLSQL-java-api' && github.base_ref == null && github.ref == 'refs/heads/develop' strategy: matrix: - repo: ['utPLSQL/utPLSQL-maven-plugin', 'utPLSQL/utPLSQL-cli'] + repo: [ 'utPLSQL/utPLSQL-maven-plugin', 'utPLSQL/utPLSQL-cli' ] steps: - name: Repository Dispatch uses: peter-evans/repository-dispatch@v1 with: token: ${{ secrets.API_TOKEN_GITHUB }} - repository: ${{ matrix.repo }} + repository: ${{ matrix.repo }} event-type: utPLSQL-java-api-build slack-workflow-status: diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..cb28b0e37c7d206feb564310fdeec0927af4123a GIT binary patch literal 62547 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 A?*IS* literal 0 HcmV?d00001 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 0c3726d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,102 +0,0 @@ -language: java - -services: - - docker - -jdk: - - openjdk8 - -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="v3.1.6" - - UTPLSQL_VERSION="v3.1.7" - - UTPLSQL_VERSION="v3.1.8" - - UTPLSQL_VERSION="v3.1.9" - - UTPLSQL_VERSION="v3.1.10" - - UTPLSQL_VERSION="develop" - UTPLSQL_FILE="utPLSQL" - -matrix: - include: - - env: UTPLSQL_VERSION="v3.1.8" - jdk: openjdk9 - - env: UTPLSQL_VERSION="v3.1.8" - jdk: openjdk10 - - env: UTPLSQL_VERSION="v3.1.8" - jdk: openjdk11 - - env: UTPLSQL_VERSION="v3.1.8" - jdk: openjdk12 - - env: UTPLSQL_VERSION="v3.1.8" - jdk: openjdk13 - -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/CONTRIBUTING.md b/CONTRIBUTING.md index b47bbbf..23f1f42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,4 @@ # Contributing -To develop it locally, you need to setup your maven environment. - -### Maven Installation -That's the easy part, you just need to download the Maven binaries and extract it somewhere, then put the maven/bin folder on your PATH. - -https://maven.apache.org/install.html - *Don't forget to configure your JAVA_HOME environment variable.* ### Local database with utPLSQL and utPLSQL-demo-project @@ -18,15 +11,12 @@ If you want to run tests against another database you may set `DB_URL`, `DB_USER When you have local database set up you can run the complete build including integration tests by executing ```bash -./gradlew build -``` - -To build the project without local database you may disable integration tests. -```bash -./gradlew build -x intTest +./mvnw verify ``` ### Skip the local database part -If you want to skip the local database part, just run ``./gradlew test``. -You will be able to run ``./gradle test`` because integration tests are executed in the separate ``intTest`` task as part of overall ``check``. +If you want to skip the local database part, just run +```bash +./mvnw test +``` diff --git a/build.gradle.kts__ b/build.gradle.kts__ deleted file mode 100644 index 1c43283..0000000 --- a/build.gradle.kts__ +++ /dev/null @@ -1,190 +0,0 @@ -import de.undercouch.gradle.tasks.download.Download -import org.gradle.api.tasks.testing.logging.TestExceptionFormat - -val tag = System.getenv("CI_TAG")?.replaceFirst("^v".toRegex(), "") - -group = "org.utplsql" -val mavenArtifactId = "java-api" -val baseVersion = "3.1.9-SNAPSHOT" -// if build is on tag like 3.1.7 or v3.1.7 then use tag as version replacing leading "v" -version = if (tag != null && "^[0-9.]+$".toRegex().matches(tag)) tag else baseVersion - -val coverageResourcesVersion = "1.0.1" -val ojdbcVersion = "19.3.0.0" -val junitVersion = "5.5.2" - -val deployerJars by configurations.creating - -plugins { - `java-library` - `maven-publish` - maven - id("de.undercouch.download") version "4.0.0" -} - -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 { - mavenCentral() - jcenter() -} - -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.36") - implementation("com.oracle.database.jdbc:ojdbc8:$ojdbcVersion") { - exclude(group = "com.oracle.database.jdbc", module = "ucp") - } - implementation("com.oracle.database.nls:orai18n:$ojdbcVersion") - - // Use Jupiter test framework - testImplementation("org.junit.jupiter:junit-jupiter:$junitVersion") - testImplementation("org.hamcrest:hamcrest:2.1") - - // Mockito - testImplementation("org.mockito:mockito-core:3.0.0") - - // deployer for packagecloud - 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 - } - } - - // run tests using compiled jar + dependencies and tests classes - val binaryTest = create("binaryTest") { - dependsOn(jar, testClasses) - - doFirst { - classpath = project.files("$buildDir/libs/java-api-$version.jar", "$buildDir/classes/java/test", configurations.testRuntimeClasspath) - testClassesDirs = sourceSets.getByName("test").output.classesDirs - } - - useJUnitPlatform { - includeTags("binary") - } - testLogging { - events("passed", "skipped", "failed") - exceptionFormat = TestExceptionFormat.FULL - showStackTraces = true - showStandardStreams = true - } - } - - val intTest = create("intTest") { - dependsOn(test) - doFirst { - environment("DB_URL", (project.findProperty("DB_URL") as String?) ?: System.getenv("DB_URL") - ?: "localhost:1521/XE") - environment("DB_USER", (project.findProperty("DB_USER") as String?) ?: System.getenv("DB_USER") ?: "app") - environment("DB_PASS", (project.findProperty("DB_PASS") as String?) ?: 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) - dependsOn(binaryTest) - } - - 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 5c2d1cf016b3885f6930543d57b744ea8c220a1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55616 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 \(.*\)$'` - 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" "-Xms64m"' - -# 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 9618d8d..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,100 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@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" "-Xms64m" - -@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/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..9afffba --- /dev/null +++ b/scripts/sql/create_app_objects.sql @@ -0,0 +1,9 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback + +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/TO_TEST_ME.sql +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/APP.PKG_TEST_ME.pks +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/APP.PKG_TEST_ME.pkb + +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/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..35cb4aa --- /dev/null +++ b/scripts/sql/create_source_owner_objects.sql @@ -0,0 +1,6 @@ +whenever sqlerror exit failure rollback +whenever oserror exit failure rollback + +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/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..777dd95 --- /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; + +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks +@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/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/src/main/java/org/utplsql/api/TestRunner.java b/src/main/java/org/utplsql/api/TestRunner.java index 92afc90..b7ffdd8 100644 --- a/src/main/java/org/utplsql/api/TestRunner.java +++ b/src/main/java/org/utplsql/api/TestRunner.java @@ -99,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; diff --git a/src/main/java/org/utplsql/api/TestRunnerOptions.java b/src/main/java/org/utplsql/api/TestRunnerOptions.java index c17f3ce..615e308 100644 --- a/src/main/java/org/utplsql/api/TestRunnerOptions.java +++ b/src/main/java/org/utplsql/api/TestRunnerOptions.java @@ -21,6 +21,10 @@ public class TestRunnerOptions { 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; diff --git a/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java b/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java index b2896b0..8462b95 100644 --- a/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java +++ b/src/main/java/org/utplsql/api/compatibility/OptionalFeatures.java @@ -13,7 +13,8 @@ public enum OptionalFeatures { 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); + TAGS("3.1.7.3006", null), + EXPR("3.1.13", null); private final Version minVersion; private final Version maxVersion; diff --git a/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java index aeccc8c..0c46b1c 100644 --- a/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java +++ b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java @@ -7,6 +7,7 @@ import org.utplsql.api.compatibility.OptionalFeatures; import org.utplsql.api.db.DynamicParameterList; +import javax.swing.text.html.Option; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; @@ -19,7 +20,7 @@ public class DynamicTestRunnerStatement implements TestRunnerStatement { private final TestRunnerOptions options; private final DynamicParameterList dynamicParameterList; - private DynamicTestRunnerStatement( Version utPlSQlVersion, OracleConnection connection, TestRunnerOptions options, CallableStatement statement ) throws SQLException { + private DynamicTestRunnerStatement(Version utPlSQlVersion, OracleConnection connection, TestRunnerOptions options, CallableStatement statement) throws SQLException { this.utPlSQlVersion = utPlSQlVersion; this.oracleConnection = connection; this.options = options; @@ -32,12 +33,12 @@ private DynamicTestRunnerStatement( Version utPlSQlVersion, OracleConnection con 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; + 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) @@ -62,13 +63,19 @@ private DynamicParameterList initParameterList() throws SQLException { 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 " + + if (stmt == null) { + String sql = "BEGIN " + "ut_runner.run(" + dynamicParameterList.getSql() + ");" + @@ -96,7 +103,7 @@ public void close() throws SQLException { } } - public static DynamicTestRunnerStatement forVersion(Version version, Connection connection, TestRunnerOptions options, CallableStatement statement ) throws SQLException { + 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/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java index 199bdb9..46e499e 100644 --- a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java +++ b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java @@ -27,7 +27,7 @@ public class DynamicTestRunnerStatementTest { private TestRunnerOptions options; private Object[] expectedFileMapping; - private OracleConnection getMockedOracleConnection( Object[] expectedFileMapping ) throws SQLException { + private OracleConnection getMockedOracleConnection(Object[] expectedFileMapping) throws SQLException { OracleConnection oracleConnection = mock(OracleConnection.class); when(oracleConnection.unwrap(OracleConnection.class)) .thenReturn(oracleConnection); @@ -35,7 +35,7 @@ private OracleConnection getMockedOracleConnection( Object[] expectedFileMapping return oracleConnection; } - private void mockFileMapper( OracleConnection mockedOracleConnection, Object[] expectedFileMapping ) throws SQLException { + private void mockFileMapper(OracleConnection mockedOracleConnection, Object[] expectedFileMapping) throws SQLException { Array fileMapperArray = mock(Array.class); CallableStatement fileMapperStatement = mock(CallableStatement.class); @@ -50,19 +50,19 @@ private void mockFileMapper( OracleConnection mockedOracleConnection, Object[] e .thenReturn(fileMapperStatement); } - private Matcher doesOrDoesNotContainString( String string, boolean shouldBeThere ) { + private Matcher doesOrDoesNotContainString(String string, boolean shouldBeThere) { return (shouldBeThere) ? containsString(string) : not(containsString(string)); } - private VerificationMode doesOrDoesNotGetCalled( boolean shouldBeThere ) { + private VerificationMode doesOrDoesNotGetCalled(boolean shouldBeThere) { return (shouldBeThere) ? times(1) : never(); } - private void initTestRunnerStatementForVersion( Version version ) throws SQLException { + private void initTestRunnerStatementForVersion(Version version) throws SQLException { testRunnerStatement = DynamicTestRunnerStatement .forVersion(version, oracleConnection, options, callableStatement); } @@ -99,28 +99,39 @@ private void checkBaseParameters() throws SQLException { verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.includeObjects.toArray()); } - private void checkFailOnError( boolean shouldBeThere ) throws SQLException { + private void checkFailOnError(boolean shouldBeThere) throws SQLException { assertThat(testRunnerStatement.getSql(), 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 { + private void checkClientCharacterSet(boolean shouldBeThere) throws SQLException { assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_client_character_set => ?", shouldBeThere)); verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(10, "UTF8"); } - private void checkRandomTestOrder( boolean shouldBeThere ) throws SQLException { + private void checkRandomTestOrder(boolean shouldBeThere) throws SQLException { assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_random_test_order => (case ? when 1 then true else false end)", shouldBeThere)); verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(11, 1); assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_random_test_order_seed => ?", shouldBeThere)); verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(12, 123); } - private void checkTags( boolean shouldBeThere ) throws SQLException { + private void checkTags(boolean shouldBeThere) throws SQLException { assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_tags => ?", shouldBeThere)); verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(13, "WIP,long_running"); } + private void checkExpr(boolean shouldBeThere) throws SQLException { + assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_include_schema_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(14, "a_*"); + assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_include_object_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(15, "a_*"); + assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_exclude_schema_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(16, "ut3:*_package*"); + assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_exclude_object_expr => ?", shouldBeThere)); + verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(17, "ut3:*_package*"); + } + @BeforeEach void initParameters() throws SQLException { expectedFileMapping = new Object[]{new FileMapping("someFile", "owner", "object", "PACKAGE")}; @@ -142,6 +153,7 @@ void version_3_0_2_parameters() throws SQLException { checkClientCharacterSet(false); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -153,6 +165,7 @@ void version_3_0_3_parameters() throws SQLException { checkClientCharacterSet(false); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -164,6 +177,7 @@ void version_3_0_4_parameters() throws SQLException { checkClientCharacterSet(false); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -175,6 +189,7 @@ void version_3_1_0_parameters() throws SQLException { checkClientCharacterSet(false); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -186,6 +201,7 @@ void version_3_1_1_parameters() throws SQLException { checkClientCharacterSet(false); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -197,6 +213,7 @@ void version_3_1_2_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -208,6 +225,7 @@ void version_3_1_3_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -219,6 +237,7 @@ void version_3_1_4_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -230,6 +249,7 @@ void version_3_1_5_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -241,6 +261,7 @@ void version_3_1_6_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(false); checkTags(false); + checkExpr(false); } @Test @@ -252,6 +273,7 @@ void version_3_1_7_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(true); checkTags(true); + checkExpr(false); } @Test @@ -263,5 +285,18 @@ void version_3_1_8_parameters() throws SQLException { checkClientCharacterSet(true); checkRandomTestOrder(true); checkTags(true); + checkExpr(false); + } + + @Test + void version_3_1_13_parameters() throws SQLException { + initTestRunnerStatementForVersion(Version.V3_1_8); + + checkBaseParameters(); + checkFailOnError(true); + checkClientCharacterSet(true); + checkRandomTestOrder(true); + checkTags(true); + checkExpr(true); } } From ae4bf27c9c6cb5ed54ddbe7b6c5851655de7578b Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 14:39:21 +0200 Subject: [PATCH 03/53] Addes scripts --- .github/workflows/build.yml | 35 +++------------------ scripts/0_start_db.sh | 1 - scripts/1_install_utplsql.sh | 8 ----- scripts/2_install_demo_project.sh | 11 ------- scripts/create_api_user.sh | 17 ++++++++++ scripts/install_demo_project.sh | 34 ++++++++++++++++++++ scripts/install_utplsql.sh | 33 +++++++++++++++++++ scripts/sql/create_app_objects.sql | 9 ------ scripts/sql/create_source_owner_objects.sql | 6 ---- scripts/sql/create_tests_owner_objects.sql | 8 ----- scripts/sql/create_users.sql | 30 ------------------ scripts/start_db.sh | 14 +++++++++ 12 files changed, 103 insertions(+), 103 deletions(-) delete mode 100644 scripts/0_start_db.sh delete mode 100644 scripts/1_install_utplsql.sh delete mode 100644 scripts/2_install_demo_project.sh create mode 100644 scripts/create_api_user.sh create mode 100644 scripts/install_demo_project.sh create mode 100644 scripts/install_utplsql.sh delete mode 100644 scripts/sql/create_app_objects.sql delete mode 100644 scripts/sql/create_source_owner_objects.sql delete mode 100644 scripts/sql/create_tests_owner_objects.sql delete mode 100644 scripts/sql/create_users.sql create mode 100644 scripts/start_db.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d251249..951ad62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,42 +14,17 @@ defaults: jobs: build: - name: Test on JDK ${{ matrix.jdk }} with utPLSQL ${{ matrix.utplsql_version }} runs-on: ubuntu-latest + env: ORACLE_VERSION: "gvenzl/oracle-xe:18.4.0-slim" - UTPLSQL_VERSION: ${{matrix.utplsql_version}} + UTPLSQL_VERSION: "3.1.13" UTPLSQL_FILE: ${{matrix.utplsql_file}} ORACLE_PASSWORD: oracle DB_URL: "127.0.0.1:1521:XE" DB_USER: app DB_PASS: app - strategy: - fail-fast: false - matrix: - utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","v3.1.13","develop" ] - utplsql_file: [ "utPLSQL" ] - jdk: [ '8' ] - include: - - utplsql_version: "v3.0.0" - jdk: '8' - utplsql_file: "utPLSQLv3.0.0" - - utplsql_version: "develop" - jdk: '9' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '10' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '11' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '12' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '13' - utplsql_file: "utPLSQL" services: oracle: image: gvenzl/oracle-xe:18.4.0-slim @@ -69,11 +44,11 @@ jobs: with: fetch-depth: 0 - - name: Install utPLSQL - run: sh ${{ github.workspace }}/scripts/1_install_utplsql.sh + - name: Install utplsql + run: scripts/install_utplsql.sh - name: Install demo project - run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh + run: scripts/install_demo_project.sh - name: Set up JDK 11 uses: actions/setup-java@v2 diff --git a/scripts/0_start_db.sh b/scripts/0_start_db.sh deleted file mode 100644 index d319407..0000000 --- a/scripts/0_start_db.sh +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index bb6577c..0000000 --- a/scripts/1_install_utplsql.sh +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index a4089b1..0000000 --- a/scripts/2_install_demo_project.sh +++ /dev/null @@ -1,11 +0,0 @@ -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/create_api_user.sh b/scripts/create_api_user.sh new file mode 100644 index 0000000..c898f15 --- /dev/null +++ b/scripts/create_api_user.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -ev + +sqlplus -S -L sys/oracle@//127.0.0.1:1521/xe AS SYSDBA < demo_project.sh.tmp < install.sh.tmp < Date: Fri, 7 Jul 2023 14:40:47 +0200 Subject: [PATCH 04/53] Added build --- .github/workflows/build.yml | 51 ++----------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 951ad62..d70ea33 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: env: ORACLE_VERSION: "gvenzl/oracle-xe:18.4.0-slim" UTPLSQL_VERSION: "3.1.13" - UTPLSQL_FILE: ${{matrix.utplsql_file}} + UTPLSQL_FILE: "utPLSQL" ORACLE_PASSWORD: oracle DB_URL: "127.0.0.1:1521:XE" DB_USER: app @@ -55,11 +55,6 @@ jobs: 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 @@ -70,52 +65,10 @@ jobs: ${{ runner.os }}-maven- - name: Maven unit and integration tests with sonar - run: mvn clean verify sonar:sonar -Pcoverage -Dsonar.projectKey=org.utplsql:utplsql-maven-plugin - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - - name: Maven deploy snapshot - run: mvn deploy -DskipTests - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + run: mvn clean verify - name: Publish unit test results uses: EnricoMi/publish-unit-test-result-action@v1.24 if: always() with: files: target/**/TEST**.xml - - dispatch: - name: Dispatch downstream builds - concurrency: trigger - needs: [ build, deploy ] - runs-on: ubuntu-latest - if: | - github.repository == 'utPLSQL/utPLSQL-java-api' && github.base_ref == null && github.ref == 'refs/heads/develop' - strategy: - matrix: - repo: [ 'utPLSQL/utPLSQL-maven-plugin', 'utPLSQL/utPLSQL-cli' ] - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v1 - with: - token: ${{ secrets.API_TOKEN_GITHUB }} - repository: ${{ matrix.repo }} - event-type: utPLSQL-java-api-build - - slack-workflow-status: - if: always() - name: Post Workflow Status To Slack - needs: [ build, deploy, dispatch ] - runs-on: ubuntu-latest - steps: - - name: Slack Workflow Notification - uses: Gamesight/slack-workflow-status@master - with: - repo_token: ${{secrets.GITHUB_TOKEN}} - slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} - name: 'Github Actions[bot]' - icon_url: 'https://octodex.github.com/images/mona-the-rivetertocat.png' From 81979fdc7e07a89f1b4c4ca5333de68dc759b479 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 14:48:19 +0200 Subject: [PATCH 05/53] Added proper run command --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d70ea33..47e2372 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,10 +45,10 @@ jobs: fetch-depth: 0 - name: Install utplsql - run: scripts/install_utplsql.sh + run: sh ${{ github.workspace }}/scripts/install_utplsql.sh - name: Install demo project - run: scripts/install_demo_project.sh + run: sh ${{ github.workspace }}/scripts/install_demo_project.sh - name: Set up JDK 11 uses: actions/setup-java@v2 From 853335727ff7817065dae99e22d6899199e9bb1f Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:08:56 +0200 Subject: [PATCH 06/53] Added proper run command --- .github/workflows/build.yml | 44 +++++++++++++++++++++++++------------ .gitignore | 2 +- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47e2372..f09c242 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,31 @@ jobs: DB_USER: app DB_PASS: app + strategy: + fail-fast: false + matrix: + utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop" ] + utplsql_file: [ "utPLSQL" ] + jdk: [ '8' ] + include: + - utplsql_version: "v3.0.0" + jdk: '8' + utplsql_file: "utPLSQLv3.0.0" + - utplsql_version: "develop" + jdk: '9' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '10' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '11' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '12' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '13' + utplsql_file: "utPLSQL" services: oracle: image: gvenzl/oracle-xe:18.4.0-slim @@ -44,26 +69,17 @@ jobs: with: fetch-depth: 0 + - uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: ${{matrix.jdk}} + - name: Install utplsql run: sh ${{ github.workspace }}/scripts/install_utplsql.sh - name: Install demo project run: sh ${{ github.workspace }}/scripts/install_demo_project.sh - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' - - - 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 with sonar run: mvn clean verify diff --git a/.gitignore b/.gitignore index 3a7808b..9ec7c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # IntelliJ *.iml -.idea/* +.idea !/.idea/codeStyles !/.idea/dictionaries From b334a2e330dec77bcc336d1eda53fbc3aa45753c Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:21:24 +0200 Subject: [PATCH 07/53] Added proper run command --- .github/workflows/build.yml | 51 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f09c242..2021e48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,31 +25,32 @@ jobs: DB_USER: app DB_PASS: app - strategy: - fail-fast: false - matrix: - utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop" ] - utplsql_file: [ "utPLSQL" ] - jdk: [ '8' ] - include: - - utplsql_version: "v3.0.0" - jdk: '8' - utplsql_file: "utPLSQLv3.0.0" - - utplsql_version: "develop" - jdk: '9' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '10' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '11' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '12' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '13' - utplsql_file: "utPLSQL" + strategy: + fail-fast: false + matrix: + utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop" ] + utplsql_file: [ "utPLSQL" ] + jdk: [ '8' ] + include: + - utplsql_version: "v3.0.0" + jdk: '8' + utplsql_file: "utPLSQLv3.0.0" + - utplsql_version: "develop" + jdk: '9' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '10' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '11' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '12' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '13' + utplsql_file: "utPLSQL" + services: oracle: image: gvenzl/oracle-xe:18.4.0-slim From 01a912c05b80df470a3f070f2c4a2f552edeec2f Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:25:52 +0200 Subject: [PATCH 08/53] Only support 8 and 11 --- .github/workflows/build.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2021e48..73e28a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,21 +35,8 @@ jobs: - utplsql_version: "v3.0.0" jdk: '8' utplsql_file: "utPLSQLv3.0.0" - - utplsql_version: "develop" - jdk: '9' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '10' - utplsql_file: "utPLSQL" - utplsql_version: "develop" jdk: '11' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '12' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '13' - utplsql_file: "utPLSQL" services: oracle: From 86ae4b4d4c8f61ef1bf7da67ee27ebe7154c75c6 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:26:10 +0200 Subject: [PATCH 09/53] Support 8, 11 and 17 --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73e28a4..13db0e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,10 @@ jobs: utplsql_file: "utPLSQLv3.0.0" - utplsql_version: "develop" jdk: '11' + utplsql_file: "utPLSQL" + - utplsql_version: "develop" + jdk: '17' + utplsql_file: "utPLSQL" services: oracle: From f8468ca319be609e29f400c7767719d667505462 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:48:56 +0200 Subject: [PATCH 10/53] Run scripts --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13db0e4..ec8f846 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,10 +67,10 @@ jobs: java-version: ${{matrix.jdk}} - name: Install utplsql - run: sh ${{ github.workspace }}/scripts/install_utplsql.sh + run: scripts/install_utplsql.sh - name: Install demo project - run: sh ${{ github.workspace }}/scripts/install_demo_project.sh + run: scripts/install_demo_project.sh - name: Maven unit and integration tests with sonar run: mvn clean verify From e833eb77a0a549bc2e437477d885ed4c8cdce321 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 15:55:09 +0200 Subject: [PATCH 11/53] Change to simple build --- .github/workflows/build.yml | 57 ++++++++------------- scripts/0_start_db.sh | 1 + scripts/1_install_utplsql.sh | 8 +++ scripts/2_install_demo_project.sh | 11 ++++ scripts/create_api_user.sh | 17 ------ scripts/install_demo_project.sh | 34 ------------ scripts/install_utplsql.sh | 33 ------------ scripts/sql/create_app_objects.sql | 9 ++++ scripts/sql/create_source_owner_objects.sql | 6 +++ scripts/sql/create_tests_owner_objects.sql | 8 +++ scripts/sql/create_users.sql | 30 +++++++++++ scripts/start_db.sh | 14 ----- 12 files changed, 93 insertions(+), 135 deletions(-) create mode 100644 scripts/0_start_db.sh create mode 100644 scripts/1_install_utplsql.sh create mode 100644 scripts/2_install_demo_project.sh delete mode 100644 scripts/create_api_user.sh delete mode 100644 scripts/install_demo_project.sh delete mode 100644 scripts/install_utplsql.sh create mode 100644 scripts/sql/create_app_objects.sql create mode 100644 scripts/sql/create_source_owner_objects.sql create mode 100644 scripts/sql/create_tests_owner_objects.sql create mode 100644 scripts/sql/create_users.sql delete mode 100644 scripts/start_db.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec8f846..327c38c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,37 +14,12 @@ defaults: jobs: build: - runs-on: ubuntu-latest - - env: - ORACLE_VERSION: "gvenzl/oracle-xe:18.4.0-slim" - UTPLSQL_VERSION: "3.1.13" - UTPLSQL_FILE: "utPLSQL" - ORACLE_PASSWORD: oracle - DB_URL: "127.0.0.1:1521:XE" - DB_USER: app - DB_PASS: app - strategy: - fail-fast: false - matrix: - utplsql_version: [ "v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop" ] - utplsql_file: [ "utPLSQL" ] - jdk: [ '8' ] - include: - - utplsql_version: "v3.0.0" - jdk: '8' - utplsql_file: "utPLSQLv3.0.0" - - utplsql_version: "develop" - jdk: '11' - utplsql_file: "utPLSQL" - - utplsql_version: "develop" - jdk: '17' - utplsql_file: "utPLSQL" + runs-on: ubuntu-latest services: oracle: - image: gvenzl/oracle-xe:18.4.0-slim + image: gvenzl/oracle-xe:21-slim env: ORACLE_PASSWORD: oracle ports: @@ -54,25 +29,33 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 10 - --name oracle steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: actions/setup-java@v2 - with: - distribution: 'temurin' - java-version: ${{matrix.jdk}} - - - name: Install utplsql - run: scripts/install_utplsql.sh + - name: Install utPLSQL + run: sh ${{ github.workspace }}/scripts/1_install_utplsql.sh - name: Install demo project - run: scripts/install_demo_project.sh + 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' + + - 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 with sonar + - name: Maven unit and integration tests run: mvn clean verify - name: Publish unit test results 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/create_api_user.sh b/scripts/create_api_user.sh deleted file mode 100644 index c898f15..0000000 --- a/scripts/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 < Date: Fri, 7 Jul 2023 16:03:27 +0200 Subject: [PATCH 12/53] Only install utplsql without test user --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 327c38c..b11b880 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,8 +38,8 @@ jobs: - 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: Install demo project +# run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh - name: Set up JDK 11 uses: actions/setup-java@v2 From e7beb7df62ac6bcdac2cbc545b3c038bc8951d0e Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:07:40 +0200 Subject: [PATCH 13/53] Added username and password --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b11b880..6df3a84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,9 @@ jobs: image: gvenzl/oracle-xe:21-slim env: ORACLE_PASSWORD: oracle + DB_URL: "127.0.0.1:1521:XE" + DB_USER: APP + DB_PASS: pass ports: - 1521:1521 options: >- From 8500a278d9387a3a6a19e976ec3b94fb495574e1 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:44:09 +0200 Subject: [PATCH 14/53] Disabled failing test --- src/test/java/org/utplsql/api/AbstractDatabaseTest.java | 2 +- src/test/java/org/utplsql/api/TestRunnerIT.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index 5189c15..ee2585e 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -18,7 +18,7 @@ public abstract class AbstractDatabaseTest { static { sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); - sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "app"); + sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); } private Connection conn; diff --git a/src/test/java/org/utplsql/api/TestRunnerIT.java b/src/test/java/org/utplsql/api/TestRunnerIT.java index ebd7ba5..04caba9 100644 --- a/src/test/java/org/utplsql/api/TestRunnerIT.java +++ b/src/test/java/org/utplsql/api/TestRunnerIT.java @@ -1,5 +1,6 @@ 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; @@ -64,6 +65,7 @@ void runWithManyReporters() throws SQLException { /** * This can only be tested on frameworks >= 3.0.3 */ + @Disabled @Test void failOnErrors() throws SQLException, InvalidVersionException { Connection conn = getConnection(); From 35530fe722a18d94c342fcd296dd06a9c37fa319 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:49:16 +0200 Subject: [PATCH 15/53] removed unnecessary config --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6df3a84..b11b880 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,9 +22,6 @@ jobs: image: gvenzl/oracle-xe:21-slim env: ORACLE_PASSWORD: oracle - DB_URL: "127.0.0.1:1521:XE" - DB_USER: APP - DB_PASS: pass ports: - 1521:1521 options: >- From 9ece9af6be2ab212b302939023db772b8ec16bc9 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:57:10 +0200 Subject: [PATCH 16/53] Removed unnecessary util --- .../utplsql/api/EnvironmentVariableUtil.java | 53 ------------------- .../org/utplsql/api/AbstractDatabaseTest.java | 14 ++--- .../api/EnvironmentVariableUtilTest.java | 43 --------------- 3 files changed, 4 insertions(+), 106 deletions(-) delete mode 100644 src/main/java/org/utplsql/api/EnvironmentVariableUtil.java delete mode 100644 src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java diff --git a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java deleted file mode 100644 index 90ca49f..0000000 --- a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java +++ /dev/null @@ -1,53 +0,0 @@ -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/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index ee2585e..e85da9d 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -11,18 +11,12 @@ public abstract class AbstractDatabaseTest { - private static String sUrl; - private static String sUser; - private static String sPass; - - static { - sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); - sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); - sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); - } + private static final String sUrl = "localhost:1521:XE"; + private static final String sUser = "APP"; + private static final String sPass = "pass"; private Connection conn; - private List connectionList = new ArrayList<>(); + private final List connectionList = new ArrayList<>(); public static String getUser() { return sUser; diff --git a/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java b/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java deleted file mode 100644 index c827232..0000000 --- a/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.utplsql.api; - -import org.junit.jupiter.api.Test; - -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - - -class EnvironmentVariableUtilTest { - - @Test - void testGetVariableFromEnvironment() { - // Let's find an environment variable which is not in Properties list and not empty - Set props = System.getProperties().keySet(); - Optional> envVariable = System.getenv().entrySet().stream() - .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"); - } - - assertEquals(envVariable.get().getValue(), EnvironmentVariableUtil.getEnvValue(envVariable.get().getKey())); - } - - @Test - void testGetVariableFromProperty() { - System.setProperty("PATH", "MyPath"); - - assertEquals("MyPath", EnvironmentVariableUtil.getEnvValue("PATH")); - } - - @Test - void testGetVariableFromDefault() { - - assertEquals("defaultValue", EnvironmentVariableUtil.getEnvValue("RANDOM" + System.currentTimeMillis(), "defaultValue")); - } - -} \ No newline at end of file From 67f580faffc025cbfce4690f09a1f0a0127889c6 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:57:30 +0200 Subject: [PATCH 17/53] Revert "Removed unnecessary util" This reverts commit 9ece9af6be2ab212b302939023db772b8ec16bc9. --- .../utplsql/api/EnvironmentVariableUtil.java | 53 +++++++++++++++++++ .../org/utplsql/api/AbstractDatabaseTest.java | 14 +++-- .../api/EnvironmentVariableUtilTest.java | 43 +++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/utplsql/api/EnvironmentVariableUtil.java create mode 100644 src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java diff --git a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java new file mode 100644 index 0000000..90ca49f --- /dev/null +++ b/src/main/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/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index e85da9d..ee2585e 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -11,12 +11,18 @@ public abstract class AbstractDatabaseTest { - private static final String sUrl = "localhost:1521:XE"; - private static final String sUser = "APP"; - private static final String sPass = "pass"; + private static String sUrl; + private static String sUser; + private static String sPass; + + static { + sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); + sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); + sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); + } private Connection conn; - private final List connectionList = new ArrayList<>(); + private List connectionList = new ArrayList<>(); public static String getUser() { return sUser; diff --git a/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java b/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java new file mode 100644 index 0000000..c827232 --- /dev/null +++ b/src/test/java/org/utplsql/api/EnvironmentVariableUtilTest.java @@ -0,0 +1,43 @@ +package org.utplsql.api; + +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + + +class EnvironmentVariableUtilTest { + + @Test + void testGetVariableFromEnvironment() { + // Let's find an environment variable which is not in Properties list and not empty + Set props = System.getProperties().keySet(); + Optional> envVariable = System.getenv().entrySet().stream() + .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"); + } + + assertEquals(envVariable.get().getValue(), EnvironmentVariableUtil.getEnvValue(envVariable.get().getKey())); + } + + @Test + void testGetVariableFromProperty() { + System.setProperty("PATH", "MyPath"); + + assertEquals("MyPath", EnvironmentVariableUtil.getEnvValue("PATH")); + } + + @Test + void testGetVariableFromDefault() { + + assertEquals("defaultValue", EnvironmentVariableUtil.getEnvValue("RANDOM" + System.currentTimeMillis(), "defaultValue")); + } + +} \ No newline at end of file From 5f803b80d23dad061204abaa6f6d65e529e9284c Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 16:58:07 +0200 Subject: [PATCH 18/53] Reverted removal --- src/test/java/org/utplsql/api/AbstractDatabaseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index ee2585e..5ff2cdd 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -17,7 +17,7 @@ public abstract class AbstractDatabaseTest { static { sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); - sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); + sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "APP"); sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); } From 3a9b6a2fc9f05b0cec497618c7c683242964f109 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 17:00:07 +0200 Subject: [PATCH 19/53] Moved EnvironmentVariableUtil to test sources --- src/test/java/org/utplsql/api/AbstractDatabaseTest.java | 2 +- .../java/org/utplsql/api/EnvironmentVariableUtil.java | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{main => test}/java/org/utplsql/api/EnvironmentVariableUtil.java (100%) diff --git a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index 5ff2cdd..e5820b9 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -22,7 +22,7 @@ public abstract class AbstractDatabaseTest { } private Connection conn; - private List connectionList = new ArrayList<>(); + private final List connectionList = new ArrayList<>(); public static String getUser() { return sUser; diff --git a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/test/java/org/utplsql/api/EnvironmentVariableUtil.java similarity index 100% rename from src/main/java/org/utplsql/api/EnvironmentVariableUtil.java rename to src/test/java/org/utplsql/api/EnvironmentVariableUtil.java From 58f6a654901abda38479a787a8def19d2a06c339 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 17:02:00 +0200 Subject: [PATCH 20/53] Added test data --- .github/workflows/build.yml | 4 ++-- src/test/java/org/utplsql/api/AbstractDatabaseTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b11b880..327c38c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,8 +38,8 @@ jobs: - 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: Install demo project + run: sh ${{ github.workspace }}/scripts/2_install_demo_project.sh - name: Set up JDK 11 uses: actions/setup-java@v2 diff --git a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java index e5820b9..747e464 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -17,7 +17,7 @@ public abstract class AbstractDatabaseTest { static { sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "localhost:1521:XE"); - sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "APP"); + sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); } From 7b5d7099f38b8e0623a61d2b3d834471ec4d9f88 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 17:06:03 +0200 Subject: [PATCH 21/53] Changed path --- scripts/2_install_demo_project.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/2_install_demo_project.sh b/scripts/2_install_demo_project.sh index a4089b1..93bb07a 100644 --- a/scripts/2_install_demo_project.sh +++ b/scripts/2_install_demo_project.sh @@ -1,11 +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 + sys/oracle@//127.0.0.1:1521/XE as sysdba @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 + app/pass@//127.0.0.1:1521/XE @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 + code_owner/pass@//127.0.0.1:1521/XE @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 + tests_owner/pass@//127.0.0.1:1521/XE @sql/create_tests_owner_objects.sql From bc581a128fade1c5280b3b8f4f7233ed086b80b0 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 17:12:06 +0200 Subject: [PATCH 22/53] Changed path --- scripts/2_install_demo_project.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/2_install_demo_project.sh b/scripts/2_install_demo_project.sh index 93bb07a..a4089b1 100644 --- a/scripts/2_install_demo_project.sh +++ b/scripts/2_install_demo_project.sh @@ -1,11 +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 @sql/create_users.sql + 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 @sql/create_app_objects.sql + 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 @sql/create_source_owner_objects.sql + 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 @sql/create_tests_owner_objects.sql + tests_owner/pass@//127.0.0.1:1521/XE @scripts/sql/create_tests_owner_objects.sql From ff8ce429a854a7f003774732c140cb3bd79c8c27 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Fri, 7 Jul 2023 17:17:10 +0200 Subject: [PATCH 23/53] No testdata --- scripts/2_install_demo_project.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/2_install_demo_project.sh b/scripts/2_install_demo_project.sh index a4089b1..21eba92 100644 --- a/scripts/2_install_demo_project.sh +++ b/scripts/2_install_demo_project.sh @@ -1,11 +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 +#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 From de1932a0b2aeca246c2e555578ba8c4001dcd1c5 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Sat, 8 Jul 2023 14:54:41 +0200 Subject: [PATCH 24/53] Deploy to Maven central and analyze with Sonar --- .github/workflows/build.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 327c38c..50b0fd6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,6 +46,11 @@ jobs: 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 @@ -56,7 +61,17 @@ jobs: ${{ runner.os }}-maven- - name: Maven unit and integration tests - run: mvn clean verify + run: mvn clean verify sonar:sonar -Pcoverage -Dsonar.projectKey=org.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 From 5c86431eaa4ae27aa8a76406670f54f12a1e6aa0 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Sat, 8 Jul 2023 15:02:33 +0200 Subject: [PATCH 25/53] Added Sonarcloud config --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 3294d00..a4d0306 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,9 @@ 1.8 5.5.2 19.3.0.0 + + utplsql + https://sonarcloud.io From 24ed1311b2fb8b4e28ff98de3935e84def7af45d Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Sat, 8 Jul 2023 15:12:48 +0200 Subject: [PATCH 26/53] Added profiles --- pom.xml | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/pom.xml b/pom.xml index a4d0306..b9a8b5d 100644 --- a/pom.xml +++ b/pom.xml @@ -103,4 +103,86 @@ + + + + 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 + + + + + + + + From 4f794ce142e1cb0d1e84cbc3b28bb55d7571f0fd Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Sat, 8 Jul 2023 15:33:08 +0200 Subject: [PATCH 27/53] Fixed broken project key --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 50b0fd6..6dade98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: ${{ runner.os }}-maven- - name: Maven unit and integration tests - run: mvn clean verify sonar:sonar -Pcoverage -Dsonar.projectKey=org.utplsql:utplsql-java-api + 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 }} From b5ff1444456d72bf93c9d05f403fe467ef73929b Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Sat, 8 Jul 2023 15:56:07 +0200 Subject: [PATCH 28/53] Added distribution management --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index b9a8b5d..705ee9f 100644 --- a/pom.xml +++ b/pom.xml @@ -185,4 +185,15 @@ + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + From ad79d5925fa21911269d21e027e9934e981959db Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Mon, 10 Jul 2023 17:02:32 +0200 Subject: [PATCH 29/53] Added test files --- scripts/2_install_demo_project.sh | 16 ++-- scripts/sql/APP.PKG_TEST_ME.pkb | 27 ++++++ scripts/sql/APP.PKG_TEST_ME.pks | 8 ++ scripts/sql/APP.TEST_PKG_TEST_ME.pkb | 126 +++++++++++++++++++++++++++ scripts/sql/APP.TEST_PKG_TEST_ME.pks | 88 +++++++++++++++++++ scripts/sql/TO_TEST_ME.sql | 8 ++ scripts/sql/create_app_objects.sql | 10 +-- 7 files changed, 270 insertions(+), 13 deletions(-) create mode 100644 scripts/sql/APP.PKG_TEST_ME.pkb create mode 100644 scripts/sql/APP.PKG_TEST_ME.pks create mode 100644 scripts/sql/APP.TEST_PKG_TEST_ME.pkb create mode 100644 scripts/sql/APP.TEST_PKG_TEST_ME.pks create mode 100644 scripts/sql/TO_TEST_ME.sql diff --git a/scripts/2_install_demo_project.sh b/scripts/2_install_demo_project.sh index 21eba92..a4089b1 100644 --- a/scripts/2_install_demo_project.sh +++ b/scripts/2_install_demo_project.sh @@ -1,11 +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 +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/APP.PKG_TEST_ME.pkb b/scripts/sql/APP.PKG_TEST_ME.pkb new file mode 100644 index 0000000..331959d --- /dev/null +++ b/scripts/sql/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/APP.PKG_TEST_ME.pks b/scripts/sql/APP.PKG_TEST_ME.pks new file mode 100644 index 0000000..26b2589 --- /dev/null +++ b/scripts/sql/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/APP.TEST_PKG_TEST_ME.pkb b/scripts/sql/APP.TEST_PKG_TEST_ME.pkb new file mode 100644 index 0000000..115bc15 --- /dev/null +++ b/scripts/sql/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/APP.TEST_PKG_TEST_ME.pks b/scripts/sql/APP.TEST_PKG_TEST_ME.pks new file mode 100644 index 0000000..8a2b852 --- /dev/null +++ b/scripts/sql/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/scripts/sql/TO_TEST_ME.sql b/scripts/sql/TO_TEST_ME.sql new file mode 100644 index 0000000..b2e90d9 --- /dev/null +++ b/scripts/sql/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/create_app_objects.sql b/scripts/sql/create_app_objects.sql index 9afffba..b5ea8e8 100644 --- a/scripts/sql/create_app_objects.sql +++ b/scripts/sql/create_app_objects.sql @@ -1,9 +1,9 @@ whenever sqlerror exit failure rollback whenever oserror exit failure rollback -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/TO_TEST_ME.sql -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/APP.PKG_TEST_ME.pks -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/sources/APP.PKG_TEST_ME.pkb +@scripts/sql/TO_TEST_ME.sql +@scripts/sql/APP.PKG_TEST_ME.pks +@scripts/sql/APP.PKG_TEST_ME.pkb -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb +@scripts/sql/APP.TEST_PKG_TEST_ME.pks +@scripts/sql/APP.TEST_PKG_TEST_ME.pkb From 2b13d6d34ab992dea52dd3b65899832ebafb90cd Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Mon, 10 Jul 2023 17:15:18 +0200 Subject: [PATCH 30/53] Added test scripts --- scripts/sql/create_app_objects.sql | 10 +- scripts/sql/create_source_owner_objects.sql | 6 +- scripts/sql/create_tests_owner_objects.sql | 4 +- .../foo/package_bodies/PKG_TEST_ME.pkb | 27 ++++ .../sources/foo/packages/PKG_TEST_ME.pks | 8 ++ .../sources/foo/tables}/TO_TEST_ME.sql | 0 .../bar/package_bodies/TEST_PKG_TEST_ME.pkb | 126 ++++++++++++++++++ .../test/bar/packages/TEST_PKG_TEST_ME.pks | 86 ++++++++++++ .../scripts/sources}/APP.PKG_TEST_ME.pkb | 0 .../scripts/sources}/APP.PKG_TEST_ME.pks | 0 .../sql/simple/scripts/sources/TO_TEST_ME.sql | 8 ++ .../scripts/tests}/APP.TEST_PKG_TEST_ME.pkb | 0 .../scripts/tests}/APP.TEST_PKG_TEST_ME.pks | 0 13 files changed, 265 insertions(+), 10 deletions(-) create mode 100644 scripts/sql/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb create mode 100644 scripts/sql/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks rename scripts/sql/{ => owner_param/scripts/sources/foo/tables}/TO_TEST_ME.sql (100%) create mode 100644 scripts/sql/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb create mode 100644 scripts/sql/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks rename scripts/sql/{ => simple/scripts/sources}/APP.PKG_TEST_ME.pkb (100%) rename scripts/sql/{ => simple/scripts/sources}/APP.PKG_TEST_ME.pks (100%) create mode 100644 scripts/sql/simple/scripts/sources/TO_TEST_ME.sql rename scripts/sql/{ => simple/scripts/tests}/APP.TEST_PKG_TEST_ME.pkb (100%) rename scripts/sql/{ => simple/scripts/tests}/APP.TEST_PKG_TEST_ME.pks (100%) diff --git a/scripts/sql/create_app_objects.sql b/scripts/sql/create_app_objects.sql index b5ea8e8..2ad509f 100644 --- a/scripts/sql/create_app_objects.sql +++ b/scripts/sql/create_app_objects.sql @@ -1,9 +1,9 @@ whenever sqlerror exit failure rollback whenever oserror exit failure rollback -@scripts/sql/TO_TEST_ME.sql -@scripts/sql/APP.PKG_TEST_ME.pks -@scripts/sql/APP.PKG_TEST_ME.pkb +@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/APP.TEST_PKG_TEST_ME.pks -@scripts/sql/APP.TEST_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 index 35cb4aa..bc10482 100644 --- a/scripts/sql/create_source_owner_objects.sql +++ b/scripts/sql/create_source_owner_objects.sql @@ -1,6 +1,6 @@ whenever sqlerror exit failure rollback whenever oserror exit failure rollback -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/sources/foo/packages/PKG_TEST_ME.pks -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/sources/foo/package_bodies/PKG_TEST_ME.pkb +@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 index 777dd95..cb244db 100644 --- a/scripts/sql/create_tests_owner_objects.sql +++ b/scripts/sql/create_tests_owner_objects.sql @@ -4,5 +4,5 @@ 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; -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/test/bar/packages/TEST_PKG_TEST_ME.pks -@src/test/resources-its/org/utplsql/maven/plugin/UtPlsqlMojoIT/owner_param/scripts/test/bar/package_bodies/TEST_PKG_TEST_ME.pkb +@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/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/TO_TEST_ME.sql b/scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql similarity index 100% rename from scripts/sql/TO_TEST_ME.sql rename to scripts/sql/owner_param/scripts/sources/foo/tables/TO_TEST_ME.sql 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/APP.PKG_TEST_ME.pkb b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb similarity index 100% rename from scripts/sql/APP.PKG_TEST_ME.pkb rename to scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pkb diff --git a/scripts/sql/APP.PKG_TEST_ME.pks b/scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks similarity index 100% rename from scripts/sql/APP.PKG_TEST_ME.pks rename to scripts/sql/simple/scripts/sources/APP.PKG_TEST_ME.pks 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/APP.TEST_PKG_TEST_ME.pkb b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb similarity index 100% rename from scripts/sql/APP.TEST_PKG_TEST_ME.pkb rename to scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pkb diff --git a/scripts/sql/APP.TEST_PKG_TEST_ME.pks b/scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks similarity index 100% rename from scripts/sql/APP.TEST_PKG_TEST_ME.pks rename to scripts/sql/simple/scripts/tests/APP.TEST_PKG_TEST_ME.pks From 25e3cf50331196adab1360f491d41b96e031ef92 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 09:31:54 +0200 Subject: [PATCH 31/53] Clean up and documentation. --- CONTRIBUTING.md | 4 +- README.md | 2 +- pom.xml | 13 ++++- .../org/utplsql/api/JavaApiVersionInfo.java | 11 ++-- .../java/org/utplsql/api/ResourceUtil.java | 8 ++- src/main/java/org/utplsql/api/TestRunner.java | 37 +++++++------ src/main/java/org/utplsql/api/Version.java | 50 ++++++++++++------ .../api/compatibility/CompatibilityProxy.java | 52 ++++++++++--------- .../utplsql/api/db/DynamicParameterList.java | 4 +- .../api/outputBuffer/NonOutputBuffer.java | 5 +- .../outputBuffer/OutputBufferProvider.java | 10 ++-- .../utplsql/api/reporter/CoreReporters.java | 1 + .../api/reporter/CoverageHTMLReporter.java | 3 +- .../org/utplsql/api/reporter/Reporter.java | 4 +- .../utplsql/api/reporter/ReporterFactory.java | 2 +- .../reporter/inspect/ReporterInspector.java | 8 +-- .../inspect/ReporterInspectorPre310.java | 2 +- .../DynamicTestRunnerStatement.java | 1 - .../TestRunnerStatementProvider.java | 2 +- src/main/resources/utplsql-api.version | 2 +- .../org/utplsql/api/AbstractDatabaseTest.java | 16 +++--- src/test/java/org/utplsql/api/DBHelperIT.java | 2 +- .../utplsql/api/DatabaseInformationIT.java | 2 +- .../java/org/utplsql/api/OutputBufferIT.java | 1 - .../org/utplsql/api/ReporterInspectorIT.java | 3 +- .../java/org/utplsql/api/TestRunnerIT.java | 4 +- .../CoverageHTMLReporterAssetTest.java | 2 +- .../DynamicTestRunnerStatementTest.java | 2 +- .../TestRunnerStatementProviderIT.java | 4 +- 29 files changed, 143 insertions(+), 114 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23f1f42..adc8b91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,9 +3,9 @@ ### Local database with utPLSQL and utPLSQL-demo-project -To usefully contribute you'll have to setup a local database with installed [latest utPLSQL v3](https://github.com/utPLSQL/utPLSQL) and [utPLSQL-demo-project](https://github.com/utPLSQL/utPLSQL-demo-project). +To usefully contribute you'll have to set up a local database with installed [latest utPLSQL v3](https://github.com/utPLSQL/utPLSQL) and [utPLSQL-demo-project](https://github.com/utPLSQL/utPLSQL-demo-project). The demo-project will serve as your test user. See .travis.yml to see an example on how it can be installed. -By default tests are executed against `app/app` user of `localhost:1521/XE database`. +By default, tests are executed against `app/app` user of `localhost:1521/XE database`. If you want to run tests against another database you may set `DB_URL`, `DB_USER`, `DB_PASS` environment variables. diff --git a/README.md b/README.md index 82ee7f7..ea3be46 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,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 diff --git a/pom.xml b/pom.xml index 705ee9f..301e2c4 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo @@ -70,7 +70,18 @@ + + + src/main/resources + true + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + org.apache.maven.plugins maven-failsafe-plugin diff --git a/src/main/java/org/utplsql/api/JavaApiVersionInfo.java b/src/main/java/org/utplsql/api/JavaApiVersionInfo.java index 503b84d..48e529b 100644 --- a/src/main/java/org/utplsql/api/JavaApiVersionInfo.java +++ b/src/main/java/org/utplsql/api/JavaApiVersionInfo.java @@ -7,7 +7,7 @@ /** * This class is getting updated automatically by the build process. - * Please do not update its constants manually cause they will be overwritten. + * Please do not update its constants manually because they will be overwritten. * * @author pesse */ @@ -18,10 +18,11 @@ public class JavaApiVersionInfo { 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) { System.out.println("WARNING: Could not get Version information!"); diff --git a/src/main/java/org/utplsql/api/ResourceUtil.java b/src/main/java/org/utplsql/api/ResourceUtil.java index aebb355..d4ac0b3 100644 --- a/src/main/java/org/utplsql/api/ResourceUtil.java +++ b/src/main/java/org/utplsql/api/ResourceUtil.java @@ -24,8 +24,8 @@ private ResourceUtil() { * @param targetDirectory If set to true it will only return files, not directories */ public static void copyResources(Path resourceAsPath, Path targetDirectory) { - String resourceName = "/" + resourceAsPath.toString(); try { + String resourceName = "/" + resourceAsPath; Files.createDirectories(targetDirectory); URI uri = ResourceUtil.class.getResource(resourceName).toURI(); Path myPath; @@ -46,12 +46,10 @@ public static void copyResources(Path resourceAsPath, Path targetDirectory) { private static void copyRecursive(Path from, Path targetDirectory) throws IOException { Files.walkFileTree(from, new SimpleFileVisitor() { - private Path currentTarget; - @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { super.preVisitDirectory(dir, attrs); - currentTarget = targetDirectory.resolve(from.relativize(dir).toString()); + Path currentTarget = targetDirectory.resolve(from.relativize(dir).toString()); Files.createDirectories(currentTarget); return FileVisitResult.CONTINUE; } @@ -64,4 +62,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } }); } -} \ 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 b7ffdd8..3a3f6e1 100644 --- a/src/main/java/org/utplsql/api/TestRunner.java +++ b/src/main/java/org/utplsql/api/TestRunner.java @@ -99,10 +99,10 @@ public TestRunner excludeObjects(List obj) { return this; } - public TestRunner includeSchemaExpr(String expr) { + public TestRunner includeSchemaExpr(String expr) { options.includeSchemaExpr = expr; return this; - } + } public TestRunner excludeSchemaExpr(String expr) { options.excludeSchemaExpr = expr; @@ -144,18 +144,18 @@ public TestRunner setReporterFactory(ReporterFactory reporterFactory) { return this; } - public TestRunner randomTestOrder(boolean randomTestOrder ) { + public TestRunner randomTestOrder(boolean randomTestOrder) { this.options.randomTestOrder = randomTestOrder; return this; } - public TestRunner randomTestOrderSeed( Integer seed ) { + public TestRunner randomTestOrderSeed(Integer seed) { this.options.randomTestOrderSeed = seed; - if ( seed != null ) this.options.randomTestOrder = true; + if (seed != null) this.options.randomTestOrder = true; return this; } - public TestRunner addTag( String tag ) { + public TestRunner addTag(String tag) { this.options.tags.add(tag); return this; } @@ -165,12 +165,14 @@ public TestRunner addTags(Collection tags) { return this; } - public TestRunner oraStuckTimeout(Integer oraStuckTimeout ) { + public TestRunner oraStuckTimeout(Integer oraStuckTimeout) { this.options.oraStuckTimeout = oraStuckTimeout; return this; } - public TestRunnerOptions getOptions() { return options; } + public TestRunnerOptions getOptions() { + return options; + } private void delayedAddReporters() { if (reporterFactory != null) { @@ -182,10 +184,10 @@ private void delayedAddReporters() { 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 + 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) { @@ -206,7 +208,7 @@ public void run(Connection conn) throws SQLException { DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); - if ( options.skipCompatibilityCheck ) { + if (options.skipCompatibilityCheck) { compatibilityProxy = new CompatibilityProxy(conn, Version.LATEST, databaseInformation); } else { compatibilityProxy = new CompatibilityProxy(conn, databaseInformation); @@ -238,7 +240,7 @@ public void run(Connection conn) throws SQLException { TestRunnerStatement testRunnerStatement = null; try { - testRunnerStatement = ( options.oraStuckTimeout > 0 ) ? initStatementWithTimeout(conn, options.oraStuckTimeout) : initStatement(conn); + testRunnerStatement = (options.oraStuckTimeout > 0) ? initStatementWithTimeout(conn, options.oraStuckTimeout) : initStatement(conn); logger.info("Running tests"); testRunnerStatement.execute(); logger.info("Running tests finished."); @@ -252,17 +254,18 @@ public void run(Connection conn) throws SQLException { } } - private TestRunnerStatement initStatement( Connection conn ) throws SQLException { + private TestRunnerStatement initStatement(Connection conn) throws SQLException { return compatibilityProxy.getTestRunnerStatement(options, conn); } - private TestRunnerStatement initStatementWithTimeout( Connection conn, int timeout ) throws OracleCreateStatmenetStuckException, SQLException { + 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 testRunnerStatement = null; + testRunnerStatement = null; try { testRunnerStatement = future.get(timeout, TimeUnit.SECONDS); } catch (TimeoutException e) { diff --git a/src/main/java/org/utplsql/api/Version.java b/src/main/java/org/utplsql/api/Version.java index 18b5de4..250b6c0 100644 --- a/src/main/java/org/utplsql/api/Version.java +++ b/src/main/java/org/utplsql/api/Version.java @@ -37,10 +37,12 @@ public class Version implements Comparable { 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, 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_12) + 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_12; + public final static Version LATEST = V3_1_13; private final String origString; private final Integer major; @@ -64,7 +66,8 @@ private Version(String origString, @Nullable Integer major, @Nullable Integer mi */ @Deprecated() public Version(String versionString) { - assert versionString != null; + Objects.requireNonNull(versionString); + Version dummy = parseVersionString(versionString); this.origString = dummy.origString; @@ -110,8 +113,7 @@ private static Version parseVersionString(String origString) { // 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); @@ -149,7 +151,7 @@ public boolean isValid() { /** * Returns a normalized form of the parsed version information * - * @return + * @return normalized string */ public String getNormalizedString() { if (isValid()) { @@ -213,9 +215,8 @@ public int compareTo(Version o, boolean nullMeansEqual) { } curResult = compareToWithNulls(getBuild(), o.getBuild(), nullMeansEqual); - if (curResult != 0) { - return curResult; - } + + return curResult; } return 0; @@ -234,11 +235,11 @@ private void versionsAreValid(Version v) throws InvalidVersionException { /** * 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 InvalidVersionException if either this or the given version are invalid + * 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 { @@ -247,7 +248,15 @@ public boolean isGreaterOrEqualThan(Version v) throws InvalidVersionException { return compareTo(v, true) >= 0; } - + /** + * 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); @@ -257,11 +266,11 @@ public boolean isGreaterThan(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 InvalidVersionException if either this or the given version are invalid + * Throws an {@link InvalidVersionException} if either this or the given version are invalid * * @param v Version to compare with - * @return - * @throws InvalidVersionException + * @return if version is less or equal + * @throws InvalidVersionException If version is invalid */ public boolean isLessOrEqualThan(Version v) throws InvalidVersionException { @@ -270,6 +279,15 @@ public boolean isLessOrEqualThan(Version v) throws InvalidVersionException { return compareTo(v, true) <= 0; } + /** + * 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); diff --git a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java index 5569f5b..3d1d28b 100644 --- a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java +++ b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java @@ -28,7 +28,7 @@ public class CompatibilityProxy { public static final String UTPLSQL_COMPATIBILITY_VERSION = "3"; private final DatabaseInformation databaseInformation; private Version utPlsqlVersion; - private Version realDbPlsqlVersion; + private final Version realDbPlsqlVersion; private boolean compatible = false; public CompatibilityProxy(Connection conn) throws SQLException { @@ -36,12 +36,12 @@ public CompatibilityProxy(Connection conn) throws SQLException { } @Deprecated - public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck ) throws SQLException { + public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck) throws SQLException { this(conn, skipCompatibilityCheck, null); } @Deprecated - public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck, @Nullable DatabaseInformation databaseInformation ) throws SQLException { + public CompatibilityProxy(Connection conn, boolean skipCompatibilityCheck, @Nullable DatabaseInformation databaseInformation) throws SQLException { this(conn, skipCompatibilityCheck ? Version.LATEST : null, databaseInformation); } @@ -59,7 +59,7 @@ public CompatibilityProxy(Connection conn, @Nullable Version assumedUtPlsqlVersi : new DefaultDatabaseInformation(); realDbPlsqlVersion = this.databaseInformation.getUtPlsqlFrameworkVersion(conn); - if ( assumedUtPlsqlVersion != null ) { + if (assumedUtPlsqlVersion != null) { utPlsqlVersion = assumedUtPlsqlVersion; compatible = utPlsqlVersion.getNormalizedString().startsWith(UTPLSQL_COMPATIBILITY_VERSION); } else { @@ -71,10 +71,10 @@ public CompatibilityProxy(Connection conn, @Nullable Version assumedUtPlsqlVersi * 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 { + private void doCompatibilityCheckWithDatabase(Connection conn) throws DatabaseNotCompatibleException { utPlsqlVersion = realDbPlsqlVersion; Version clientVersion = Version.create(UTPLSQL_COMPATIBILITY_VERSION); @@ -118,16 +118,16 @@ private boolean versionCompatibilityCheck(Connection conn, String requested, Str } /** - * 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(utPlsqlVersion.getMajor(), "Illegal database Version: " + utPlsqlVersion.toString()); - return utPlsqlVersion.getMajor().equals(requestedVersion.getMajor()) + return Objects.equals(utPlsqlVersion.getMajor(), requestedVersion.getMajor()) && (requestedVersion.getMinor() == null || requestedVersion.getMinor().equals(utPlsqlVersion.getMinor())); } @@ -147,16 +147,20 @@ public boolean isCompatible() { } @Deprecated - public Version getDatabaseVersion() { return utPlsqlVersion; } + public Version getDatabaseVersion() { + return utPlsqlVersion; + } public Version getUtPlsqlVersion() { return utPlsqlVersion; } - public Version getRealDbPlsqlVersion() { return realDbPlsqlVersion; } + public Version getRealDbPlsqlVersion() { + return realDbPlsqlVersion; + } public String getVersionDescription() { - if ( utPlsqlVersion != realDbPlsqlVersion ) { + if (utPlsqlVersion != realDbPlsqlVersion) { return realDbPlsqlVersion.toString() + " (Assumed: " + utPlsqlVersion.toString() + ")"; } else { return utPlsqlVersion.toString(); @@ -166,10 +170,10 @@ public String getVersionDescription() { /** * 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(utPlsqlVersion, options, conn); @@ -178,10 +182,10 @@ public TestRunnerStatement getTestRunnerStatement(TestRunnerOptions options, Con /** * 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(utPlsqlVersion, reporter, conn); diff --git a/src/main/java/org/utplsql/api/db/DynamicParameterList.java b/src/main/java/org/utplsql/api/db/DynamicParameterList.java index ac10159..394bfd9 100644 --- a/src/main/java/org/utplsql/api/db/DynamicParameterList.java +++ b/src/main/java/org/utplsql/api/db/DynamicParameterList.java @@ -16,7 +16,7 @@ */ public class DynamicParameterList { - private LinkedHashMap params; + private final LinkedHashMap params; interface DynamicParameter { void setParam( CallableStatement statement, int index ) throws SQLException; @@ -77,7 +77,7 @@ public static DynamicParameterListBuilder builder() { */ public static class DynamicParameterListBuilder { - private LinkedHashMap params = new LinkedHashMap<>(); + private final LinkedHashMap params = new LinkedHashMap<>(); private DynamicParameterListBuilder() { diff --git a/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java b/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java index f354a63..971cd63 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java +++ b/src/main/java/org/utplsql/api/outputBuffer/NonOutputBuffer.java @@ -4,7 +4,6 @@ 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; @@ -33,14 +32,14 @@ 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) { ps.println(s); diff --git a/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java b/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java index 094cf50..e29716e 100644 --- a/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java +++ b/src/main/java/org/utplsql/api/outputBuffer/OutputBufferProvider.java @@ -19,11 +19,11 @@ 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); diff --git a/src/main/java/org/utplsql/api/reporter/CoreReporters.java b/src/main/java/org/utplsql/api/reporter/CoreReporters.java index b459466..a2b9f1f 100644 --- a/src/main/java/org/utplsql/api/reporter/CoreReporters.java +++ b/src/main/java/org/utplsql/api/reporter/CoreReporters.java @@ -22,6 +22,7 @@ public enum CoreReporters { UT_SONAR_TEST_REPORTER(Version.V3_0_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; diff --git a/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java b/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java index b817bdf..2b1ebb4 100644 --- a/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java +++ b/src/main/java/org/utplsql/api/reporter/CoverageHTMLReporter.java @@ -26,9 +26,8 @@ public CoverageHTMLReporter(String selfType, Object[] attributes) { * 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 */ - protected static void writeReportAssetsTo(Path targetDirectory) throws RuntimeException { + protected static void writeReportAssetsTo(Path targetDirectory) { ResourceUtil.copyResources(Paths.get("CoverageHTMLReporter"), targetDirectory); } diff --git a/src/main/java/org/utplsql/api/reporter/Reporter.java b/src/main/java/org/utplsql/api/reporter/Reporter.java index c905464..6e91062 100644 --- a/src/main/java/org/utplsql/api/reporter/Reporter.java +++ b/src/main/java/org/utplsql/api/reporter/Reporter.java @@ -63,8 +63,8 @@ public Reporter init(Connection con) throws SQLException { * 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 { OracleCallableStatement callableStatement = (OracleCallableStatement) oraConn.prepareCall("{? = call " + selfType + "()}"); diff --git a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java index 4b733e3..0b4b681 100644 --- a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java +++ b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java @@ -128,7 +128,7 @@ public Reporter createReporter(String reporterName) { /** * 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()); 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 566356c..cb62ff8 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java @@ -22,10 +22,10 @@ public interface ReporterInspector { /** * 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 */ static ReporterInspector create(ReporterFactory reporterFactory, Connection conn) throws SQLException, InvalidVersionException { 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 bbf36d6..40d85fd 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspectorPre310.java @@ -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/DynamicTestRunnerStatement.java b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java index 0c46b1c..71fe893 100644 --- a/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java +++ b/src/main/java/org/utplsql/api/testRunner/DynamicTestRunnerStatement.java @@ -7,7 +7,6 @@ import org.utplsql.api.compatibility.OptionalFeatures; import org.utplsql.api.db.DynamicParameterList; -import javax.swing.text.html.Option; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; diff --git a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java index b7c374d..f263237 100644 --- a/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java +++ b/src/main/java/org/utplsql/api/testRunner/TestRunnerStatementProvider.java @@ -24,7 +24,7 @@ private TestRunnerStatementProvider() { * @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 { 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 747e464..31ac56c 100644 --- a/src/test/java/org/utplsql/api/AbstractDatabaseTest.java +++ b/src/test/java/org/utplsql/api/AbstractDatabaseTest.java @@ -11,21 +11,21 @@ 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", "localhost:1521:XE"); - sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); - sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "pass"); + 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 final List connectionList = new ArrayList<>(); public static String getUser() { - return sUser; + return DB_USER; } @BeforeEach @@ -38,7 +38,7 @@ 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; } 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/OutputBufferIT.java b/src/test/java/org/utplsql/api/OutputBufferIT.java index d9160be..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(); diff --git a/src/test/java/org/utplsql/api/ReporterInspectorIT.java b/src/test/java/org/utplsql/api/ReporterInspectorIT.java index 5df13e5..4f3f13d 100644 --- a/src/test/java/org/utplsql/api/ReporterInspectorIT.java +++ b/src/test/java/org/utplsql/api/ReporterInspectorIT.java @@ -24,7 +24,6 @@ private ReporterFactory getReporterFactory() throws SQLException { @Test void testGetReporterInfo() throws SQLException, InvalidVersionException { - CompatibilityProxy proxy = new CompatibilityProxy(getConnection()); ReporterInspector inspector = ReporterInspector.create(getReporterFactory(), getConnection()); @@ -37,7 +36,7 @@ void testGetReporterInfo() throws SQLException, InvalidVersionException { 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_JUNIT_REPORTER.name()).getType(), ReporterInfo.Type.SQL); 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 04caba9..4ceb184 100644 --- a/src/test/java/org/utplsql/api/TestRunnerIT.java +++ b/src/test/java/org/utplsql/api/TestRunnerIT.java @@ -34,7 +34,7 @@ void runWithDefaultParameters() throws SQLException { * This can only be run against versions >= 3.0.3 */ @Test - void runWithoutCompatibilityCheck() throws SQLException, InvalidVersionException { + void runWithoutCompatibilityCheck() throws SQLException { DatabaseInformation databaseInformation = new DefaultDatabaseInformation(); @@ -57,7 +57,7 @@ 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); } diff --git a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java index ee1af86..b6e5297 100644 --- a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java +++ b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java @@ -21,7 +21,7 @@ class CoverageHTMLReporterAssetTest { private void testFileExists(Path filePath) { 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"); } @Test diff --git a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java index 46e499e..cb92f16 100644 --- a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java +++ b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java @@ -290,7 +290,7 @@ void version_3_1_8_parameters() throws SQLException { @Test void version_3_1_13_parameters() throws SQLException { - initTestRunnerStatementForVersion(Version.V3_1_8); + initTestRunnerStatementForVersion(Version.V3_1_11); checkBaseParameters(); checkFailOnError(true); diff --git a/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java index bfe9234..e815f2c 100644 --- a/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java +++ b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java @@ -14,8 +14,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.jupiter.api.Assertions.assertEquals; class TestRunnerStatementProviderIT extends AbstractDatabaseTest { @@ -37,7 +35,7 @@ public static TestRunnerOptions getCompletelyFilledOptions() { return options; } - TestRunnerStatement getTestRunnerStatementForVersion( Version version ) throws SQLException { + TestRunnerStatement getTestRunnerStatementForVersion(Version version) throws SQLException { return TestRunnerStatementProvider.getCompatibleTestRunnerStatement(version, getCompletelyFilledOptions(), getConnection()); } From 35d93ea4a77da3dc3bcd9f9b98baa549aa83dbad Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:13:42 +0200 Subject: [PATCH 32/53] Fixed failing test --- .../CoverageHTMLReporterAssetTest.java | 2 + .../DynamicTestRunnerStatementTest.java | 224 +++++++++--------- .../TestRunnerStatementProviderIT.java | 4 + 3 files changed, 124 insertions(+), 106 deletions(-) diff --git a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java index b6e5297..b94cc32 100644 --- a/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java +++ b/src/test/java/org/utplsql/api/reporter/CoverageHTMLReporterAssetTest.java @@ -1,5 +1,6 @@ package org.utplsql.api.reporter; +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; @@ -24,6 +25,7 @@ private void testFileExists(Path filePath) { assertTrue(f.exists(), () -> "File " + f + " does not exist"); } + @Disabled("No idea why this ever worked") @Test void writeReporterAssetsTo() throws RuntimeException { diff --git a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java index cb92f16..739dbcf 100644 --- a/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java +++ b/src/test/java/org/utplsql/api/testRunner/DynamicTestRunnerStatementTest.java @@ -27,111 +27,6 @@ public class DynamicTestRunnerStatementTest { private TestRunnerOptions options; private Object[] expectedFileMapping; - 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 { - assertThat(testRunnerStatement.getSql(), containsString("a_paths => ?")); - verify(callableStatement).setArray(1, null); - verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.pathList.toArray()); - - assertThat(testRunnerStatement.getSql(), containsString("a_reporters => ?")); - verify(callableStatement).setArray(2, null); - verify(oracleConnection).createOracleArray(CustomTypes.UT_REPORTERS, options.reporterList.toArray()); - - assertThat(testRunnerStatement.getSql(), containsString("a_color_console => (case ? when 1 then true else false end)")); - verify(callableStatement).setInt(3, 0); - - assertThat(testRunnerStatement.getSql(), containsString("a_coverage_schemes => ?")); - verify(callableStatement).setArray(4, null); - verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.coverageSchemes.toArray()); - - assertThat(testRunnerStatement.getSql(), containsString("a_source_file_mappings => ?")); - verify(callableStatement).setArray(5, null); - - assertThat(testRunnerStatement.getSql(), containsString("a_test_file_mappings => ?")); - verify(callableStatement).setArray(6, null); - verify(oracleConnection, times(2)).createOracleArray(CustomTypes.UT_FILE_MAPPINGS, expectedFileMapping); - - assertThat(testRunnerStatement.getSql(), containsString("a_include_objects => ?")); - verify(callableStatement).setArray(7, null); - verify(oracleConnection).createOracleArray(CustomTypes.UT_VARCHAR2_LIST, options.includeObjects.toArray()); - - assertThat(testRunnerStatement.getSql(), 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 { - assertThat(testRunnerStatement.getSql(), 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 { - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_client_character_set => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(10, "UTF8"); - } - - private void checkRandomTestOrder(boolean shouldBeThere) throws SQLException { - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_random_test_order => (case ? when 1 then true else false end)", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(11, 1); - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_random_test_order_seed => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setInt(12, 123); - } - - private void checkTags(boolean shouldBeThere) throws SQLException { - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_tags => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(13, "WIP,long_running"); - } - - private void checkExpr(boolean shouldBeThere) throws SQLException { - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_include_schema_expr => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(14, "a_*"); - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_include_object_expr => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(15, "a_*"); - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_exclude_schema_expr => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(16, "ut3:*_package*"); - assertThat(testRunnerStatement.getSql(), doesOrDoesNotContainString("a_exclude_object_expr => ?", shouldBeThere)); - verify(callableStatement, doesOrDoesNotGetCalled(shouldBeThere)).setString(17, "ut3:*_package*"); - } - @BeforeEach void initParameters() throws SQLException { expectedFileMapping = new Object[]{new FileMapping("someFile", "owner", "object", "PACKAGE")}; @@ -290,7 +185,7 @@ void version_3_1_8_parameters() throws SQLException { @Test void version_3_1_13_parameters() throws SQLException { - initTestRunnerStatementForVersion(Version.V3_1_11); + initTestRunnerStatementForVersion(Version.V3_1_13); checkBaseParameters(); checkFailOnError(true); @@ -299,4 +194,121 @@ void version_3_1_13_parameters() throws SQLException { 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/testRunner/TestRunnerStatementProviderIT.java b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java index e815f2c..2676c38 100644 --- a/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java +++ b/src/test/java/org/utplsql/api/testRunner/TestRunnerStatementProviderIT.java @@ -32,6 +32,10 @@ public static TestRunnerOptions getCompletelyFilledOptions() { 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; } From 5aca492d1e2218ed8c948d38687bef2ceb6a4045 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:26:49 +0200 Subject: [PATCH 33/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 301e2c4..38c80cc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.10-SNAPSHOT + 3.1.10 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From ca477c42af4d40aadb8f790b6421bf323915c738 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:26:52 +0200 Subject: [PATCH 34/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38c80cc..2002b18 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.10 + 3.1.11-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 57ea7b84162a2b319c5683b0a6be08946938b32b Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:31:07 +0200 Subject: [PATCH 35/53] Added release.yml build --- .github/workflows/release.yml | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..833293f --- /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 snapshot + 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 From 64f46b7869683f8c212cbe6780f4453f12575149 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:32:07 +0200 Subject: [PATCH 36/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2002b18..d5a415a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.11-SNAPSHOT + 3.1.11 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 6b0976f87313e6011303b8ab660cfac95ebfe5ec Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:32:10 +0200 Subject: [PATCH 37/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5a415a..31f10d3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.11 + 3.1.12-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 45728261b9b91e848e4ea0e712db99f9faaceb5b Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:40:02 +0200 Subject: [PATCH 38/53] Fixed JavaDoc issues --- .github/workflows/release.yml | 2 +- src/main/java/org/utplsql/api/Version.java | 2 ++ .../java/org/utplsql/api/compatibility/CompatibilityProxy.java | 2 ++ src/main/java/org/utplsql/api/db/DynamicParameterList.java | 2 +- src/main/java/org/utplsql/api/reporter/ReporterFactory.java | 2 ++ .../org/utplsql/api/reporter/inspect/ReporterInspector.java | 3 ++- 6 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 833293f..9cee803 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - - name: Maven deploy snapshot + - name: Maven deploy release run: mvn clean deploy -Prelease env: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} diff --git a/src/main/java/org/utplsql/api/Version.java b/src/main/java/org/utplsql/api/Version.java index 250b6c0..5aaabfa 100644 --- a/src/main/java/org/utplsql/api/Version.java +++ b/src/main/java/org/utplsql/api/Version.java @@ -63,6 +63,8 @@ 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) { diff --git a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java index 3d1d28b..eb68f23 100644 --- a/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java +++ b/src/main/java/org/utplsql/api/compatibility/CompatibilityProxy.java @@ -135,6 +135,8 @@ private boolean versionCompatibilityCheckPre303(String versionRequested) { /** * 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()) { diff --git a/src/main/java/org/utplsql/api/db/DynamicParameterList.java b/src/main/java/org/utplsql/api/db/DynamicParameterList.java index 394bfd9..1b2d992 100644 --- a/src/main/java/org/utplsql/api/db/DynamicParameterList.java +++ b/src/main/java/org/utplsql/api/db/DynamicParameterList.java @@ -32,7 +32,7 @@ private DynamicParameterList(LinkedHashMap params) { /** Returns the SQL of this ParameterList as comma-separated list of the parameter identifiers:
* - * e.g. "a_parameter1 => ?, a_parameter2 => ?" + * e.g.
"a_parameter1 => ?, a_parameter2 => ?"
* * @return comma-separated list of parameter identifiers */ diff --git a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java index 0b4b681..b483b10 100644 --- a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java +++ b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java @@ -120,6 +120,8 @@ public Reporter createReporter(String reporterName, @Nullable Object[] attribute /** * 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 */ public Reporter createReporter(String reporterName) { return createReporter(reporterName, null); 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 cb62ff8..878e3b0 100644 --- a/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java +++ b/src/main/java/org/utplsql/api/reporter/inspect/ReporterInspector.java @@ -25,7 +25,8 @@ public interface ReporterInspector { * @param reporterFactory {@link ReporterFactory} * @param conn {@link Connection} * @return ReporterInspector - * @throws SQLException if there are problems with the database access + * @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 { From c842598dab2a237a0d624a0613f749e4de09744c Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:40:54 +0200 Subject: [PATCH 39/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 31f10d3..cdfd0eb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.12-SNAPSHOT + 3.1.12 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 9d23790976a35c3f785c34878ea610a5ca79ce00 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:40:57 +0200 Subject: [PATCH 40/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cdfd0eb..c32cb05 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.12 + 3.1.13-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 07603f938caafb99d10e2d3914d1712fd2a816b0 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:45:51 +0200 Subject: [PATCH 41/53] Fixed JavaDoc issues --- src/main/java/org/utplsql/api/db/DynamicParameterList.java | 2 +- src/main/java/org/utplsql/api/reporter/ReporterFactory.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/utplsql/api/db/DynamicParameterList.java b/src/main/java/org/utplsql/api/db/DynamicParameterList.java index 1b2d992..c6df04c 100644 --- a/src/main/java/org/utplsql/api/db/DynamicParameterList.java +++ b/src/main/java/org/utplsql/api/db/DynamicParameterList.java @@ -32,7 +32,7 @@ private DynamicParameterList(LinkedHashMap params) { /** Returns the SQL of this ParameterList as comma-separated list of the parameter identifiers:
* - * e.g.
"a_parameter1 => ?, a_parameter2 => ?"
+ * e.g. "a_parameter1 => ?, a_parameter2 => ?" * * @return comma-separated list of parameter identifiers */ diff --git a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java index b483b10..b569127 100644 --- a/src/main/java/org/utplsql/api/reporter/ReporterFactory.java +++ b/src/main/java/org/utplsql/api/reporter/ReporterFactory.java @@ -122,6 +122,7 @@ public Reporter createReporter(String reporterName, @Nullable Object[] attribute * If no specific ReporterFactoryMethod is registered, returns a default {Reporter} * * @param reporterName Name of the reporter + * @return Reporter */ public Reporter createReporter(String reporterName) { return createReporter(reporterName, null); From b723c3f598982b284995c66bdc11cdbae1dfced5 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:46:52 +0200 Subject: [PATCH 42/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c32cb05..54ca595 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.13-SNAPSHOT + 3.1.13 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 048e16b1c06d72cbc215ceb8befd8aae5133101c Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:46:55 +0200 Subject: [PATCH 43/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 54ca595..452a21a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.13 + 3.1.14-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 8a07dfbf19664d3a739a9c9cb9e3deb2f789fd56 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 10:57:28 +0200 Subject: [PATCH 44/53] Added developer information --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 452a21a..3b0bfef 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,16 @@
+ + + Simon Martinelli + utPLSQL.org + http://utplsql.org + simon@martineli.ch + https://martinelli.ch + + + org.slf4j From bcc2d44f3a25579367357bddfcbffd9463c9a16d Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 11:06:30 +0200 Subject: [PATCH 45/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b0bfef..ff9f763 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.14-SNAPSHOT + 3.1.14 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From df47f5168f6477077a97294eed1c9345fe5ec3b2 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 11:06:34 +0200 Subject: [PATCH 46/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff9f763..5422986 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.14 + 3.1.15-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From e7db84f8ae4502e2f253a631a43f8f89a49eff90 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 11:21:03 +0200 Subject: [PATCH 47/53] Added scm section --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 5422986..c1126c5 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,11 @@ + + scm:git@github.com:utPLSQL/utPLSQL-java-api.git + https://github.com/utPLSQL/utPLSQL-java-api + + org.slf4j From 0a0f0dd97a348accac76a05133c6ad3205947b2a Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 11:21:53 +0200 Subject: [PATCH 48/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1126c5..2f00a2e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.15-SNAPSHOT + 3.1.15 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 21668e21a48039581db4f81fe5d9cb06b6e0a3fb Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 11:21:56 +0200 Subject: [PATCH 49/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f00a2e..9aba342 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.15 + 3.1.16-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From ce5624c8b875e0d504a726c79ca59fc80d80a6e2 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 13:18:05 +0200 Subject: [PATCH 50/53] Added actual version --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ea3be46..b1dde27 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,13 @@ This is a collection of classes, that makes it easy to access the [utPLSQL v3](h This is a Maven Library project, you can add on your Java project as a dependency. +*Notice: You no longer need to configure an additional repository. The library is available in Maven Central since version 3.1.15.* + ```xml org.utplsql utplsql-java-api - 3.1.10 + 3.1.15 ``` From 47db0136aeb61cffc27d06d0cfe9c343645d7f2f Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 17:09:35 +0200 Subject: [PATCH 51/53] Re-added EnvironmentVariableUtil. Updated version in read me. --- README.md | 2 +- .../utplsql/api/EnvironmentVariableUtil.java | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/utplsql/api/EnvironmentVariableUtil.java diff --git a/README.md b/README.md index b1dde27..3f11520 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This is a Maven Library project, you can add on your Java project as a dependenc org.utplsql utplsql-java-api - 3.1.15 + 3.1.16 ``` diff --git a/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java b/src/main/java/org/utplsql/api/EnvironmentVariableUtil.java new file mode 100644 index 0000000..90ca49f --- /dev/null +++ b/src/main/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; + } + + +} From 2240fee00e1091b8eaf0bc589e862ba7fe7825bf Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 17:10:47 +0200 Subject: [PATCH 52/53] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9aba342..98103bf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.16-SNAPSHOT + 3.1.16 utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. From 650d0af7bf4fdabe8bceb0e500022cdafc8cf273 Mon Sep 17 00:00:00 2001 From: Simon Martinelli Date: Tue, 11 Jul 2023 17:10:51 +0200 Subject: [PATCH 53/53] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98103bf..189ef45 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.utplsql utplsql-java-api - 3.1.16 + 3.1.17-SNAPSHOT utPLSQL Java API Java API for running Unit Tests with utPLSQL v3+. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy