diff --git a/.travis.yml b/.travis.yml index b48adb107..018038144 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ env: - CURRENT_BRANCH=${TRAVIS_BRANCH} - UTPLSQL_REPO="utPLSQL/utPLSQL" - UTPLSQL_BUILD_NO="${TRAVIS_BUILD_NUMBER:-0}" - - UTPLSQL_VERSION_PATTERN="v?([0-9]+\.){3}[0-9]+[^']*" - UTPLSQL_VERSION=$(. .travis/get_project_version.sh) - UTPLSQL_BUILD_VERSION=$(. .travis/get_project_build_version.sh) - UTPLSQL_SOURCES_DIR='source' @@ -51,10 +50,10 @@ env: - MAVEN_CFG=$HOME/.m2 matrix: - ORACLE_VERSION="${DOCKER_TAG_11G:-11g-r2-xe}" CONNECTION_STR='127.0.0.1:1521/XE' DOCKER_OPTIONS='--shm-size=1g' - - ORACLE_VERSION="${DOCKER_TAG_12C:-12c-r1-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' DOCKER_OPTIONS="-v /dev/pdbs:/opt/oracle/oradata/pdbs" - - ORACLE_VERSION="${DOCKER_TAG_12C2:-12c-r2-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' DOCKER_OPTIONS="-v /dev/pdbs:/opt/oracle/oradata/pdbs" - - ORACLE_VERSION="${DOCKER_TAG_18:-18c-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' DOCKER_OPTIONS="-v /dev/pdbs:/opt/oracle/oradata/pdbs" - - ORACLE_VERSION="${DOCKER_TAG_19:-19c-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' DOCKER_OPTIONS="-v /dev/pdbs:/opt/oracle/oradata/pdbs" + - ORACLE_VERSION="${DOCKER_TAG_12C:-12c-r1-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' + - ORACLE_VERSION="${DOCKER_TAG_12C2:-12c-r2-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' + - ORACLE_VERSION="${DOCKER_TAG_18:-18c-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' + - ORACLE_VERSION="${DOCKER_TAG_19:-19c-se2-small}" CONNECTION_STR='127.0.0.1:1521/ORCLCDB' cache: pip: true @@ -81,11 +80,7 @@ before_install: install: #- unzip utPLSQL.zip - unzip utPLSQL-cli.zip && chmod -R u+x utPLSQL-cli - # Get ojdbc via maven - - bash .travis/maven_cfg.sh - bash .travis/install_sqlcl.sh - - sudo mkdir -p /dev/pdbs - - sudo chmod -R 777 /dev/pdbs - if [[ ! $TRAVIS_TAG ]]; then bash .travis/start_db.sh; fi before_script: @@ -115,16 +110,15 @@ jobs: - pip install mkdocs before_script: skip script: - - if [[ ($TRAVIS_BRANCH == develop) && ($TRAVIS_PULL_REQUEST == false) ]]; then bash .travis/trigger_travis.sh $TRAVIS_ACCESS_TOKEN; fi - - #The update_project_version.sh needs to be done before pushing changes to develop branch - bash .travis/update_project_version.sh - git config --global user.email "builds@travis-ci.com" - git config --global user.name "${UTPLSQL_BUILD_USER_NAME}" - git remote rm origin - git remote add origin https://${github_api_token}@github.com/${UTPLSQL_REPO} - if [[ ! $TRAVIS_TAG ]]; then bash .travis/push_release_version.sh; fi + - bash .travis/push_docs_to_github_io.sh - bash .travis/build_docs.sh - - bash .travis/push_docs_to_gh_pages.sh + - if [[ ($TRAVIS_BRANCH == develop) && ($TRAVIS_PULL_REQUEST == false) ]]; then bash .travis/trigger_travis.sh $TRAVIS_ACCESS_TOKEN; fi before_deploy: - bash .travis/build_release_archive.sh deploy: diff --git a/.travis/install.sh b/.travis/install.sh index 4806105b1..2e96ebfb2 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -74,10 +74,6 @@ PROMPT Creating $UT3_TESTER - Power-user for testing internal framework code create user $UT3_TESTER identified by "$UT3_TESTER_PASSWORD" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; grant create session, create procedure, create type, create table to $UT3_TESTER; -PROMPT Additional grants for disabling DDL trigger and testing parser without trigger enabled/present - -grant alter any trigger to $UT3_TESTER; -grant administer database trigger to $UT3_TESTER; grant execute on dbms_lock to $UT3_TESTER; PROMPT Granting $UT3_OWNER code to $UT3_TESTER @@ -98,7 +94,7 @@ end; PROMPT Granting $UT3_OWNER tables to $UT3_TESTER begin - for i in ( select table_name from all_tables t where owner = 'UT3' and nested = 'NO' and IOT_NAME is NULL) + for i in ( select table_name from all_tables t where owner = 'UT3' and nested = 'NO' and iot_name is null) loop execute immediate 'grant select on UT3.'||i.table_name||' to UT3_TESTER'; end loop; @@ -124,8 +120,33 @@ grant create public database link to $UT3_TESTER_HELPER; grant drop public database link to $UT3_TESTER_HELPER; PROMPT Grants for testing coverage outside of main UT3 schema. -grant create any procedure, drop any procedure, execute any procedure, create any type, drop any type, execute any type, under any type, select any table, update any table, insert any table, delete any table, create any table, drop any table, alter any table, select any dictionary, create any synonym, drop any synonym to $UT3_TESTER_HELPER; +grant create any procedure, drop any procedure, execute any procedure, create any type, drop any type, execute any type, under any type, + select any table, update any table, insert any table, delete any table, create any table, drop any table, alter any table, + select any dictionary, create any synonym, drop any synonym, + grant any object privilege, grant any privilege + to $UT3_TESTER_HELPER; + grant create job to $UT3_TESTER_HELPER; +PROMPT Additional grants for disabling DDL trigger and testing parser without trigger enabled/present + +grant alter any trigger to $UT3_TESTER_HELPER; +grant administer database trigger to $UT3_TESTER_HELPER; +grant execute on dbms_lock to $UT3_TESTER_HELPER; + +create user ut3_cache_test_owner identified by ut3; +grant create session, create procedure to ut3_cache_test_owner; + +create user ut3_no_extra_priv_user identified by ut3; +grant create session, create procedure to ut3_no_extra_priv_user; + +create user ut3_select_catalog_user identified by ut3; +grant create session, create procedure, select_catalog_role to ut3_select_catalog_user; + +create user ut3_select_any_table_user identified by ut3; +grant create session, create procedure, select any table to ut3_select_any_table_user; + +create user ut3_execute_any_proc_user identified by ut3; +grant create session, create procedure, execute any procedure to ut3_execute_any_proc_user; exit SQL diff --git a/.travis/maven_cfg.sh b/.travis/maven_cfg.sh deleted file mode 100755 index ef2348099..000000000 --- a/.travis/maven_cfg.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -ev -cp .travis/settings.xml $MAVEN_CFG/settings.xml - -cd $(dirname $(readlink -f $0)) - -# Download wagon-http recommended by Oracle. -# On maven latest version this is not needed, but travis doesn't have it. -if [ ! -f $CACHE_DIR/wagon-http-2.8-shaded.jar ]; then - curl -L -O "http://central.maven.org/maven2/org/apache/maven/wagon/wagon-http/2.8/wagon-http-2.8-shaded.jar" - mv wagon-http-2.8-shaded.jar $CACHE_DIR/ - sudo cp $CACHE_DIR/wagon-http-2.8-shaded.jar $MAVEN_HOME/lib/ext/ -else - echo "Using cached wagon-http..." - sudo cp $CACHE_DIR/wagon-http-2.8-shaded.jar $MAVEN_HOME/lib/ext/ -fi - -mvn dependency:copy-dependencies -DoutputDirectory=../utPLSQL-cli/lib \ No newline at end of file diff --git a/.travis/pom.xml b/.travis/pom.xml deleted file mode 100644 index 706bd79bb..000000000 --- a/.travis/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - 4.0.0 - org.utplsql - utplsql - 3.0.4-SNAPSHOT - jar - - utPLSQL - https://github.com/utPLSQL/utPLSQL - - - UTF-8 - - - - - com.oracle.jdbc - ojdbc8 - 12.2.0.1 - compile - - - com.oracle.jdbc - orai18n - 12.2.0.1 - compile - - - - - - maven.oracle.com - - true - - - false - - https://maven.oracle.com - default - - - - - - maven.oracle.com - https://maven.oracle.com - - - - - - - io.packagecloud.maven.wagon - maven-packagecloud-wagon - 0.0.6 - - - - diff --git a/.travis/push_docs_to_gh_pages.sh b/.travis/push_docs_to_github_io.sh old mode 100755 new mode 100644 similarity index 67% rename from .travis/push_docs_to_gh_pages.sh rename to .travis/push_docs_to_github_io.sh index d2c5a685c..e29f1c88c --- a/.travis/push_docs_to_gh_pages.sh +++ b/.travis/push_docs_to_github_io.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Many aspects of this came from https://gist.github.com/domenic/ec8b0fc8ab45f39403dd +# Based on `push_docs_to_gh_pages.sh` # Significant alterations # - Support for multiple copies of documentation, # - only clearing out develop @@ -13,59 +13,55 @@ # - File: "docs/index.md" with that contains develop docs # Required ENV Variables -PAGES_TARGET_BRANCH="gh-pages" LATEST_DOCS_BRANCH="develop" +GITHUB_IO_REPO='utPLSQL/utPLSQL.github.io' +GITHUB_IO_BRANCH='master' + # TRAVIS_* variables are set by travis directly and only need to be if testing externally -# We don't want a pull request automatically updating the repository +# We deploy only when building on develop branch or on TAG (release) if [ "$TRAVIS_PULL_REQUEST" == "false" ] && { [ "${CURRENT_BRANCH}" == "${LATEST_DOCS_BRANCH}" ] || [ -n "${TRAVIS_TAG}" ]; }; then # ENV Variable checks are to help with configuration troubleshooting, they silently exit with unique message. # Anyone one of them not set can be used to turn off this functionality. # If a version of the project is not defined - [[ -n "${UTPLSQL_VERSION}" ]] || { echo "variable UTPLSQL_VERSION is not defines or missing value"; exit 0; } + [[ -n "${UTPLSQL_VERSION}" ]] || { echo "variable UTPLSQL_VERSION is not defines or missing value"; exit 1; } # Fail if the markdown documentation is not present. [[ -f ./docs/index.md ]] || { echo "file docs/index.md not found"; exit 1; } - # Save some useful information + # Store latest commit SHA to be used when committing and pushing to github.io repo SHA=`git rev-parse --verify HEAD` - # clone the repository and switch to PAGES_TARGET_BRANCH branch + # clone the repository and switch to GITHUB_IO_BRANCH branch mkdir pages - cd pages - git clone https://${github_api_token}@github.com/${UTPLSQL_REPO} . - - PAGES_BRANCH_EXISTS=$(git ls-remote --heads origin ${PAGES_TARGET_BRANCH}) + cd ./pages + git clone --depth 1 https://${github_api_token}@github.com/${GITHUB_IO_REPO} -b ${GITHUB_IO_BRANCH} . - if [ -n "$PAGES_BRANCH_EXISTS" ] ; then - echo "Pages Branch Found" - git checkout ${PAGES_TARGET_BRANCH} - else - echo "Creating Pages Branch" - git checkout --orphan ${PAGES_TARGET_BRANCH} - git rm -rf . - fi + mkdir -p utPLSQL + cd ./utPLSQL #clear out develop documentation directory and copy docs contents to it. - echo "updating 'develop' directory" - mkdir -p develop - rm -rf develop/**./* || exit 0 - cp -a ../docs/. ./develop + echo "updating 'develop' documentation directory" + mkdir -p ./develop + rm -rf ./develop/**./* || exit 0 + cp -a ../../docs/. ./develop + # If a Tagged Build then copy to it's own directory as well and to the 'latest' release directory if [ -n "$TRAVIS_TAG" ]; then - echo "Creating ${UTPLSQL_VERSION}" - mkdir -p ${UTPLSQL_VERSION} - rm -rf ${UTPLSQL_VERSION}/**./* || exit 0 - cp -a ../docs/. ${UTPLSQL_VERSION} - echo "Populating 'latest' directory" - mkdir -p latest - rm -rf latest/**./* || exit 0 - cp -a ../docs/. latest + echo "Creating directory ./${UTPLSQL_VERSION}" + mkdir -p ./${UTPLSQL_VERSION} + rm -rf ./${UTPLSQL_VERSION}/**./* || exit 0 + cp -a ../../docs/. ./${UTPLSQL_VERSION} + echo "Populating 'latest' directory" + mkdir -p ./latest + rm -rf ./latest/**./* || exit 0 + cp -a ../../docs/. ./latest fi # Stage changes for commit git add . + #Check if there are doc changes, if none exit the script - if [[ -z `git diff HEAD --exit-code` ]] && [ -n "${PAGES_BRANCH_EXISTS}" ] ; then + if [[ -z `git diff HEAD --exit-code` ]]; then echo "No changes to docs detected." exit 0 fi @@ -75,7 +71,7 @@ if [ "$TRAVIS_PULL_REQUEST" == "false" ] && { [ "${CURRENT_BRANCH}" == "${LATEST echo "---" >>index.md echo "layout: default" >>index.md echo "---" >>index.md - echo "" >>index.md + echo "" >>index.md echo "# Documentation versions" >>index.md echo "" >>index.md echo "" >>index.md #- 7th line - placeholder for latest release doc @@ -96,6 +92,6 @@ if [ "$TRAVIS_PULL_REQUEST" == "false" ] && { [ "${CURRENT_BRANCH}" == "${LATEST git add . git commit -m "Deploy to gh-pages branch: base commit ${SHA}" # Now that we're all set up, we can push. - git push --quiet origin HEAD:${PAGES_TARGET_BRANCH} + git push --quiet origin HEAD:${GITHUB_IO_BRANCH} fi diff --git a/.travis/settings.xml b/.travis/settings.xml deleted file mode 100644 index 731ef5298..000000000 --- a/.travis/settings.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - maven.oracle.com - ${env.ORACLE_OTN_USER} - ${env.ORACLE_OTN_PASSWORD} - - - ANY - ANY - OAM 11g - - - - - - http.protocol.allow-circular-redirects - %b,true - - - - - - - - - diff --git a/.travis/update_project_version.sh b/.travis/update_project_version.sh index 3f162fb78..c4540eef8 100755 --- a/.travis/update_project_version.sh +++ b/.travis/update_project_version.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +UTPLSQL_VERSION_PATTERN="v?([0-9]+\.){3}[0-9]+[^']*" + echo Current branch is "${CURRENT_BRANCH}" echo Update version in project source files diff --git a/VERSION b/VERSION index 76cb3f689..04c455c73 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v3.1.9 +v3.1.10 diff --git a/development/cleanup.sh b/development/cleanup.sh index 313e382ca..4df1e43f5 100755 --- a/development/cleanup.sh +++ b/development/cleanup.sh @@ -23,6 +23,11 @@ drop user ${UT3_RELEASE_VERSION_SCHEMA} cascade; drop user ${UT3_TESTER} cascade; drop user ${UT3_TESTER_HELPER} cascade; drop user ${UT3_USER} cascade; +drop user ut3_cache_test_owner cascade; +drop user ut3_no_extra_priv_user cascade; +drop user ut3_select_catalog_user cascade; +drop user ut3_select_any_table_user cascade; +drop user ut3_execute_any_proc_user cascade; begin for i in ( diff --git a/development/releasing.md b/development/releasing.md index 3e9ac64fa..93634018d 100644 --- a/development/releasing.md +++ b/development/releasing.md @@ -1,9 +1,10 @@ -The release process is semi-automated. +## Release process With every build, the build process on Travis updates files with an appropriate version number before deployment into the database. This step is performed, to confirm that the update of versions works properly. -To create a release: +## To create a release + - create release branch from development branch and make sure to name the release branch: `release/vX.Y.Z` - update, commit and push at least one file change in the release branch, to kickoff a Travis build - wait for th build to complete successfully diff --git a/docs/about/authors.md b/docs/about/authors.md index 776e5cd44..3ed66a43f 100644 --- a/docs/about/authors.md +++ b/docs/about/authors.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) ### utPLSQL v3 Major Contributors diff --git a/docs/about/license.md b/docs/about/license.md index edf1181bd..31072519e 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Version Information diff --git a/docs/about/project-details.md b/docs/about/project-details.md index 46ddbf89b..10ce84eb1 100644 --- a/docs/about/project-details.md +++ b/docs/about/project-details.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # utPLSQL Project Details diff --git a/docs/about/support.md b/docs/about/support.md index 4092c00c9..2c9fcb98d 100644 --- a/docs/about/support.md +++ b/docs/about/support.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # How to get support diff --git a/docs/index.md b/docs/index.md index 20d119f93..3d48c47e6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Introduction to utPLSQL diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index 36ed86ae6..feb521f69 100644 --- a/docs/userguide/advanced_data_comparison.md +++ b/docs/userguide/advanced_data_comparison.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Advanced data comparison diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 33950623d..bb56ab4ef 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Annotations diff --git a/docs/userguide/best-practices.md b/docs/userguide/best-practices.md index 3378cafe4..89aa004ac 100644 --- a/docs/userguide/best-practices.md +++ b/docs/userguide/best-practices.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Best Practices diff --git a/docs/userguide/coverage.md b/docs/userguide/coverage.md index 203daffe0..115678802 100644 --- a/docs/userguide/coverage.md +++ b/docs/userguide/coverage.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Coverage utPLSQL comes with a built-in coverage reporting engine. The code coverage reporting is based on the DBMS_PROFILER package provided with Oracle database. diff --git a/docs/userguide/exception-reporting.md b/docs/userguide/exception-reporting.md index 3d01aafbd..14cded7c6 100644 --- a/docs/userguide/exception-reporting.md +++ b/docs/userguide/exception-reporting.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Exception handling and reporting diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index fe2369957..7876c7a79 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Expectation concepts Validation of the code under test (the tested logic of procedure/function etc.) is performed by comparing the actual data against the expected data. @@ -1114,7 +1114,8 @@ utPLSQL is capable of comparing compound data-types including: - It is possible to compare PL/SQL records, collections, varrays and associative arrays. To compare this types of data, use cursor comparison feature of utPLSQL and TABLE operator in SQL query - On Oracle 11g Release 2 - pipelined table functions are needed (see section [Implicit (Shadow) Types in this artcile](https://oracle-base.com/articles/misc/pipelined-table-functions)) - On Oracle 12c and above - use [TABLE function on nested tables/varrays/associative arrays of PL/SQL records](https://oracle-base.com/articles/12c/using-the-table-operator-with-locally-defined-types-in-plsql-12cr1) - +- utPLSQL is not able to distinguish between NULL and whitespace-only column/attribute value when comparing compound data. This is due to Oracle limitation on of XMLType. + See [issue #880](https://github.com/utPLSQL/utPLSQL/issues/880) for details. *Note: This behavior might be fixed in future releases, when utPLSQL is no longer depending on XMLType for compound data comparison.* utPLSQL offers advanced data-comparison options, for comparing compound data-types. The options allow you to: - define columns/attributes to exclude from comparison diff --git a/docs/userguide/getting-started.md b/docs/userguide/getting-started.md index aab93b2f8..290419986 100644 --- a/docs/userguide/getting-started.md +++ b/docs/userguide/getting-started.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Getting started with TDD and utPLSQL diff --git a/docs/userguide/install.md b/docs/userguide/install.md index 3d6e23843..520533cae 100644 --- a/docs/userguide/install.md +++ b/docs/userguide/install.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Downloading latest version of utPLSQL @@ -138,13 +138,6 @@ cd source sqlplus sys/sys_pass@db as sysdba @install_headless_with_trigger.sql utp3 my_verySecret_password utp3_tablespace ``` -**Note:** ->When installing utPLSQL into database with existing unit test packages, utPLSQL will not be able to already-existing unit test packages. When utPSLQL was installed with DDL trigger, you have to do one of: ->- Recompile existing Unit Test packages to make utPLSQL aware of their existence ->- Invoke `exec ut_runner.rebuild_annotation_cache(a_object_owner=> ... );` for every schema containing unit tests in your database -> -> Steps above are required to assure annotation cache is populated properly from existing objects. Rebuilding annotation cache might be faster than code recompilation. - # Recommended Schema It is highly recommended to install utPLSQL in it's own schema. You are free to choose any name for this schema. Installing uPLSQL into shared schema is really not recommended as you loose isolation of framework. diff --git a/docs/userguide/querying_suites.md b/docs/userguide/querying_suites.md index ceb1cbb13..d07c4836c 100644 --- a/docs/userguide/querying_suites.md +++ b/docs/userguide/querying_suites.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Qyerying for test suites diff --git a/docs/userguide/reporters.md b/docs/userguide/reporters.md index 798b2fd0b..8b3033539 100644 --- a/docs/userguide/reporters.md +++ b/docs/userguide/reporters.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) utPLSQL provides the following reporting formats. diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md index 2c8b4ebdd..4d7882852 100644 --- a/docs/userguide/running-unit-tests.md +++ b/docs/userguide/running-unit-tests.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Running tests diff --git a/docs/userguide/upgrade.md b/docs/userguide/upgrade.md index 5e48ee239..924e80d17 100644 --- a/docs/userguide/upgrade.md +++ b/docs/userguide/upgrade.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.9.3268-blue.svg) +![version](https://img.shields.io/badge/version-v3.1.10.3347-blue.svg) # Upgrading from version 2 diff --git a/readme.md b/readme.md index 2ccf8ee68..0d66152d0 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ [![license](https://img.shields.io/github/license/utPLSQL/utPLSQL.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![latest-release](https://img.shields.io/github/release/utPLSQL/utPLSQL.svg)](https://github.com/utPLSQL/utPLSQL/releases) -[![Github All Releases](https://img.shields.io/github/downloads/utPLSQL/utPLSQL/total.svg)](https://github.com/utPLSQL/utPLSQL/releases) +[![Download statistics](https://img.shields.io/github/downloads/utPLSQL/utPLSQL/total.svg)](http://gra.caldis.me/?url=https://github.com/utPLSQL/utPLSQL) [![chat](http://img.shields.io/badge/slack-team--chat-blue.svg)](http://utplsql-slack-invite.herokuapp.com/) [![twitter](https://img.shields.io/twitter/follow/utPLSQL.svg?style=social&label=Follow)](https://twitter.com/utPLSQL) diff --git a/sonar-project.properties b/sonar-project.properties index da061019b..7ab61df28 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -2,7 +2,7 @@ sonar.projectKey=utPLSQL # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. sonar.projectName=utPLSQL -sonar.projectVersion=v3.1.9 +sonar.projectVersion=v3.1.10 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. # Since SonarQube 4.2, this property is optional if sonar.modules is set. diff --git a/source/core/annotations/ut_annotation_cache_info.sql b/source/core/annotations/ut_annotation_cache_info.sql index e8dc2bfdb..7bbf99e00 100644 --- a/source/core/annotations/ut_annotation_cache_info.sql +++ b/source/core/annotations/ut_annotation_cache_info.sql @@ -17,7 +17,8 @@ create table ut_annotation_cache_info ( object_name varchar2(250) not null, object_type varchar2(250) not null, parse_time timestamp not null, - constraint ut_annotation_cache_info_pk primary key(cache_id), - constraint ut_annotation_cache_info_uk unique (object_owner, object_type, object_name) + constraint ut_annotation_cache_info_pk primary key(cache_id) using index, + constraint ut_annotation_cache_info_uk unique (object_owner, object_type, object_name) using index, + constraint ut_annotation_cache_info_fk foreign key(object_owner, object_type) references ut_annotation_cache_schema(object_owner, object_type) on delete cascade ) organization index; diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb index eb60ac3dd..4882108fc 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pkb +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -17,15 +17,25 @@ create or replace package body ut_annotation_cache_manager as */ procedure update_cache(a_object ut_annotated_object) is - l_cache_id integer; - l_new_objects_count integer := 0; + l_cache_id integer; + l_timestamp timestamp := systimestamp; pragma autonomous_transaction; begin + update ut_annotation_cache_schema s + set s.max_parse_time = l_timestamp + where s.object_type = a_object.object_type and s.object_owner = a_object.object_owner; + + if sql%rowcount = 0 then + insert into ut_annotation_cache_schema s + (object_owner, object_type, max_parse_time) + values (a_object.object_owner, a_object.object_type, l_timestamp); + end if; + -- if not in trigger, or object has annotations if ora_sysevent is null or a_object.annotations is not null and a_object.annotations.count > 0 then update ut_annotation_cache_info i - set i.parse_time = systimestamp + set i.parse_time = l_timestamp where (i.object_owner, i.object_name, i.object_type) in ((a_object.object_owner, a_object.object_name, a_object.object_type)) returning cache_id into l_cache_id; @@ -34,23 +44,12 @@ create or replace package body ut_annotation_cache_manager as insert into ut_annotation_cache_info (cache_id, object_owner, object_name, object_type, parse_time) - values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, systimestamp) + values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, l_timestamp) returning cache_id into l_cache_id; - l_new_objects_count := 1; end if; end if; - update ut_annotation_cache_schema s - set s.object_count = s.object_count + l_new_objects_count, s.max_parse_time = systimestamp - where s.object_type = a_object.object_type and s.object_owner = a_object.object_owner; - - if sql%rowcount = 0 then - insert into ut_annotation_cache_schema s - (object_owner, object_type, object_count, max_parse_time) - values (a_object.object_owner, a_object.object_type, l_new_objects_count, systimestamp); - end if; - delete from ut_annotation_cache c where cache_id = l_cache_id; if a_object.annotations is not null and a_object.annotations.count > 0 then @@ -63,7 +62,8 @@ create or replace package body ut_annotation_cache_manager as end; - procedure cleanup_cache(a_objects ut_annotation_objs_cache_info) is + procedure reset_objects_cache(a_objects ut_annotation_objs_cache_info) is + l_timestamp timestamp := systimestamp; pragma autonomous_transaction; begin @@ -75,23 +75,45 @@ create or replace package body ut_annotation_cache_manager as on o.object_name = i.object_name and o.object_type = i.object_type and o.object_owner = i.object_owner + and o.needs_refresh = 'Y' ); + update ut_annotation_cache_schema s + set s.max_parse_time = l_timestamp + where (s.object_owner, s.object_type) + in ( + select o.object_owner, o.object_type + from table(a_objects) o + where o.needs_refresh = 'Y' + ); + + if sql%rowcount = 0 then + insert into ut_annotation_cache_schema s + (object_owner, object_type, max_parse_time) + select distinct o.object_owner, o.object_type, l_timestamp + from table(a_objects) o + where o.needs_refresh = 'Y'; + end if; + merge into ut_annotation_cache_info i using (select o.object_name, o.object_type, o.object_owner - from table(a_objects) o ) o + from table(a_objects) o + where o.needs_refresh = 'Y' + ) o on (o.object_name = i.object_name and o.object_type = i.object_type and o.object_owner = i.object_owner) - when matched then update set parse_time = systimestamp + when matched then + update + set parse_time = l_timestamp when not matched then insert - (cache_id, object_owner, object_name, object_type, parse_time) - values (ut_annotation_cache_seq.nextval, o.object_owner, o.object_name, o.object_type, systimestamp); + (cache_id, object_owner, object_name, object_type, parse_time) + values (ut_annotation_cache_seq.nextval, o.object_owner, o.object_name, o.object_type, l_timestamp); commit; end; - function get_annotations_objects_info(a_object_owner varchar2, a_object_type varchar2) return ut_annotation_objs_cache_info is + function get_cached_objects_list(a_object_owner varchar2, a_object_type varchar2, a_parsed_after timestamp := null) return ut_annotation_objs_cache_info is l_result ut_annotation_objs_cache_info; begin select ut_annotation_obj_cache_info( @@ -104,7 +126,8 @@ create or replace package body ut_annotation_cache_manager as bulk collect into l_result from ut_annotation_cache_info i where i.object_owner = a_object_owner - and i.object_type = a_object_type; + and i.object_type = a_object_type + and (i.parse_time > a_parsed_after or a_parsed_after is null); return l_result; end; @@ -123,6 +146,16 @@ create or replace package body ut_annotation_cache_manager as return l_result; end; + procedure set_fully_refreshed(a_object_owner varchar2, a_object_type varchar2) is + pragma autonomous_transaction; + begin + update ut_annotation_cache_schema s + set s.full_refresh_time = s.max_parse_time + where s.object_owner = a_object_owner + and s.object_type = a_object_type; + commit; + end; + procedure remove_from_cache(a_objects ut_annotation_objs_cache_info) is pragma autonomous_transaction; begin @@ -138,10 +171,10 @@ create or replace package body ut_annotation_cache_manager as commit; end; - function get_annotations_for_objects(a_cached_objects ut_annotation_objs_cache_info, a_parse_time timestamp) return sys_refcursor is - l_results sys_refcursor; + function get_annotations_parsed_since(a_object_owner varchar2, a_object_type varchar2, a_parsed_after timestamp) return sys_refcursor is + l_results sys_refcursor; begin - open l_results for q'[ + open l_results for select ut_annotated_object( i.object_owner, i.object_name, i.object_type, i.parse_time, cast( @@ -151,14 +184,12 @@ create or replace package body ut_annotation_cache_manager as ) order by c.annotation_position ) as ut_annotations ) - ) - from table(:a_cached_objects) o - join ut_annotation_cache_info i - on o.object_owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type + ) as annotated_object + from ut_annotation_cache_info i join ut_annotation_cache c on i.cache_id = c.cache_id - where ]'|| case when a_parse_time is null then ':a_parse_date is null' else 'i.parse_time > :a_parse_time' end ||q'[ - group by i.object_owner, i.object_name, i.object_type, i.parse_time]' - using a_cached_objects, a_parse_time; + where i.object_owner = a_object_owner and i.object_type = a_object_type + and (i.parse_time > a_parsed_after or a_parsed_after is null) + group by i.object_owner, i.object_type, i.object_name, i.parse_time; return l_results; end; @@ -168,35 +199,24 @@ create or replace package body ut_annotation_cache_manager as pragma autonomous_transaction; begin if a_object_owner is null and a_object_type is null then - l_cache_filter := ':a_object_owner is null and :a_object_type is null'; - l_filter := l_cache_filter; + l_filter := ':a_object_owner is null and :a_object_type is null'; + l_cache_filter := l_filter; else - l_filter := - case when a_object_owner is null then ':a_object_owner is null' else 'object_owner = :a_object_owner' end || ' - and '||case when a_object_type is null then ':a_object_type is null' else 'object_type = :a_object_type' end; - l_cache_filter := ' c.cache_id - in (select i.cache_id - from ut_annotation_cache_info i - where '|| l_filter || ' - )'; + l_filter := case when a_object_owner is null then ':a_object_owner is null' else 'object_owner = :a_object_owner' end; + l_filter := l_filter || ' and ' || case when a_object_type is null then ':a_object_type is null' else 'object_type = :a_object_type' end; + l_cache_filter := ' c.cache_id in (select i.cache_id from ut_annotation_cache_info i where ' || l_filter || ' )'; end if; - execute immediate ' - delete from ut_annotation_cache c - where '||l_cache_filter + execute immediate 'delete from ut_annotation_cache c where ' || l_cache_filter using a_object_owner, a_object_type; - execute immediate ' - delete from ut_annotation_cache_info i - where ' || l_filter + execute immediate ' delete from ut_annotation_cache_info i where ' || l_filter using a_object_owner, a_object_type; - execute immediate ' - delete from ut_annotation_cache_schema s - where ' || l_filter + execute immediate ' delete from ut_annotation_cache_schema s where ' || l_filter using a_object_owner, a_object_type; commit; end; -end ut_annotation_cache_manager; +end; / diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks index 2a1d60f1e..e4ba6dd7a 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pks +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -16,6 +16,7 @@ create or replace package ut_annotation_cache_manager authid definer as limitations under the License. */ subtype t_cache_schema_info is ut_annotation_cache_schema%rowtype; + /** * Populates cache with information about object and it's annotations * Cache information for individual object is modified by this code @@ -30,21 +31,27 @@ create or replace package ut_annotation_cache_manager authid definer as * Returns a ref_cursor containing `ut_annotated_object` as result * Range of data returned is limited by the input collection o cache object info * - * @param a_cached_objects a `ut_annotation_objs_cache_info` list with information about objects to get from cache + * @param a_cached_objects - list of `ut_annotation_objs_cache_info` containing objects to get from cache + * @param a_min_parse_time - limit results to annotations parsed after specified time only, + * if null - all cached annotations for given objects are returned */ - function get_annotations_for_objects(a_cached_objects ut_annotation_objs_cache_info, a_parse_time timestamp) return sys_refcursor; - + function get_annotations_parsed_since(a_object_owner varchar2, a_object_type varchar2, a_parsed_after timestamp) return sys_refcursor; - function get_annotations_objects_info(a_object_owner varchar2, a_object_type varchar2) return ut_annotation_objs_cache_info; + procedure set_fully_refreshed(a_object_owner varchar2, a_object_type varchar2); function get_cache_schema_info(a_object_owner varchar2, a_object_type varchar2) return t_cache_schema_info; /** - * Removes cached information about annotations for objects on the list and updates parse_time in cache info table. + * Returns information about all objects stored in annotation cache + */ + function get_cached_objects_list(a_object_owner varchar2, a_object_type varchar2, a_parsed_after timestamp := null) return ut_annotation_objs_cache_info; + + /** + * Resets cached information about annotations for objects on the list and updates parse_time in cache info table. * * @param a_objects a `ut_annotation_objs_cache_info` list with information about objects to remove annotations for */ - procedure cleanup_cache(a_objects ut_annotation_objs_cache_info); + procedure reset_objects_cache(a_objects ut_annotation_objs_cache_info); /** * Removes information about objects on the list diff --git a/source/core/annotations/ut_annotation_cache_schema.sql b/source/core/annotations/ut_annotation_cache_schema.sql index abab7385e..b8f908e84 100644 --- a/source/core/annotations/ut_annotation_cache_schema.sql +++ b/source/core/annotations/ut_annotation_cache_schema.sql @@ -12,10 +12,10 @@ create table ut_annotation_cache_schema ( See the License for the specific language governing permissions and limitations under the License. */ - object_owner varchar2(250) not null, - object_type varchar2(250) not null, - object_count integer not null, - max_parse_time date not null, + object_owner varchar2(250) not null, + object_type varchar2(250) not null, + max_parse_time date not null, + full_refresh_time timestamp, constraint ut_annotation_cache_schema_pk primary key(object_owner, object_type) ) organization index; diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index fd2997632..a37391d37 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -19,86 +19,85 @@ create or replace package body ut_annotation_manager as ------------------------------ --private definitions - function get_missing_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotation_objs_cache_info is - l_rows sys_refcursor; - l_ut_owner varchar2(250) := ut_utils.ut_owner; - l_objects_view varchar2(200) := ut_metadata.get_objects_view_name(); - l_cursor_text varchar2(32767); - l_data ut_annotation_objs_cache_info; - l_result ut_annotation_objs_cache_info; - l_card natural; + function user_can_see_whole_schema( a_schema_name varchar2 ) return boolean is begin - l_data := ut_annotation_cache_manager.get_annotations_objects_info(a_object_owner, a_object_type); - l_card := ut_utils.scale_cardinality(cardinality(l_data)); - - l_cursor_text := - 'select /*+ cardinality(i '||l_card||') */ - value(i) - from table( cast( :l_data as '||l_ut_owner||'.ut_annotation_objs_cache_info ) ) i - where - not exists ( - select 1 from '||l_objects_view||q'[ o - where o.owner = i.object_owner - and o.object_name = i.object_name - and o.object_type = i.object_type - and o.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' - and o.object_type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' - )]'; - open l_rows for l_cursor_text using l_data; - fetch l_rows bulk collect into l_result limit ut_utils.gc_max_objects_fetch_limit; - close l_rows; - return l_result; + return sys_context('userenv','current_schema') = a_schema_name + or ut_metadata.user_has_execute_any_proc() + or ut_metadata.is_object_visible('dba_objects'); end; - function get_annotation_objs_info( - a_object_owner varchar2, - a_object_type varchar2, - a_parse_date timestamp := null, - a_full_scan boolean := true - ) return ut_annotation_objs_cache_info is - l_rows sys_refcursor; - l_ut_owner varchar2(250) := ut_utils.ut_owner; - l_objects_view varchar2(200) := ut_metadata.get_objects_view_name(); - l_cursor_text varchar2(32767); - l_data ut_annotation_objs_cache_info; - l_result ut_annotation_objs_cache_info; + function get_non_existing_objects( a_object_owner varchar2, a_object_type varchar2 ) return ut_annotation_objs_cache_info is + l_objects_view varchar2(200) := ut_metadata.get_objects_view_name(); + l_object_to_delete ut_annotation_objs_cache_info := ut_annotation_objs_cache_info(); + l_cached_objects ut_annotation_objs_cache_info; begin - ut_event_manager.trigger_event( - 'get_annotation_objs_info - start ( a_full_scan = ' || ut_utils.to_string(a_full_scan) || ' )' - ); + l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type ); + + if l_cached_objects is not empty then + execute immediate 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */ + value(i) + from table( :l_data ) i + where + not exists ( + select 1 from '||l_objects_view||q'[ o + where o.owner = i.object_owner + and o.object_name = i.object_name + and o.object_type = i.object_type + and o.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' + and o.object_type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' + )]' + bulk collect into l_object_to_delete + using l_cached_objects; + end if; + return l_object_to_delete; + end; - l_data := ut_annotation_cache_manager.get_annotations_objects_info(a_object_owner, a_object_type); + function get_objects_to_refresh( + a_object_owner varchar2, + a_object_type varchar2, + a_modified_after timestamp + ) return ut_annotation_objs_cache_info is + l_ut_owner varchar2(250) := ut_utils.ut_owner; + l_refresh_needed boolean; + l_objects_view varchar2(200) := ut_metadata.get_objects_view_name(); + l_cached_objects ut_annotation_objs_cache_info; + l_result ut_annotation_objs_cache_info; + begin + ut_event_manager.trigger_event( 'get_objects_to_refresh - start' ); - if not a_full_scan then - l_result := l_data; - else - l_cursor_text := - 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_data))||') */ - '||l_ut_owner||q'[.ut_annotation_obj_cache_info( - object_owner => o.owner, - object_name => o.object_name, - object_type => o.object_type, - needs_refresh => case when o.last_ddl_time < cast(i.parse_time as date) then 'N' else 'Y' end, - parse_time => i.parse_time - ) - from ]'||l_objects_view||' o - left join table( cast(:l_data as '||l_ut_owner||q'[.ut_annotation_objs_cache_info ) ) i - on o.owner = i.object_owner - and o.object_name = i.object_name - and o.object_type = i.object_type - where o.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' - and o.object_type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' - and ]' + l_refresh_needed := ( ut_trigger_check.is_alive() = false ) or a_modified_after is null; + l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type, a_modified_after ); + if l_refresh_needed then + --limit the list to objects that exist and are visible to the invoking user + --enrich the list by info about cache validity + execute immediate + 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */ + '||l_ut_owner||q'[.ut_annotation_obj_cache_info( + object_owner => o.owner, + object_name => o.object_name, + object_type => o.object_type, + needs_refresh => 'Y', + parse_time => c.parse_time + ) + from ]'||l_objects_view||' o + left join table( cast(:l_cached_objects as '||l_ut_owner||q'[.ut_annotation_objs_cache_info ) ) c + on o.owner = c.object_owner + and o.object_name = c.object_name + and o.object_type = c.object_type + where o.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' + and o.object_type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' + and case when o.last_ddl_time < cast(c.parse_time as date) then 'N' else 'Y' end = 'Y' + and ]' || case - when a_parse_date is null - then ':a_parse_date is null' - else 'o.last_ddl_time >= cast(:a_parse_date as date)' - end; - open l_rows for l_cursor_text using l_data, a_parse_date; - fetch l_rows bulk collect into l_result limit ut_utils.gc_max_objects_fetch_limit; - close l_rows; + when a_modified_after is null + then ':a_modified_after is null' + else 'o.last_ddl_time >= cast(:a_modified_after as date)' + end + bulk collect into l_result using l_cached_objects, a_modified_after; + else + l_result := l_cached_objects; end if; - ut_event_manager.trigger_event('get_annotation_objs_info - end (count='||l_result.count||')'); + ut_event_manager.trigger_event('get_objects_to_refresh - end (count='||l_result.count||')'); return l_result; end; @@ -109,24 +108,24 @@ create or replace package body ut_annotation_manager as begin l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); open l_result for - q'[select s.name, s.text + q'[select x.name, x.text from (select /*+ cardinality( r ]'||l_card||q'[ )*/ s.name, s.text, s.line, max(case when s.text like '%--%\%%' escape '\' - and regexp_like(s.text,'--\s*%') + and regexp_like(s.text,'^\s*--\s*%') then 'Y' else 'N' end ) over(partition by s.name) is_annotated from table(:a_objects_to_refresh) r join ]'||l_sources_view||q'[ s - on s.name = r.object_name + on s.name = r.object_name and s.owner = r.object_owner - and s.type = r.object_type - where s.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' - and s.type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' - ) s - where s.is_annotated = 'Y' - order by s.name, s.line]' + and s.type = r.object_type + where s.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' + and s.type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' + ) x + where x.is_annotated = 'Y' + order by x.name, x.line]' using a_objects_to_refresh; return l_result; @@ -174,47 +173,57 @@ create or replace package body ut_annotation_manager as end; - procedure refresh_annotation_cache( - a_object_owner varchar2, - a_object_type varchar2, - a_info_rows ut_annotation_objs_cache_info + procedure validate_annotation_cache( + a_object_owner varchar2, + a_object_type varchar2, + a_modified_after timestamp := null ) is - l_objects_to_parse ut_annotation_objs_cache_info; + l_objects_to_refresh ut_annotation_objs_cache_info; + l_modified_after timestamp := a_modified_after; begin - select value(x) - bulk collect into l_objects_to_parse - from table(a_info_rows) x where x.needs_refresh = 'Y'; + if ut_annotation_cache_manager.get_cache_schema_info(a_object_owner, a_object_type).full_refresh_time is null then + l_modified_after := null; + end if; - ut_event_manager.trigger_event('rebuild_annotation_cache - start (l_objects_to_parse.count = '||l_objects_to_parse.count||')'); - ut_annotation_cache_manager.cleanup_cache(l_objects_to_parse); + l_objects_to_refresh := get_objects_to_refresh(a_object_owner, a_object_type, l_modified_after); - if sys_context('userenv','current_schema') = a_object_owner - or ut_metadata.user_has_execute_any_proc() - or ut_metadata.is_object_visible('dba_objects') - then - ut_annotation_cache_manager.remove_from_cache( - get_missing_objects(a_object_owner, a_object_type) - ); + ut_event_manager.trigger_event('validate_annotation_cache - start (l_objects_to_refresh.count = '||l_objects_to_refresh.count||')'); + + + if user_can_see_whole_schema( a_object_owner ) then + --Remove non existing objects from cache only when user can see whole schema + ut_annotation_cache_manager.remove_from_cache( get_non_existing_objects( a_object_owner, a_object_type ) ); end if; --if some source needs parsing and putting into cache - if l_objects_to_parse.count > 0 then + if l_objects_to_refresh.count > 0 then + --Delete annotations for objects that are to be refreshed + ut_annotation_cache_manager.reset_objects_cache(l_objects_to_refresh); + --Rebuild cache from objects source build_annot_cache_for_sources( a_object_owner, a_object_type, - get_sources_to_annotate(a_object_owner, a_object_type, l_objects_to_parse) + get_sources_to_annotate(a_object_owner, a_object_type, l_objects_to_refresh) ); end if; - ut_event_manager.trigger_event('rebuild_annotation_cache - end'); + + if l_modified_after is null then + if user_can_see_whole_schema( a_object_owner ) then + ut_annotation_cache_manager.set_fully_refreshed( a_object_owner, a_object_type ); + else + -- if user cannot see full schema - we dont mark it as fully refreshed + -- it will get refreshed each time until someone with proper privs will refresh it + null; + end if; + end if; + ut_event_manager.trigger_event('validate_annotation_cache - end'); end; ------------------------------------------------------------ --public definitions ------------------------------------------------------------ procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2) is - l_annotation_objs_info ut_annotation_objs_cache_info; begin - l_annotation_objs_info := get_annotation_objs_info(a_object_owner, a_object_type, null, true); - refresh_annotation_cache( a_object_owner, a_object_type, l_annotation_objs_info ); + validate_annotation_cache( a_object_owner, a_object_type ); end; procedure trigger_obj_annotation_rebuild is @@ -295,17 +304,14 @@ create or replace package body ut_annotation_manager as end if; end; - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_parse_date timestamp := null) return sys_refcursor is - l_annotation_objs_info ut_annotation_objs_cache_info; + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_modified_after timestamp) return sys_refcursor is l_cursor sys_refcursor; - l_full_scan_needed boolean := not ut_trigger_check.is_alive(); begin - ut_event_manager.trigger_event('get_annotated_objects - start'); - l_annotation_objs_info := get_annotation_objs_info(a_object_owner, a_object_type, a_parse_date, l_full_scan_needed); - refresh_annotation_cache(a_object_owner, a_object_type, l_annotation_objs_info); + ut_event_manager.trigger_event('get_annotated_objects - start: a_modified_after='||ut_utils.to_string(a_modified_after)); + validate_annotation_cache(a_object_owner, a_object_type, a_modified_after); --pipe annotations from cache - l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_annotation_objs_info, a_parse_date); + l_cursor := ut_annotation_cache_manager.get_annotations_parsed_since(a_object_owner, a_object_type, a_modified_after); ut_event_manager.trigger_event('get_annotated_objects - end'); return l_cursor; end; @@ -315,5 +321,5 @@ create or replace package body ut_annotation_manager as ut_annotation_cache_manager.purge_cache(a_object_owner, a_object_type); end; -end ut_annotation_manager; +end; / diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index 1d6bd3d71..70257afac 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -23,18 +23,17 @@ create or replace package ut_annotation_manager authid current_user as /** * Gets annotations for all objects of a specified type for database schema. * Annotations that are stale or missing are parsed and placed in persistent cache. - * After placing in cache, annotation data is returned as pipelined table data. + * After placing in cache, annotation data is returned as ref_cursor. * - * @param a_object_owner owner of objects to get annotations for - * @param a_object_type type of objects to get annotations for - * @param a_parse_date date when object was last parsed - * @return array containing annotated objects along with annotations for each object (nested) + * @param a_object_owner owner of objects to get annotations for + * @param a_object_type type of objects to get annotations for + * @param a_modified_after return only objects modified after thr timestamp + * @return cursor containing annotated objects along with annotations for each object (nested) */ - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_parse_date timestamp := null) return sys_refcursor; + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_modified_after timestamp) return sys_refcursor; /** * Rebuilds annotation cache for a specified schema and object type. - * The procedure is called internally by `get_annotated_objects` function. * It can be used to speedup initial execution of utPLSQL on a given schema * if it is executed before any call is made to `ut.run` or `ut_runner.run` procedure. * @@ -57,5 +56,5 @@ create or replace package ut_annotation_manager authid current_user as procedure purge_cache(a_object_owner varchar2, a_object_type varchar2); -end ut_annotation_manager; +end; / diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index bbe58a667..6248f36b9 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -212,14 +212,6 @@ create or replace package body ut_annotation_parser as select value(x) bulk collect into l_result from table(l_annotations) x order by x.position; - -- printing out parsed structure for debugging - $if $$ut_trace $then - print_parse_results(l_result); - dbms_output.put_line('Annotations count: ' || l_result.count); - for i in 1 .. l_result.count loop - dbms_output.put_line(xmltype(l_result(i)).getclobval()); - end loop; - $end return l_result; end parse_object_annotations; @@ -229,7 +221,8 @@ create or replace package body ut_annotation_parser as l_annotations ut_annotations := ut_annotations(); ex_package_is_wrapped exception; pragma exception_init(ex_package_is_wrapped, -24241); - + source_text_is_empty exception; + pragma exception_init(source_text_is_empty, -24236); begin if a_source_lines.count > 0 then --convert to post-processed source clob @@ -248,12 +241,12 @@ create or replace package body ut_annotation_parser as l_annotations := parse_object_annotations(l_source); dbms_lob.freetemporary(l_source); exception - when ex_package_is_wrapped then + when ex_package_is_wrapped or source_text_is_empty then null; end; end if; return l_annotations; end; -end ut_annotation_parser; +end; / diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index b0a61e576..0368811db 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -40,5 +40,5 @@ create or replace package ut_annotation_parser authid current_user as */ function parse_object_annotations(a_source clob) return ut_annotations; -end ut_annotation_parser; +end; / diff --git a/source/core/annotations/ut_trigger_check.pkb b/source/core/annotations/ut_trigger_check.pkb index e07acd3b2..76d3aa11c 100644 --- a/source/core/annotations/ut_trigger_check.pkb +++ b/source/core/annotations/ut_trigger_check.pkb @@ -22,9 +22,7 @@ create or replace package body ut_trigger_check is function is_alive return boolean is pragma autonomous_transaction; begin - if not g_is_trigger_live then - execute immediate 'create or replace synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object'; - end if; + execute immediate 'create or replace synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object'; return g_is_trigger_live; end; diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb index a11797a54..6f25ea1c8 100644 --- a/source/core/types/ut_executable_test.tpb +++ b/source/core/types/ut_executable_test.tpb @@ -31,7 +31,7 @@ create or replace type body ut_executable_test as member procedure do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_varchar2_rows ) is l_completed_without_errors boolean; begin @@ -40,10 +40,114 @@ create or replace type body ut_executable_test as member function do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_varchar2_rows ) return boolean is l_expected_except_message varchar2(4000); + l_expected_error_numbers ut_integer_list; + function build_exception_numbers_list( + a_item in out nocopy ut_suite_item, + a_expected_error_codes in ut_varchar2_rows + ) return ut_integer_list is + l_exception_number integer; + l_exception_number_list ut_integer_list := ut_integer_list(); + c_regexp_for_exception_no constant varchar2(30) := '^-?[[:digit:]]{1,5}$'; + + c_integer_exception constant varchar2(1) := 'I'; + c_named_exception constant varchar2(1) := 'N'; + + function is_valid_qualified_name (a_name varchar2) return boolean is + l_name varchar2(500); + begin + l_name := dbms_assert.qualified_sql_name(a_name); + return true; + exception when others then + return false; + end; + + function check_exception_type(a_exception_name in varchar2) return varchar2 is + l_exception_type varchar2(50); + begin + --check if it is a predefined exception + begin + execute immediate 'begin null; exception when '||a_exception_name||' then null; end;'; + l_exception_type := c_named_exception; + exception + when others then + if dbms_utility.format_error_stack() like '%PLS-00485%' then + declare + e_invalid_number exception; + pragma exception_init ( e_invalid_number, -6502 ); + begin + execute immediate 'declare x integer := '||a_exception_name||'; begin null; end;'; + l_exception_type := c_integer_exception; + exception + when others then + null; + end; + end if; + end; + return l_exception_type; + end; + + function get_exception_number (a_exception_var in varchar2) return integer is + l_exc_no integer; + l_exc_type varchar2(50); + function remap_no_data_found (a_number integer) return integer is + begin + return case a_number when 100 then -1403 else a_number end; + end; + begin + l_exc_type := check_exception_type(a_exception_var); + + execute immediate + case l_exc_type + when c_integer_exception then + 'declare l_exception number; begin :l_exception := '||a_exception_var||'; end;' + when c_named_exception then + 'begin raise '||a_exception_var||'; exception when others then :l_exception := sqlcode; end;' + else + 'begin :l_exception := null; end;' + end + using out l_exc_no; + + return remap_no_data_found(l_exc_no); + end; + + begin + if a_expected_error_codes is not empty then + for i in 1 .. a_expected_error_codes.count loop + /** + * Check if its a valid qualified name and if so try to resolve name to an exception number + */ + if is_valid_qualified_name(a_expected_error_codes(i)) then + l_exception_number := get_exception_number(a_expected_error_codes(i)); + elsif regexp_like(a_expected_error_codes(i), c_regexp_for_exception_no) then + l_exception_number := a_expected_error_codes(i); + end if; + + if l_exception_number is null then + a_item.put_warning( + 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Parameter ignored.', + self.procedure_name, + a_item.line_no + ); + elsif l_exception_number >= 0 then + a_item.put_warning( + 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.', + self.procedure_name, + a_item.line_no + ); + else + l_exception_number_list.extend; + l_exception_number_list(l_exception_number_list.last) := l_exception_number; + end if; + l_exception_number := null; + end loop; + end if; + + return l_exception_number_list; + end; function failed_expec_errnum_message(a_expected_error_codes in ut_integer_list) return varchar is l_actual_error_no integer; l_expected_error_codes varchar2(4000); @@ -56,7 +160,7 @@ create or replace type body ut_executable_test as l_fail_message := 'Expected one of exceptions ('||l_expected_error_codes||') but nothing was raised.'; else l_actual_error_no := regexp_substr(self.error_stack, '^[a-zA-Z]{3}(-[0-9]+)', subexpression=>1); - if not l_actual_error_no member of a_expected_error_codes then + if not l_actual_error_no member of a_expected_error_codes or l_actual_error_no is null then l_fail_message := 'Actual: '||l_actual_error_no||' was expected to '; if cardinality(a_expected_error_codes) > 1 then l_fail_message := l_fail_message || 'be one of: ('||l_expected_error_codes||')'; @@ -72,9 +176,9 @@ create or replace type body ut_executable_test as begin --Create a ut_executable object and call do_execute after that get the data to know the test's execution result self.do_execute(a_item); - - if a_expected_error_codes is not null and a_expected_error_codes is not empty then - l_expected_except_message := failed_expec_errnum_message(a_expected_error_codes); + l_expected_error_numbers := build_exception_numbers_list(a_item, a_expected_error_codes); + if l_expected_error_numbers is not null and l_expected_error_numbers is not empty then + l_expected_except_message := failed_expec_errnum_message( l_expected_error_numbers ); if l_expected_except_message is not null then ut_expectation_processor.add_expectation_result( diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps index 2772720de..88500c9be 100644 --- a/source/core/types/ut_executable_test.tps +++ b/source/core/types/ut_executable_test.tps @@ -22,12 +22,12 @@ create or replace type ut_executable_test authid current_user under ut_executabl member procedure do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_varchar2_rows ), member function do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_varchar2_rows ) return boolean ) final; diff --git a/source/core/types/ut_suite_cache_row.tps b/source/core/types/ut_suite_cache_row.tps index 6817be5ec..d9f542de3 100644 --- a/source/core/types/ut_suite_cache_row.tps +++ b/source/core/types/ut_suite_cache_row.tps @@ -15,26 +15,26 @@ create type ut_suite_cache_row as object ( See the License for the specific language governing permissions and limitations under the License. */ - id number(22,0), - self_type varchar2(250 byte), - path varchar2(1000 byte), - object_owner varchar2(250 byte), - object_name varchar2(250 byte), - name varchar2(250 byte), - line_no number, - parse_time timestamp (6), - description varchar2(4000 byte), - rollback_type number, - disabled_flag number, - warnings ut_varchar2_rows, - before_all_list ut_executables, - after_all_list ut_executables, - before_each_list ut_executables, - before_test_list ut_executables, - after_each_list ut_executables, - after_test_list ut_executables, - expected_error_codes ut_integer_list, - tags ut_varchar2_rows, - item ut_executable_test + id number(22,0), + self_type varchar2(250 byte), + path varchar2(1000 byte), + object_owner varchar2(250 byte), + object_name varchar2(250 byte), + name varchar2(250 byte), + line_no number, + parse_time timestamp (6), + description varchar2(4000 byte), + rollback_type number, + disabled_flag number, + warnings ut_varchar2_rows, + before_all_list ut_executables, + after_all_list ut_executables, + before_each_list ut_executables, + before_test_list ut_executables, + after_each_list ut_executables, + after_test_list ut_executables, + expected_error_codes ut_varchar2_rows, + tags ut_varchar2_rows, + item ut_executable_test ) / \ No newline at end of file diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index 6478d5c82..9b939727a 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -97,6 +97,16 @@ create or replace type body ut_suite_item as self.results_count.increase_warning_count; end; + member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2, a_procedure_name varchar2, a_line_no integer) is + l_result varchar2(1000); + begin + l_result := self.object_owner || '.' || self.object_name ; + if a_procedure_name is not null then + l_result := l_result || '.' || a_procedure_name ; + end if; + put_warning( a_message || chr( 10 ) || 'at package "' || upper(l_result) || '", line ' || a_line_no ); + end; + member function get_transaction_invalidators return ut_varchar2_list is begin return transaction_invalidators; diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 53c59a2ea..87f577459 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -84,7 +84,8 @@ create or replace type ut_suite_item force under ut_event_item ( not instantiable member procedure mark_as_errored(self in out nocopy ut_suite_item, a_error_stack_trace varchar2), not instantiable member function get_error_stack_traces return ut_varchar2_list, not instantiable member function get_serveroutputs return clob, - member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2) + member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2), + member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2, a_procedure_name varchar2, a_line_no integer) ) not final not instantiable / diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 9fbcf9269..4ed3f665e 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -18,7 +18,7 @@ create or replace type body ut_test as constructor function ut_test( self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, - a_line_no integer, a_expected_error_codes ut_integer_list := null, a_tags ut_varchar2_rows := null + a_line_no integer, a_expected_error_codes ut_varchar2_rows := null, a_tags ut_varchar2_rows := null ) return self as result is begin self.self_type := $$plsql_unit; diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index fcf565671..85caf7380 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -54,10 +54,10 @@ create or replace type ut_test force under ut_suite_item ( /** *Holds the expected error codes list when the user use the annotation throws */ - expected_error_codes ut_integer_list, + expected_error_codes ut_varchar2_rows, constructor function ut_test( self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, - a_line_no integer, a_expected_error_codes ut_integer_list := null, a_tags ut_varchar2_rows := null + a_line_no integer, a_expected_error_codes ut_varchar2_rows := null, a_tags ut_varchar2_rows := null ) return self as result, overriding member procedure mark_as_skipped(self in out nocopy ut_test), overriding member function do_execute(self in out nocopy ut_test) return boolean, diff --git a/source/core/ut_metadata.pkb b/source/core/ut_metadata.pkb index 7859d6b3f..5448f1b5f 100644 --- a/source/core/ut_metadata.pkb +++ b/source/core/ut_metadata.pkb @@ -123,7 +123,7 @@ create or replace package body ut_metadata as function user_has_execute_any_proc return boolean is l_ut_owner varchar2(250) := ut_utils.ut_owner; begin - return is_object_visible(l_ut_owner||'.ut_utils'); + return is_object_visible(l_ut_owner||'.ut_utils') and sys_context('userenv','current_schema') != l_ut_owner; end; function is_object_visible(a_object_name varchar2) return boolean is diff --git a/source/core/ut_savepoint_seq.sql b/source/core/ut_savepoint_seq.sql index e1d80f452..b87d18e6d 100644 --- a/source/core/ut_savepoint_seq.sql +++ b/source/core/ut_savepoint_seq.sql @@ -12,4 +12,4 @@ create sequence ut_savepoint_seq See the License for the specific language governing permissions and limitations under the License. */ - start with 1 cache 20; \ No newline at end of file + start with 1 cache 20 minvalue 1 maxvalue 99999999999999999 cycle; \ No newline at end of file diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index c7e968467..d13cbaed9 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -15,7 +15,7 @@ create or replace package body ut_suite_builder is See the License for the specific language governing permissions and limitations under the License. */ - + subtype t_annotation_text is varchar2(4000); subtype t_annotation_name is varchar2(4000); subtype t_object_name is varchar2(500); @@ -62,9 +62,6 @@ create or replace package body ut_suite_builder is gc_endcontext ); - gc_integer_exception constant varchar2(1) := 'I'; - gc_named_exception constant varchar2(1) := 'N'; - type tt_executables is table of ut_executables index by t_annotation_position; type t_annotation is record( @@ -97,27 +94,6 @@ create or replace package body ut_suite_builder is by_name tt_annotations_by_name ); - function get_qualified_object_name( - a_suite ut_suite_item, a_procedure_name t_object_name - ) return varchar2 is - l_result varchar2(1000); - begin - if a_suite is not null then - l_result := upper( a_suite.object_owner || '.' || a_suite.object_name ); - if a_procedure_name is not null then - l_result := l_result || upper( '.' || a_procedure_name ); - end if; - end if; - return l_result; - end; - - function get_object_reference( - a_suite ut_suite_item, a_procedure_name t_object_name, a_line_no binary_integer - ) return varchar2 is - begin - return chr( 10 ) || 'at package "' || get_qualified_object_name(a_suite, a_procedure_name) || '", line ' || a_line_no; - end; - procedure delete_annotations_range( a_annotations in out nocopy t_annotations_info, a_start_pos t_annotation_position, @@ -163,8 +139,9 @@ create or replace package body ut_suite_builder is ) is begin a_suite.put_warning( - replace(a_message,'%%%','"--%'||a_annotation||'"') - || ' Annotation ignored.' || get_object_reference( a_suite, a_procedure_name, a_line_no ) + replace(a_message,'%%%','"--%'||a_annotation||'"')|| ' Annotation ignored.', + a_procedure_name, + a_line_no ); end; @@ -181,147 +158,37 @@ create or replace package body ut_suite_builder is procedure add_to_throws_numbers_list( a_suite in out nocopy ut_suite, - a_list in out nocopy ut_integer_list, + a_list in out nocopy ut_varchar2_rows, a_procedure_name t_object_name, a_throws_ann_text tt_annotation_texts ) is l_annotation_pos binary_integer; - function is_valid_qualified_name (a_name varchar2) return boolean is - l_name varchar2(500); - begin - l_name := dbms_assert.qualified_sql_name(a_name); - return true; - exception when others then - return false; - end; - - function check_exception_type(a_exception_name in varchar2) return varchar2 is - l_exception_type varchar2(50); - begin - --check if it is a predefined exception - begin - execute immediate 'begin null; exception when '||a_exception_name||' then null; end;'; - l_exception_type := gc_named_exception; - exception - when others then - if dbms_utility.format_error_stack() like '%PLS-00485%' then - begin - execute immediate 'declare x positiven := -('||a_exception_name||'); begin null; end;'; - l_exception_type := gc_integer_exception; - exception - when others then - --invalid exception number (positive) - --TODO add warning for this value - null; - end; - end if; - end; - return l_exception_type; - end; - - function get_exception_number (a_exception_var in varchar2) return integer is - l_exc_no integer; - l_exc_type varchar2(50); - function remap_no_data_found (a_number integer) return integer is - begin - return case a_number when 100 then -1403 else a_number end; - end; - begin - l_exc_type := check_exception_type(a_exception_var); - - if l_exc_type is not null then - - execute immediate - case l_exc_type - when gc_integer_exception then - 'declare - l_exception number; - begin - :l_exception := '||a_exception_var||'; ' - when gc_named_exception then - 'begin - raise '||a_exception_var||'; - exception - when others then - :l_exception := sqlcode; ' - end || - 'end;' - using out l_exc_no; - - end if; - return remap_no_data_found(l_exc_no); - end; - - function build_exception_numbers_list( - a_suite in out nocopy ut_suite, - a_procedure_name t_object_name, - a_line_no integer, - a_annotation_text in varchar2 - ) return ut_integer_list is - l_throws_list ut_varchar2_list; - l_exception_number integer; - l_exception_number_list ut_integer_list := ut_integer_list(); - c_regexp_for_exception_no constant varchar2(30) := '^-?[[:digit:]]{1,5}$'; - begin - --the a_expected_error_codes is converted to a ut_varchar2_list after that is trimmed and filtered to left only valid exception numbers - l_throws_list := ut_utils.trim_list_elements(ut_utils.string_to_table(a_annotation_text, ',', 'Y')); - - for i in 1 .. l_throws_list.count - loop - /** - * Check if its a valid qualified name and if so try to resolve name to an exception number - */ - if is_valid_qualified_name(l_throws_list(i)) then - l_exception_number := get_exception_number(l_throws_list(i)); - elsif regexp_like(l_throws_list(i), c_regexp_for_exception_no) then - l_exception_number := l_throws_list(i); - end if; - - if l_exception_number is null then - a_suite.put_warning( - 'Invalid parameter value "'||l_throws_list(i) - ||'" for "--%throws" annotation. Parameter ignored.'||get_object_reference( a_suite, a_procedure_name, a_line_no ) - ); - else - l_exception_number_list.extend; - l_exception_number_list(l_exception_number_list.last) := l_exception_number; - end if; - l_exception_number := null; - end loop; - - return l_exception_number_list; - end; - begin - a_list := ut_integer_list(); l_annotation_pos := a_throws_ann_text.first; while l_annotation_pos is not null loop if a_throws_ann_text(l_annotation_pos) is null then a_suite.put_warning( - '"--%throws" annotation requires a parameter. Annotation ignored.' - || get_object_reference( a_suite, a_procedure_name, l_annotation_pos ) + '"--%throws" annotation requires a parameter. Annotation ignored.', + a_procedure_name, + l_annotation_pos ); else - a_list := - a_list multiset union - build_exception_numbers_list( - a_suite, - a_procedure_name, - l_annotation_pos, - a_throws_ann_text(l_annotation_pos) - ); + ut_utils.append_to_list( + a_list, + ut_utils.convert_collection( ut_utils.trim_list_elements ( ut_utils.string_to_table( a_throws_ann_text(l_annotation_pos), ',' ) ) ) + ); end if; l_annotation_pos := a_throws_ann_text.next(l_annotation_pos); end loop; end; - + procedure add_tags_to_suite_item( a_suite in out nocopy ut_suite, a_tags_ann_text tt_annotation_texts, a_list in out nocopy ut_varchar2_rows, a_procedure_name t_object_name := null - ) is + ) is l_annotation_pos binary_integer; l_tags_list ut_varchar2_list := ut_varchar2_list(); l_tag_items ut_varchar2_list; @@ -330,8 +197,9 @@ create or replace package body ut_suite_builder is while l_annotation_pos is not null loop if a_tags_ann_text(l_annotation_pos) is null then a_suite.put_warning( - '"--%tags" annotation requires a tag value populated. Annotation ignored.' - || get_object_reference( a_suite, a_procedure_name, l_annotation_pos ) + '"--%tags" annotation requires a tag value populated. Annotation ignored.', + a_procedure_name, + l_annotation_pos ); else l_tag_items := ut_utils.trim_list_elements(ut_utils.string_to_table(a_tags_ann_text(l_annotation_pos),',')); @@ -342,8 +210,9 @@ create or replace package body ut_suite_builder is l_tags_list(l_tags_list.last) := l_tag_items(i); else a_suite.put_warning( - 'Invalid value "'||l_tag_items(i)||'" for "--%tags" annotation. See documentation for details on valid tag values. Annotation value ignored.' - || get_object_reference( a_suite, a_procedure_name, l_annotation_pos ) + 'Invalid value "'||l_tag_items(i)||'" for "--%tags" annotation. See documentation for details on valid tag values. Annotation value ignored.', + a_procedure_name, + l_annotation_pos ); end if; end loop; @@ -354,7 +223,7 @@ create or replace package body ut_suite_builder is --remove empty strings from table list e.g. tag1,,tag2 and convert to rows a_list := ut_utils.convert_collection( ut_utils.filter_list(set(l_tags_list),ut_utils.gc_word_no_space) ); end; - + procedure set_seq_no( a_list in out nocopy ut_executables ) is @@ -533,16 +402,16 @@ create or replace package body ut_suite_builder is ); set_seq_no(l_test.after_test_list); end if; - + if l_proc_annotations.exists( gc_tags) then add_tags_to_suite_item(a_suite, l_proc_annotations( gc_tags), l_test.tags, a_procedure_name); end if; - + if l_proc_annotations.exists( gc_throws) then add_to_throws_numbers_list(a_suite, l_test.expected_error_codes, a_procedure_name, l_proc_annotations( gc_throws)); end if; l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled)); - + a_suite_items.extend; a_suite_items( a_suite_items.last ) := l_test; @@ -687,7 +556,7 @@ create or replace package body ut_suite_builder is if a_annotations.by_name.exists(gc_aftereach) then l_after_each_list := add_executables( a_suite.object_owner, a_suite.object_name, a_annotations.by_name(gc_aftereach), gc_aftereach ); end if; - + if a_annotations.by_name.exists(gc_tags) then add_tags_to_suite_item(a_suite, a_annotations.by_name(gc_tags),a_suite.tags); end if; @@ -704,21 +573,59 @@ create or replace package body ut_suite_builder is set_seq_no(a_suite.after_all_list); end; + function get_next_annotation_of_type( + a_start_position t_annotation_position, + a_annotation_type varchar2, + a_package_annotations in tt_annotations_by_name + ) return t_annotation_position is + l_result t_annotation_position; + begin + if a_package_annotations.exists(a_annotation_type) then + l_result := a_package_annotations(a_annotation_type).first; + while l_result <= a_start_position loop + l_result := a_package_annotations(a_annotation_type).next(l_result); + end loop; + end if; + return l_result; + end; + function get_endcontext_position( a_context_ann_pos t_annotation_position, - a_package_annotations in out nocopy tt_annotations_by_name + a_package_annotations in tt_annotations_by_line ) return t_annotation_position is l_result t_annotation_position; + l_open_count integer := 1; + l_idx t_annotation_position := a_package_annotations.next(a_context_ann_pos); begin - if a_package_annotations.exists(gc_endcontext) then - l_result := a_package_annotations(gc_endcontext).first; - while l_result <= a_context_ann_pos loop - l_result := a_package_annotations(gc_endcontext).next(l_result); - end loop; + while l_open_count > 0 and l_idx is not null loop + if ( a_package_annotations(l_idx).name = gc_context ) then + l_open_count := l_open_count+1; + elsif ( a_package_annotations(l_idx).name = gc_endcontext ) then + l_open_count := l_open_count-1; + l_result := l_idx; + end if; + l_idx := a_package_annotations.next(l_idx); + end loop; + if ( l_open_count > 0 ) then + l_result := null; end if; return l_result; end; + function has_nested_context( + a_context_ann_pos t_annotation_position, + a_package_annotations in tt_annotations_by_name + ) return boolean is + l_next_endcontext_pos t_annotation_position := 0; + l_next_context_pos t_annotation_position := 0; + begin + if ( a_package_annotations.exists(gc_endcontext) and a_package_annotations.exists(gc_context)) then + l_next_endcontext_pos := get_next_annotation_of_type(a_context_ann_pos, gc_endcontext, a_package_annotations); + l_next_context_pos := a_package_annotations(gc_context).next(a_context_ann_pos); + end if; + return ( l_next_context_pos < l_next_endcontext_pos ); + end; + function get_annotations_in_context( a_annotations t_annotations_info, a_context_pos t_annotation_position, @@ -753,7 +660,8 @@ create or replace package body ut_suite_builder is a_parent in out nocopy ut_suite, a_annotations in out nocopy t_annotations_info, a_suite_items out nocopy ut_suite_items, - a_parent_context_pos in integer := 0 + a_parent_context_pos in integer := 0, + a_parent_end_context_pos in integer default null ) is l_context_pos t_annotation_position; l_next_context_pos t_annotation_position; @@ -768,26 +676,36 @@ create or replace package body ut_suite_builder is l_default_context_name t_object_name; function get_context_name( a_parent in out nocopy ut_suite, - a_context_names in tt_annotation_texts, - a_start_position binary_integer, - a_end_position binary_integer + a_start_position binary_integer ) return varchar2 is l_result t_annotation_name; l_found boolean; + l_end_position binary_integer; l_annotation_pos binary_integer; + l_context_names tt_annotation_texts; begin - l_annotation_pos := a_context_names.first; - while l_annotation_pos is not null loop - if l_annotation_pos > a_start_position and l_annotation_pos < a_end_position then - if l_found then - add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos); - else - l_result := a_context_names(l_annotation_pos); + if a_annotations.by_name.exists(gc_name) then + l_context_names := a_annotations.by_name( gc_name ); + -- Maximum end-position to look for %name annotation is either the next %context or the next %endcontext annotation + l_end_position := + least( + coalesce( get_next_annotation_of_type(a_start_position, gc_endcontext, a_annotations.by_name), a_annotations.by_line.last ), + coalesce( get_next_annotation_of_type(a_start_position, gc_context, a_annotations.by_name), a_annotations.by_line.last ) + ); + l_annotation_pos := l_context_names.first; + + while l_annotation_pos is not null loop + if l_annotation_pos > a_start_position and l_annotation_pos < l_end_position then + if l_found then + add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos); + else + l_result := l_context_names(l_annotation_pos); + end if; + l_found := true; end if; - l_found := true; - end if; - l_annotation_pos := a_context_names.next(l_annotation_pos); - end loop; + l_annotation_pos := l_context_names.next(l_annotation_pos); + end loop; + end if; return l_result; end; begin @@ -801,28 +719,17 @@ create or replace package body ut_suite_builder is while l_context_pos is not null loop l_default_context_name := 'nested_context_#'||l_context_no; l_context_name := null; - l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name ); - + l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_line ); l_next_context_pos := a_annotations.by_name(gc_context).next(l_context_pos); - if a_annotations.by_name.exists(gc_name) then - l_context_name := - get_context_name( - a_parent, - a_annotations.by_name( gc_name ), - l_context_pos, - least( - coalesce( l_end_context_pos, a_annotations.by_line.last ), - coalesce( l_next_context_pos, a_annotations.by_line.last ) - ) - ); - end if; + l_context_name := get_context_name(a_parent, l_context_pos); if not regexp_like( l_context_name, '^(\w|[$#])+$' ) or l_context_name is null then if not regexp_like( l_context_name, '^(\w|[$#])+$' ) then a_parent.put_warning( 'Invalid value "'||l_context_name||'" for context name.' || - ' Context name ignored and fallback to auto-name "'||l_default_context_name||'" ' || - get_object_reference( a_parent, null, l_context_pos ) - ); + ' Context name ignored and fallback to auto-name "'||l_default_context_name||'" ', + null, + l_context_pos + ); end if; l_context_name := l_default_context_name; end if; @@ -841,17 +748,18 @@ create or replace package body ut_suite_builder is l_context.parse_time := a_annotations.parse_time; --if nested context found - if l_next_context_pos < l_end_context_pos or l_end_context_pos is null then - get_context_items( l_context, a_annotations, l_context_items, l_context_pos ); - l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name ); + if has_nested_context(l_context_pos, a_annotations.by_name) then + get_context_items( l_context, a_annotations, l_context_items, l_context_pos, l_end_context_pos ); else l_context_items := ut_suite_items(); end if; if l_end_context_pos is null then a_parent.put_warning( - 'Missing "--%endcontext" annotation for a "--%context" annotation. The end of package is considered end of context.'|| get_object_reference( a_parent, null, l_context_pos ) - ); + 'Missing "--%endcontext" annotation for a "--%context" annotation. The end of package is considered end of context.', + null, + l_context_pos + ); l_end_context_pos := a_annotations.by_line.last; end if; @@ -870,6 +778,10 @@ create or replace package body ut_suite_builder is exit when not a_annotations.by_name.exists( gc_context); l_context_pos := a_annotations.by_name( gc_context).next( l_context_pos); + -- don't go on when the next context is outside the parent's context boundaries + if (a_parent_end_context_pos <= l_context_pos ) then + l_context_pos := null; + end if; l_context_no := l_context_no + 1; end loop; end; diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb index f52d15bf7..7c4b1ee93 100644 --- a/source/core/ut_suite_cache_manager.pkb +++ b/source/core/ut_suite_cache_manager.pkb @@ -23,7 +23,7 @@ create or replace package body ut_suite_cache_manager is gc_get_cache_suite_sql constant varchar2(32767) := q'[with suite_items as ( - select /*+ cardinality(c 100) */ value(c) as obj + select /*+ cardinality(c 500) */ value(c) as obj from ut_suite_cache c where 1 = 1 and c.object_owner = :l_object_owner @@ -57,7 +57,7 @@ create or replace package body ut_suite_cache_manager is select 'UT_LOGICAL_SUITE' as self_type, p.path, p.object_owner, upper( substr(p.path, instr( p.path, '.', -1 ) + 1 ) ) as object_name, cast(null as ut_executables) as x, - cast(null as ut_integer_list) as y, + cast(null as ut_varchar2_rows) as y, cast(null as ut_executable_test) as z from suitepath_part p where p.path @@ -87,7 +87,7 @@ create or replace package body ut_suite_cache_manager is l_result ut_varchar2_rows; l_data ut_annotation_objs_cache_info; begin - l_data := ut_annotation_cache_manager.get_annotations_objects_info(a_object_owner, 'PACKAGE'); + l_data := ut_annotation_cache_manager.get_cached_objects_list(a_object_owner, 'PACKAGE'); select i.object_name bulk collect into l_result @@ -162,14 +162,24 @@ create or replace package body ut_suite_cache_manager is return case when a_random_seed is null then q'[ replace( - case - when c.obj.self_type in ( 'UT_TEST' ) - then substr(c.obj.path, 1, instr(c.obj.path, '.', -1) ) - else c.obj.path - end, '.', chr(0) + --suite path until objects name (excluding contexts and test path) with trailing dot (full stop) + substr( c.obj.path, 1, instr( c.obj.path, lower(c.obj.object_name), -1 ) + length(c.obj.object_name) ), + '.', + --'.' replaced with chr(0) to assure that child elements come before parent when sorting in descending oder + chr(0) ) desc nulls last, - c.obj.object_name desc, - c.obj.line_no, + case when c.obj.self_type = 'UT_SUITE_CONTEXT' then + ( select max( x.line_no ) + 1 + from ut_suite_cache x + where c.obj.object_owner = x.object_owner + and c.obj.object_name = x.object_name + and x.path like c.obj.path || '.%' + ) + else + c.obj.line_no + end, + --assures that child contexts come before parent contexts + regexp_count(c.obj.path,'\.') desc, :a_random_seed]' else ' ut_runner.hash_suite_path( @@ -248,7 +258,7 @@ create or replace package body ut_suite_cache_manager is select min(t.parse_time) into l_cache_parse_time from ut_suite_cache_schema t - where object_owner = a_schema_name; + where object_owner = upper(a_schema_name); return l_cache_parse_time; end; @@ -370,13 +380,16 @@ create or replace package body ut_suite_cache_manager is pragma autonomous_transaction; begin l_objects := get_missing_cache_objects(a_schema_name); - delete from ut_suite_cache i - where i.object_owner = a_schema_name - and i.object_name in ( select column_value from table (l_objects) ); - delete from ut_suite_cache_package i - where i.object_owner = a_schema_name - and i.object_name in ( select column_value from table (l_objects) ); + if l_objects is not empty then + delete from ut_suite_cache i + where i.object_owner = a_schema_name + and i.object_name in ( select column_value from table (l_objects) ); + + delete from ut_suite_cache_package i + where i.object_owner = a_schema_name + and i.object_name in ( select column_value from table (l_objects) ); + end if; commit; end; @@ -449,5 +462,5 @@ create or replace package body ut_suite_cache_manager is return l_count > 0; end; -end ut_suite_cache_manager; +end; / diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index e71a51dc8..ee5063105 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -363,7 +363,8 @@ create or replace package body ut_suite_manager is where a.object_name = c.object_name and a.owner = c.object_owner and a.object_type = 'PACKAGE' - ); + ) + or c.self_type = 'UT_LOGICAL_SUITE'; end if; return l_result; @@ -373,7 +374,7 @@ create or replace package body ut_suite_manager is a_owner_name varchar2 ) return boolean is begin - return sys_context( 'userenv', 'current_schema' ) = a_owner_name or ut_metadata.user_has_execute_any_proc() or ut_trigger_check.is_alive(); + return sys_context( 'userenv', 'current_schema' ) = a_owner_name or ut_metadata.user_has_execute_any_proc(); end; procedure build_and_cache_suites( @@ -608,6 +609,7 @@ create or replace package body ut_suite_manager is and a.owner = c.object_owner and a.object_type = 'PACKAGE' ) + or c.item_type = 'UT_LOGICAL_SUITE' order by c.object_owner, c.object_name, c.item_line_no; end if; return l_result; @@ -627,7 +629,7 @@ create or replace package body ut_suite_manager is refresh_cache(l_owner_name); l_item_exists := ut_suite_cache_manager.suite_item_exists( l_owner_name, l_package_name, l_procedure_name ); - if not can_skip_all_objects_scan( l_owner_name ) then + if not can_skip_all_objects_scan( l_owner_name ) and l_package_name is not null then select count(1) into l_count from dual c diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 1e4b63c7f..87a14763c 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -22,6 +22,7 @@ create or replace package body ut_utils is gc_invalid_first_xml_char constant varchar2(50) := '[^_a-zA-Z]'; gc_invalid_xml_char constant varchar2(50) := '[^_a-zA-Z0-9\.-]'; gc_full_valid_xml_name constant varchar2(50) := '^([_a-zA-Z])([_a-zA-Z0-9\.-])*$'; + gc_owner_hash constant integer(11) := dbms_utility.get_hash_value( ut_owner(), 0, power(2,31)-1); function surround_with(a_value varchar2, a_quote_char varchar2) return varchar2 is begin @@ -59,7 +60,7 @@ create or replace package body ut_utils is function gen_savepoint_name return varchar2 is begin - return 's'||trim(to_char(ut_savepoint_seq.nextval,'0000000000000000000000000000')); + return 's'||gc_owner_hash||trim(to_char(ut_savepoint_seq.nextval,'00000000000000000')); end; procedure debug_log(a_message varchar2) is @@ -511,9 +512,11 @@ create or replace package body ut_utils is procedure flush_lines(a_lines ut_varchar2_rows, a_offset integer) is begin - insert into ut_dbms_output_cache (seq_no,text) - select rownum+a_offset, column_value - from table(a_lines); + if a_lines is not empty then + insert into ut_dbms_output_cache (seq_no,text) + select rownum+a_offset, column_value + from table(a_lines); + end if; end; begin loop @@ -533,7 +536,7 @@ create or replace package body ut_utils is procedure read_cache_to_dbms_output is l_lines_data sys_refcursor; l_lines ut_varchar2_rows; - c_lines_limit constant integer := 1000; + c_lines_limit constant integer := 10000; pragma autonomous_transaction; begin open l_lines_data for select text from ut_dbms_output_cache order by seq_no; @@ -766,7 +769,7 @@ create or replace package body ut_utils is /** * Change string into unicode to match xmlgen format _00_ * https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adxdb/generation-of-XML-data-from-relational-data.html#GUID-5BE09A7D-80D8-4734-B9AF-4A61F27FA9B2 - * secion v3.1.9.3268 + * secion v3.1.10.3347 */ function char_to_xmlgen_unicode(a_character varchar2) return varchar2 is begin diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index f1a19efb9..bd017020a 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -21,7 +21,7 @@ create or replace package ut_utils authid definer is * */ - gc_version constant varchar2(50) := 'v3.1.9.3268'; + gc_version constant varchar2(50) := 'v3.1.10.3347'; subtype t_executable_type is varchar2(30); gc_before_all constant t_executable_type := 'beforeall'; diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index d7c0aab76..69ba90811 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -119,8 +119,6 @@ create or replace package body ut_compound_data_helper is || case when a_order_enforced then q'[ --column position is not matching (both when excluded extra/missing columns as well as when they are included) or (a_pos_nn != e_pos_nn and exp_col_pos != act_col_pos)]' - else - null end ||q'[ order by exp_col_pos, act_col_pos]' bulk collect into l_results using a_expected, a_actual; @@ -417,39 +415,78 @@ create or replace package body ut_compound_data_helper is l_join_xpath varchar2(32767) := ut_utils.to_xpath(a_join_by_list); l_results tt_row_diffs; l_sql varchar2(32767); + l_column_order varchar2(100); begin + if a_enforce_column_order then + l_column_order := 'col_no'; + else + l_column_order := 'col_name'; + end if; l_sql := q'[ - with exp as ( + with + exp_cols as ( select - exp_item_data, exp_data_id, item_no rn, rownum col_no, pk_value, + i.exp_item_data, i.exp_data_id, i.item_no rn, rownum col_no, i.diff_id, s.column_value col, s.column_value.getRootElement() col_name, - nvl(s.column_value.getclobval(),empty_clob()) col_val + nvl( s.column_value.getclobval(), empty_clob() ) col_val from ( select - exp_data_id, extract( ucd.exp_item_data, :column_path ) exp_item_data, item_no, - replace( extract( ucd.exp_item_data, :join_by ).getclobval(), chr(10) ) pk_value - from ut_compound_data_diff_tmp ucd - where diff_id = :diff_id + ucd.exp_data_id, extract( ucd.exp_item_data, :column_path ) exp_item_data, + ucd.item_no, ucd.diff_id + from ut_compound_data_diff_tmp ucd + where ucd.diff_id = :diff_id and ucd.exp_data_id = :self_guid ) i, - table( xmlsequence( extract(i.exp_item_data,:extract_path) ) ) s + table( xmlsequence( extract( i.exp_item_data, :extract_path ) ) ) s ), - act as ( + act_cols as ( select - act_item_data, act_data_id, item_no rn, rownum col_no, pk_value, + i.act_item_data, i.act_data_id, i.item_no rn, rownum col_no, i.diff_id, s.column_value col, s.column_value.getRootElement() col_name, - nvl(s.column_value.getclobval(),empty_clob()) col_val + nvl( s.column_value.getclobval(), empty_clob() ) col_val from ( select - act_data_id, extract( ucd.act_item_data, :column_path ) act_item_data, item_no, - replace( extract( ucd.act_item_data, :join_by ).getclobval(), chr(10) ) pk_value - from ut_compound_data_diff_tmp ucd - where diff_id = :diff_id + ucd.act_data_id, extract( ucd.act_item_data, :column_path ) act_item_data, + ucd.item_no, ucd.diff_id + from ut_compound_data_diff_tmp ucd + where ucd.diff_id = :diff_id and ucd.act_data_id = :other_guid ) i, - table( xmlsequence( extract(i.act_item_data,:extract_path) ) ) s + table( xmlsequence( extract( i.act_item_data, :extract_path ) ) ) s + ), + data_diff as ( + select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, null pk_value, col_name, diff_id + from ( + select nvl(exp.rn, act.rn) rn, + exp.diff_id diff_id, + xmlagg(exp.col order by exp.col_no) exp_item, + xmlagg(act.col order by nvl(exp.col_no, act.col_no)) act_item, + max(nvl(exp.col_name,act.col_name)) col_name + from exp_cols exp + join act_cols act + on exp.rn = act.rn and exp.col_name = act.col_name + where dbms_lob.compare(exp.col_val, act.col_val) != 0 + group by nvl(exp.rn, act.rn), exp.diff_id + ) + unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') ) + ), + unordered_diff as ( + select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, null pk_value, col_name, diff_id + from ( + select nvl(exp.rn, act.rn) rn, + exp.diff_id diff_id, + xmlagg(exp.col order by exp.col_name) exp_item, + xmlagg(act.col order by act.col_name) act_item, + max(nvl(exp.col_name,act.col_name)) col_name + from exp_cols exp + join act_cols act + on exp.rn = act.rn and exp.col_name = act.col_name + where dbms_lob.compare(exp.col_val, act.col_val) != 0 + group by nvl(exp.rn, act.rn), exp.diff_id + ) + unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') ) ) - select rn, diff_type, diffed_row, pk_value pk_value + select rn, diff_type, diffed_row, pk_value from ( select rn, diff_type, diffed_row, pk_value, case when diff_type = 'Actual:' then 1 else 2 end rnk, @@ -457,33 +494,21 @@ create or replace package body ut_compound_data_helper is col_name from ( ]' || case when a_unordered then q'[ - select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, pk_value, col_name - from ( - select nvl(exp.rn, act.rn) rn, - nvl(exp.pk_value, act.pk_value) pk_value, - exp.col exp_item, - act.col act_item, - nvl(exp.col_name,act.col_name) col_name - from exp - join act - on exp.rn = act.rn and exp.col_name = act.col_name - where dbms_lob.compare(exp.col_val, act.col_val) != 0 - ) - unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') ) ]' + select /*+ no_unnest */ + u.rn, u.diff_type, u.diffed_row, + replace( + extract( case when i.exp_data_id is null then i.act_item_data else i.exp_item_data end, :join_by ).getclobval(), + chr(10) + ) pk_value, + u.col_name + from data_diff u + join ut_compound_data_diff_tmp i + on i.diff_id = u.diff_id + and i.item_no = u.rn]' else q'[ - select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, null pk_value, col_name - from ( - select nvl(exp.rn, act.rn) rn, - xmlagg(exp.col order by exp.col_no) exp_item, - xmlagg(act.col order by act.col_no) act_item, - max(nvl(exp.col_name,act.col_name)) col_name - from exp exp - join act act - on exp.rn = act.rn and exp.col_name = act.col_name - where dbms_lob.compare(exp.col_val, act.col_val) != 0 - group by (exp.rn, act.rn) - ) - unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') ) ]' + select rn, diff_type, diffed_row, pk_value, col_name + from data_diff + where :join_by is null ]' end ||q'[ ) union all @@ -522,14 +547,12 @@ create or replace package body ut_compound_data_helper is case when final_order = 1 then to_char(rn) else col_name end, case when final_order = 1 then to_char(rnk) else col_name end ]' - else - null end; execute immediate l_sql bulk collect into l_results - using l_exp_extract_xpath, l_join_xpath, a_diff_id, a_expected_dataset_guid,a_extract_path, - l_act_extract_xpath, l_join_xpath, a_diff_id, a_actual_dataset_guid,a_extract_path, - l_join_xpath, l_join_xpath, a_diff_id; + using l_exp_extract_xpath, a_diff_id, a_expected_dataset_guid, a_extract_path, + l_act_extract_xpath, a_diff_id, a_actual_dataset_guid, a_extract_path, + l_join_xpath, l_join_xpath, l_join_xpath, a_diff_id; return l_results; end; diff --git a/source/reporters/ut_teamcity_reporter.tpb b/source/reporters/ut_teamcity_reporter.tpb index ee0a53098..9475c0a90 100644 --- a/source/reporters/ut_teamcity_reporter.tpb +++ b/source/reporters/ut_teamcity_reporter.tpb @@ -74,7 +74,7 @@ create or replace type body ut_teamcity_reporter is begin l_idx := a_executables.first; while l_idx is not null loop - l_message := l_message || add_error_message(a_executables(l_idx).error_backtrace, a_message_name); + l_message := l_message || add_error_message( a_executables(l_idx).get_error_stack_trace(), a_message_name ); l_idx := a_executables.next(l_idx); end loop; return l_message; @@ -92,7 +92,7 @@ create or replace type body ut_teamcity_reporter is if a_test.result = ut_utils.gc_error then l_std_err_msg := l_std_err_msg || add_error_messages(a_test.before_each_list, 'Before each exception:'); l_std_err_msg := l_std_err_msg || add_error_messages(a_test.before_test_list, 'Before test exception:'); - l_std_err_msg := l_std_err_msg || add_error_message(a_test.item.error_backtrace, 'Test exception:'); + l_std_err_msg := l_std_err_msg || add_error_message(a_test.item.get_error_stack_trace(), 'Test exception:'); l_std_err_msg := l_std_err_msg || add_error_messages(a_test.after_test_list, 'After test exception:'); l_std_err_msg := l_std_err_msg || add_error_messages(a_test.after_each_list, 'After each exception:'); diff --git a/test/install_ut3_tester_helper.sql b/test/install_ut3_tester_helper.sql index c6244fd39..c0c20634e 100644 --- a/test/install_ut3_tester_helper.sql +++ b/test/install_ut3_tester_helper.sql @@ -26,6 +26,10 @@ alter session set plsql_optimize_level=0; @@ut3_tester_helper/expectations_helper.pkb @@ut3_tester_helper/ut_example_tests.pkb +@@ut3_tester_helper/annotation_cache_helper.pks +@@ut3_tester_helper/annotation_cache_helper.pkb +create or replace synonym ut3_tester.annotation_cache_helper for ut3_tester_helper.annotation_cache_helper; + set linesize 200 set define on set verify off diff --git a/test/install_ut3_tester_tests.sql b/test/install_ut3_tester_tests.sql index 6b3a5356c..9548d5148 100644 --- a/test/install_ut3_tester_tests.sql +++ b/test/install_ut3_tester_tests.sql @@ -13,6 +13,7 @@ alter session set plsql_optimize_level=0; @@ut3_tester/core/annotations/test_annotation_parser.pks @@ut3_tester/core/annotations/test_annot_throws_exception.pks @@ut3_tester/core/annotations/test_annotation_manager.pks +@@ut3_tester/core/annotations/test_annotation_cache.pks @@ut3_tester/core/expectations/test_expectation_processor.pks @@ut3_tester/core/test_ut_utils.pks @@ut3_tester/core/test_ut_test.pks @@ -27,9 +28,10 @@ alter session set plsql_optimize_level=0; @@ut3_tester/core.pkb @@ut3_tester/core/annotations/test_before_after_annotations.pkb @@ut3_tester/core/annotations/test_annotation_parser.pkb -@@ut3_tester/core/expectations/test_expectation_processor.pkb @@ut3_tester/core/annotations/test_annotation_manager.pkb @@ut3_tester/core/annotations/test_annot_throws_exception.pkb +@@ut3_tester/core/annotations/test_annotation_cache.pkb +@@ut3_tester/core/expectations/test_expectation_processor.pkb @@ut3_tester/core/test_ut_utils.pkb @@ut3_tester/core/test_ut_test.pkb @@ut3_tester/core/test_ut_suite.pkb diff --git a/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb b/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb index 1ad4f19a5..04e65a6c2 100644 --- a/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb +++ b/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb @@ -20,212 +20,230 @@ is c_e_diff_exc constant number := -20204; c_e_mix_list constant number := -20205; c_e_mix_missin constant number := -20206; - + c_e_positive constant number := 20207; + e_some_exception exception; pragma exception_init(e_some_exception, -20207); end;]'; l_package_spec := ' - create package annotated_package_with_throws is - --%suite(Dummy package to test annotation throws) - - --%test(Throws same annotated exception) - --%throws(-20145) - procedure raised_same_exception; - - --%test(Throws one of the listed exceptions) - --%throws(-20145,-20146, -20189 ,-20563) - procedure raised_one_listed_exception; - - --%test(Leading zero is ignored in exception list) - --%throws(-01476) - procedure leading_0_exception_no; - - --%test(Throws diff exception) - --%throws(-20144) - procedure raised_diff_exception; - - --%test(Throws empty) - --%throws() - procedure empty_throws; - - --%test(Ignores annotation and fails when exception was thrown) - --%throws(hello,784#,0-=234,,u1234) - procedure bad_paramters_with_except; - - --%test(Ignores annotation and succeeds when no exception thrown) - --%throws(hello,784#,0-=234,,u1234) - procedure bad_paramters_without_except; - - --%test(Detects a valid exception number within many invalid ones) - --%throws(7894562, operaqk, -=1, -1, pow74d, posdfk3) - procedure one_valid_exception_number; - - --%test(Gives failure when a exception is expected and nothing is thrown) - --%throws(-20459, -20136, -20145) - procedure nothing_thrown; - - --%test(Single exception defined as a constant number in package) - --%throws(exc_pkg.c_e_single_exc) - procedure single_exc_const_pkg; - - --%test(Gives success when one of annotated exception using constant is thrown) - --%throws(exc_pkg.c_e_list_1,exc_pkg.c_e_list_2) - procedure list_of_exc_constant; - - --%test(Gives failure when the raised exception is different that the annotated one using variable) - --%throws(exc_pkg.c_e_diff_exc) - procedure fail_not_match_exc; - - --%test(Success when one of exception from mixed list of number and constant is thrown) - --%throws(exc_pkg.c_e_mix_list,-20105) - procedure mixed_exc_list; - - --%test(Success when match exception even if other variable on list dont exists) - --%throws(exc_pkg.c_e_mix_missin,utter_rubbish) - procedure mixed_list_notexi; - - --%test(Success resolve and match named exception defined in pragma exception init) - --%throws(exc_pkg.e_some_exception) - procedure named_exc_pragma; - - --%test(Success resolve and match oracle named exception) - --%throws(NO_DATA_FOUND) - procedure named_exc_ora; - - --%test(Success resolve and match oracle named exception dup val index) - --%throws(DUP_VAL_ON_INDEX) - procedure named_exc_ora_dup_ind; - - --%test(Success map no data 100 to -1403) - --%throws(-1403) - procedure nodata_exc_ora; - - --%test(Success for exception defined as varchar) - --%throws(exc_pkg.c_e_varch_exc) - procedure defined_varchar_exc; - - --%test(Non existing constant exception) - --%throws(dummy.c_dummy); - procedure non_existing_const; - - --%test(Bad exception constant) - --%throws(exc_pkg.c_e_dummy); - procedure bad_exc_const; - - end; + create package annotated_package_with_throws is + --%suite(Dummy package to test annotation throws) + + --%test(Throws same annotated exception) + --%throws(-20145) + procedure raised_same_exception; + + --%test(Throws one of the listed exceptions) + --%throws(-20145,-20146, -20189 ,-20563) + procedure raised_one_listed_exception; + + --%test(Leading zero is ignored in exception list) + --%throws(-01476) + procedure leading_0_exception_no; + + --%test(Throws diff exception) + --%throws(-20144) + procedure raised_diff_exception; + + --%test(Throws empty) + --%throws() + procedure empty_throws; + + --%test(Ignores annotation and fails when exception was thrown) + --%throws(hello,784#,0-=234,,u1234) + procedure bad_paramters_with_except; + + --%test(Ignores annotation and succeeds when no exception thrown) + --%throws(hello,784#,0-=234,,u1234) + procedure bad_paramters_without_except; + + --%test(Ignores annotation for positive exception number value) + --%throws(20001) + procedure positive_exception_number; + + --%test(Ignores annotation for positive exception number variable) + --%throws(exc_pkg.c_e_positive) + procedure positive_exception_number_var; + + --%test(Detects a valid exception number within many invalid ones) + --%throws(7894562, operaqk, -=1, -1, pow74d, posdfk3) + procedure one_valid_exception_number; + + --%test(Gives failure when a exception is expected and nothing is thrown) + --%throws(-20459, -20136, -20145) + procedure nothing_thrown; + + --%test(Single exception defined as a constant number in package) + --%throws(exc_pkg.c_e_single_exc) + procedure single_exc_const_pkg; + + --%test(Gives success when one of annotated exception using constant is thrown) + --%throws(exc_pkg.c_e_list_1,exc_pkg.c_e_list_2) + procedure list_of_exc_constant; + + --%test(Gives failure when the raised exception is different that the annotated one using variable) + --%throws(exc_pkg.c_e_diff_exc) + procedure fail_not_match_exc; + + --%test(Success when one of exception from mixed list of number and constant is thrown) + --%throws(exc_pkg.c_e_mix_list,-20105) + procedure mixed_exc_list; + + --%test(Success when match exception even if other variable on list dont exists) + --%throws(exc_pkg.c_e_mix_missin,utter_rubbish) + procedure mixed_list_notexi; + + --%test(Success resolve and match named exception defined in pragma exception init) + --%throws(exc_pkg.e_some_exception) + procedure named_exc_pragma; + + --%test(Success resolve and match oracle named exception) + --%throws(NO_DATA_FOUND) + procedure named_exc_ora; + + --%test(Success resolve and match oracle named exception dup val index) + --%throws(DUP_VAL_ON_INDEX) + procedure named_exc_ora_dup_ind; + + --%test(Success map no data 100 to -1403) + --%throws(-1403) + procedure nodata_exc_ora; + + --%test(Success for exception defined as varchar) + --%throws(exc_pkg.c_e_varch_exc) + procedure defined_varchar_exc; + + --%test(Non existing constant exception) + --%throws(dummy.c_dummy); + procedure non_existing_const; + + --%test(Bad exception constant) + --%throws(exc_pkg.c_e_dummy); + procedure bad_exc_const; + end; '; l_package_body := ' - create package body annotated_package_with_throws is - procedure raised_same_exception is - begin - raise_application_error(-20145, ''Test error''); - end; - - procedure raised_one_listed_exception is - begin - raise_application_error(-20189, ''Test error''); - end; - - procedure leading_0_exception_no is - x integer; - begin - x := 1 / 0; - end; - - procedure raised_diff_exception is - begin - raise_application_error(-20143, ''Test error''); - end; - - procedure empty_throws is - begin - raise_application_error(-20143, ''Test error''); - end; - - procedure bad_paramters_with_except is - begin - raise_application_error(-20143, ''Test error''); - end; - - procedure bad_paramters_without_except is - begin - null; - end; - - procedure one_valid_exception_number is - begin - raise dup_val_on_index; - end; - - procedure nothing_thrown is - begin - null; - end; - - procedure single_exc_const_pkg is - begin - raise_application_error(exc_pkg.c_e_single_exc,''Test''); - end; - - procedure list_of_exc_constant is - begin - raise_application_error(exc_pkg.c_e_list_1,''Test''); - end; - - procedure fail_not_match_exc is - begin - raise NO_DATA_FOUND; - end; - - procedure mixed_exc_list is - begin - raise_application_error(exc_pkg.c_e_mix_list,''Test''); - end; - - procedure mixed_list_notexi is - begin - raise_application_error(exc_pkg.c_e_mix_missin,''Test''); - end; - - procedure named_exc_pragma is - begin - raise exc_pkg.e_some_exception; - end; - - procedure named_exc_ora is - begin - raise NO_DATA_FOUND; - end; - - procedure named_exc_ora_dup_ind is - begin - raise DUP_VAL_ON_INDEX; - end; - - procedure nodata_exc_ora is - begin - raise NO_DATA_FOUND; - end; - - procedure defined_varchar_exc is - begin - raise_application_error(exc_pkg.c_e_varch_exc,''Test''); - end; - - procedure non_existing_const is - begin - raise_application_error(-20143, ''Test error''); - end; - - procedure bad_exc_const is - begin - raise_application_error(-20143, ''Test error''); - end; - + create package body annotated_package_with_throws is + procedure raised_same_exception is + begin + raise_application_error(-20145, ''Test error''); + end; + + procedure raised_one_listed_exception is + begin + raise_application_error(-20189, ''Test error''); + end; + + procedure leading_0_exception_no is + x integer; + begin + x := 1 / 0; + end; + + procedure raised_diff_exception is + begin + raise_application_error(-20143, ''Test error''); + end; + + procedure empty_throws is + begin + raise_application_error(-20143, ''Test error''); + end; + + procedure bad_paramters_with_except is + begin + raise_application_error(-20143, ''Test error''); + end; + + procedure bad_paramters_without_except is + begin + null; + end; + + procedure positive_exception_number is + begin + null; + end; + + procedure positive_exception_number_var is + begin + null; + end; + + procedure one_valid_exception_number is + begin + raise dup_val_on_index; + end; + + procedure nothing_thrown is + begin + null; + end; + + procedure single_exc_const_pkg is + begin + raise_application_error(exc_pkg.c_e_single_exc,''Test''); + end; + + procedure list_of_exc_constant is + begin + raise_application_error(exc_pkg.c_e_list_1,''Test''); + end; + + procedure fail_not_match_exc is + begin + raise NO_DATA_FOUND; + end; + + procedure mixed_exc_list is + begin + raise_application_error(exc_pkg.c_e_mix_list,''Test''); + end; + + procedure mixed_list_notexi is + begin + raise_application_error(exc_pkg.c_e_mix_missin,''Test''); + end; + + procedure named_exc_pragma is + begin + raise exc_pkg.e_some_exception; + end; + + procedure named_exc_ora is + begin + raise NO_DATA_FOUND; + end; + + procedure named_exc_ora_dup_ind is + begin + raise DUP_VAL_ON_INDEX; + end; + + procedure nodata_exc_ora is + begin + raise NO_DATA_FOUND; + end; + + procedure defined_varchar_exc is + begin + raise_application_error(exc_pkg.c_e_varch_exc,''Test''); + end; + + procedure non_existing_const is + begin + raise_application_error(-20143, ''Test error''); end; + + procedure bad_exc_const is + begin + raise_application_error(-20143, ''Test error''); + end; + + end; '; execute immediate l_exception_spec; @@ -277,13 +295,25 @@ is procedure bad_paramters_without_except is begin ut.expect(g_tests_results).to_match('^\s*Ignores annotation and succeeds when no exception thrown \[[,\.0-9]+ sec\]\s*$','m'); - ut.expect(g_tests_results).not_to_match('bad_paramters_without_except'); + ut.expect(g_tests_results).to_match('bad_paramters_without_except\s*Invalid parameter value ".*" for "--%throws" annotation. Parameter ignored.','m'); + end; + + procedure positive_exception_number is + begin + ut.expect(g_tests_results).to_match('^\s*Ignores annotation for positive exception number value \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect(g_tests_results).to_match('positive_exception_number\s*Invalid parameter value "20001" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.','m'); + end; + + procedure positive_exception_number_var is + begin + ut.expect(g_tests_results).to_match('^\s*Ignores annotation for positive exception number variable \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect(g_tests_results).to_match('positive_exception_number_var\s*Invalid parameter value ".*" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.','m'); end; procedure one_valid_exception_number is begin ut.expect(g_tests_results).to_match('^\s*Detects a valid exception number within many invalid ones \[[\.0-9]+ sec\]\s*$','m'); - ut.expect(g_tests_results).not_to_match('one_valid_exception_number'); + ut.expect(g_tests_results).to_match('one_valid_exception_number\s*Invalid parameter value ".*" for "--%throws" annotation. Parameter ignored.','m'); end; procedure nothing_thrown is @@ -319,13 +349,13 @@ is procedure mixed_list_notexi is begin ut.expect(g_tests_results).to_match('^\s*Success when match exception even if other variable on list dont exists \[[,\.0-9]+ sec\]\s*$','m'); - ut.expect(g_tests_results).not_to_match('mixed_list_notexi'); + ut.expect(g_tests_results).to_match('mixed_list_notexi\s*Invalid parameter value "utter_rubbish" for "--%throws" annotation. Parameter ignored.','m'); end; procedure named_exc_pragma is begin ut.expect(g_tests_results).to_match('^\s*Success resolve and match named exception defined in pragma exception init \[[,\.0-9]+ sec\]\s*$','m'); - ut.expect(g_tests_results).not_to_match('mixed_list_notexi'); + ut.expect(g_tests_results).not_to_match('named_exc_pragma'); end; procedure named_exc_ora is diff --git a/test/ut3_tester/core/annotations/test_annot_throws_exception.pks b/test/ut3_tester/core/annotations/test_annot_throws_exception.pks index ce2e6ba7a..a9d20183d 100644 --- a/test/ut3_tester/core/annotations/test_annot_throws_exception.pks +++ b/test/ut3_tester/core/annotations/test_annot_throws_exception.pks @@ -26,7 +26,13 @@ is --%test(Ignores when only bad parameters are passed, the test does not raise a exception and it shows successful test) procedure bad_paramters_without_except; - + + --%test(Ignores annotation for positive exception number value) + procedure positive_exception_number; + + --%test(Ignores annotation for positive exception number variable) + procedure positive_exception_number_var; + --%test(Detects a valid exception number within many invalid ones) procedure one_valid_exception_number; diff --git a/test/ut3_tester/core/annotations/test_annotation_cache.pkb b/test/ut3_tester/core/annotations/test_annotation_cache.pkb new file mode 100644 index 000000000..372fec890 --- /dev/null +++ b/test/ut3_tester/core/annotations/test_annotation_cache.pkb @@ -0,0 +1,503 @@ +create or replace package body test_annotation_cache is + + procedure cache_populated_for_packages(a_packages ut_varchar2_rows) is + l_actual_cache_info sys_refcursor; + l_expected_cache_info sys_refcursor; + begin + open l_actual_cache_info for + select * + from ut3.ut_annotation_cache_info + where object_owner = 'UT3_CACHE_TEST_OWNER'; + open l_expected_cache_info for + select 'UT3_CACHE_TEST_OWNER' as object_owner, upper( column_value ) as object_name + from table (a_packages) x; + ut.expect( l_actual_cache_info ).to_equal( l_expected_cache_info ).exclude( 'CACHE_ID,PARSE_TIME,OBJECT_TYPE' ).JOIN_BY('OBJECT_NAME'); + end; + + procedure can_run_one_package(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + + --Assert - only granted_test_suite is invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%2 tests, 0 failed%' ); + rollback; + end; + + procedure can_run_new_package(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + --Assert -Both granted_test_suite and new_suite are invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%new_suite%4 tests, 0 failed%' ); + rollback; + end ; + + procedure cant_run_revoked_package(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + annotation_cache_helper.revoke_granted_suite( ); + + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + --Assert -Only new_suite gets invoked + ut.expect( l_actual ).to_be_like( 'new_suite%2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s)%' ); + ut.expect( l_actual ).not_to_be_like( '%granted_test_suite%' ); + rollback; + end; + + procedure cant_run_dropped_package(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.revoke_granted_suite( ); + annotation_cache_helper.cleanup_new_suite( ); + + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + --Assert - no test suites are invoked + ut.expect( l_actual ).to_be_like( '%0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s)%' ); + ut.expect( l_actual ).not_to_be_like( '%new_suite%' ); + ut.expect( l_actual ).not_to_be_like( '%granted_test_suite%' ); + rollback; + end; + + procedure can_run_all_packages(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + + --Assert - only granted_test_suite is invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%not_granted_test_suite%4 tests, 0 failed%' ); + rollback; + end; + + procedure can_run_all_new_packages(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + + --Assert - only granted_test_suite is invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%new_suite%not_granted_test_suite% tests, 0 failed%' ); + rollback; + end; + + procedure can_run_revoked_packages(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + annotation_cache_helper.revoke_granted_suite( ); + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + + --Assert - only granted_test_suite is invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%new_suite%not_granted_test_suite% tests, 0 failed%' ); + rollback; + end; + + procedure can_run_all_but_dropped(a_user varchar2) is + l_actual clob; + l_current_time date := sysdate; + pragma autonomous_transaction; + begin + --Arrange + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.add_new_suite( ); + l_actual := annotation_cache_helper.run_tests_as( a_user ); + annotation_cache_helper.revoke_granted_suite( ); + annotation_cache_helper.cleanup_new_suite( ); + + --Act + l_actual := annotation_cache_helper.run_tests_as( a_user ); + --Assert - no test suites are invoked + ut.expect( l_actual ).to_be_like( 'granted_test_suite%not_granted_test_suite%4 tests, 0 failed%' ); + ut.expect( l_actual ).not_to_be_like( '%new_suite%' ); + rollback; + end; + + + procedure user_can_run_one_package is + begin + can_run_one_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure user_can_run_new_package is + begin + can_run_new_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure user_cant_run_revoked_package is + begin + cant_run_revoked_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure user_cant_run_dropped_package is + begin + cant_run_dropped_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_cat_user_can_run_one is + begin + can_run_one_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure sel_cat_user_can_run_new is + begin + can_run_new_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_cat_user_cant_run_revoked is + begin + cant_run_revoked_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_cat_user_cant_run_dropped is + begin + cant_run_dropped_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_any_user_can_run_one is + begin + can_run_one_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure sel_any_user_can_run_new is + begin + can_run_new_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_any_user_cant_run_revoked is + begin + cant_run_revoked_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure sel_any_user_cant_run_dropped is + begin + cant_run_dropped_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure exe_any_user_can_run_all is + begin + can_run_all_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure exe_any_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure exe_any_user_can_run_revoked is + begin + can_run_revoked_packages('ut3_execute_any_proc_user'); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure exe_any_user_cant_run_dropped is + begin + can_run_all_but_dropped('ut3_execute_any_proc_user'); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure owner_user_can_run_all is + begin + can_run_all_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + + procedure owner_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure owner_user_cant_run_dropped is + begin + can_run_all_but_dropped('ut3_cache_test_owner'); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + + procedure t_user_can_run_one_package is + begin + can_run_one_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_user_can_run_new_package is + begin + can_run_new_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_user_cant_run_revoked_pkg is + begin + cant_run_revoked_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_user_cant_run_dropped_pkg is + begin + cant_run_dropped_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_cat_user_can_run_one is + begin + can_run_one_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_cat_user_can_run_new is + begin + can_run_new_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_cat_user_cant_run_revokd is + begin + cant_run_revoked_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_cat_user_cant_run_dropd is + begin + cant_run_dropped_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + + procedure t_sel_any_user_can_run_one is + begin + can_run_one_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_any_user_can_run_new is + begin + can_run_new_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_any_user_cant_run_revokd is + begin + cant_run_revoked_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_sel_any_user_cant_run_dropd is + begin + cant_run_dropped_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + + procedure t_exe_any_user_can_run_all is + begin + can_run_all_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_exe_any_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_exe_any_user_can_run_revokd is + begin + can_run_revoked_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_exe_any_user_cant_run_dropd is + begin + can_run_all_but_dropped( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_owner_user_can_run_all is + begin + can_run_all_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_owner_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure t_owner_user_cant_run_dropd is + begin + can_run_all_but_dropped( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + + + + procedure p_user_can_run_one_package is + begin + can_run_one_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure p_user_can_run_new_package is + begin + can_run_new_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_user_cant_run_revoked_pack is + begin + cant_run_revoked_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_user_cant_run_dropped_pack is + begin + cant_run_dropped_package( 'ut3_no_extra_priv_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + + + procedure p_sel_cat_user_can_run_one is + begin + can_run_one_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure p_sel_cat_user_can_run_new is + begin + can_run_new_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_sel_cat_user_cant_run_revokd is + begin + cant_run_revoked_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_sel_cat_user_cant_run_dropd is + begin + cant_run_dropped_package( 'ut3_select_catalog_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure p_sel_any_user_can_run_one is + begin + can_run_one_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure p_sel_any_user_can_run_new is + begin + can_run_new_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_sel_any_user_cant_run_revokd is + begin + cant_run_revoked_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE' ) ); + end; + + procedure p_sel_any_user_cant_run_dropd is + begin + cant_run_dropped_package( 'ut3_select_any_table_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE' ) ); + end; + + procedure p_exe_any_user_can_run_all is + begin + can_run_all_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_exe_any_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_exe_any_user_can_run_revokd is + begin + can_run_revoked_packages( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_exe_any_user_cant_run_dropd is + begin + can_run_all_but_dropped( 'ut3_execute_any_proc_user' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_owner_user_can_run_all is + begin + can_run_all_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_owner_user_can_run_new is + begin + can_run_all_new_packages( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NEW_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + + procedure p_owner_user_cant_run_dropped is + begin + can_run_all_but_dropped( 'ut3_cache_test_owner' ); + cache_populated_for_packages( ut_varchar2_rows( 'GRANTED_TEST_SUITE', 'NOT_GRANTED_TEST_SUITE' ) ); + end; + +end; +/ diff --git a/test/ut3_tester/core/annotations/test_annotation_cache.pks b/test/ut3_tester/core/annotations/test_annotation_cache.pks new file mode 100644 index 000000000..f44b816e1 --- /dev/null +++ b/test/ut3_tester/core/annotations/test_annotation_cache.pks @@ -0,0 +1,273 @@ +create or replace package test_annotation_cache is + + --%suite(annotation cache) + --%suitepath(utplsql.ut3_tester.core.annotations) + --%beforeall(annotation_cache_helper.create_run_function_for_users) + --%afterall(annotation_cache_helper.drop_run_function_for_users) + + --%context(With DDL trigger enabled) + + --%beforeall(annotation_cache_helper.enable_ddl_trigger) + --%beforeeach(annotation_cache_helper.setup_two_suites) + --%aftereach(annotation_cache_helper.cleanup_new_suite) + --%afterall(annotation_cache_helper.cleanup_two_suites) + + --%context(User without elevated privileges) + + --%test(Can only execute granted packages) + procedure t_user_can_run_one_package; + + --%test(Can see newly created packages as they are added) + procedure t_user_can_run_new_package; + + --%test(Cannot execute revoked packages) + procedure t_user_cant_run_revoked_pkg; + + --%test(Cannot execute dropped unit test packages) + procedure t_user_cant_run_dropped_pkg; + + --%endcontext + + --%context(User with select_catalog_role) + + --%test(Can only execute granted packages) + procedure t_sel_cat_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure t_sel_cat_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure t_sel_cat_user_cant_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure t_sel_cat_user_cant_run_dropd; + + --%endcontext + + --%context(User with select any table privilege) + + --%test(Can only execute granted packages) + procedure t_sel_any_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure t_sel_any_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure t_sel_any_user_cant_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure t_sel_any_user_cant_run_dropd; + + --%endcontext + + --%context(User with execute any procedure) + + --%test(Can execute and see all unit test packages) + procedure t_exe_any_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure t_exe_any_user_can_run_new; + + --%test(Can execute revoked packages) + procedure t_exe_any_user_can_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure t_exe_any_user_cant_run_dropd; + + --%endcontext + + --%context(User owning test packages) + + --%test(Can execute and see all unit test packages) + procedure t_owner_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure t_owner_user_can_run_new; + + --%test(Cannot execute dropped unit test packages) + procedure t_owner_user_cant_run_dropd; + + --%endcontext + + --%endcontext + + --%context(With DDL trigger disabled) + + --%beforeall(annotation_cache_helper.disable_ddl_trigger) + --%beforeeach(annotation_cache_helper.setup_two_suites) + --%beforeeach(annotation_cache_helper.purge_annotation_cache) + --%aftereach(annotation_cache_helper.cleanup_new_suite) + --%afterall(annotation_cache_helper.enable_ddl_trigger) + --%afterall(annotation_cache_helper.cleanup_two_suites) + + --%context(User without elevated privileges) + + --%test(Can only execute granted packages) + procedure user_can_run_one_package; + + --%test(Can see newly created packages as they are added) + procedure user_can_run_new_package; + + --%test(Cannot execute revoked packages) + procedure user_cant_run_revoked_package; + + --%test(Cannot execute dropped unit test packages) + procedure user_cant_run_dropped_package; + + --%endcontext + + --%context(User with select_catalog_role) + + --%test(Can only execute granted packages) + procedure sel_cat_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure sel_cat_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure sel_cat_user_cant_run_revoked; + + --%test(Cannot execute dropped unit test packages) + procedure sel_cat_user_cant_run_dropped; + + --%endcontext + + --%context(User with select any table privilege) + + --%test(Can only execute granted packages) + procedure sel_any_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure sel_any_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure sel_any_user_cant_run_revoked; + + --%test(Cannot execute dropped unit test packages) + procedure sel_any_user_cant_run_dropped; + + --%endcontext + + --%context(User with execute any procedure) + + --%test(Can execute and see all unit test packages) + procedure exe_any_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure exe_any_user_can_run_new; + + --%test(Can execute revoked packages) + procedure exe_any_user_can_run_revoked; + + --%test(Cannot execute dropped unit test packages) + procedure exe_any_user_cant_run_dropped; + + --%endcontext + + --%context(User owning test packages) + + --%test(Can execute and see all unit test packages) + procedure owner_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure owner_user_can_run_new; + + --%test(Cannot execute dropped unit test packages) + procedure owner_user_cant_run_dropped; + + --%endcontext + + --%endcontext + + --%context(With DDL trigger enabled and cache purged) + + --%beforeall(annotation_cache_helper.enable_ddl_trigger) + + --%beforeeach(annotation_cache_helper.setup_two_suites) + --%beforeeach(annotation_cache_helper.purge_annotation_cache) + + --%aftereach(annotation_cache_helper.cleanup_new_suite) + --%aftereach(annotation_cache_helper.cleanup_two_suites) + + --%context(User without elevated privileges) + + --%test(Can only execute granted packages) + procedure p_user_can_run_one_package; + + --%test(Can see newly created packages as they are added) + procedure p_user_can_run_new_package; + + --%test(Cannot execute revoked packages) + procedure p_user_cant_run_revoked_pack; + + --%test(Cannot execute dropped unit test packages) + procedure p_user_cant_run_dropped_pack; + + --%endcontext + + --%context(User with select_catalog_role) + + --%test(Can only execute granted packages) + procedure p_sel_cat_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure p_sel_cat_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure p_sel_cat_user_cant_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure p_sel_cat_user_cant_run_dropd; + + --%endcontext + + --%context(User with select any table privilege) + + --%test(Can only execute granted packages) + procedure p_sel_any_user_can_run_one; + + --%test(Can see newly created packages as they are added) + procedure p_sel_any_user_can_run_new; + + --%test(Cannot execute revoked packages) + procedure p_sel_any_user_cant_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure p_sel_any_user_cant_run_dropd; + + --%endcontext + + --%context(User with execute any procedure) + + --%test(Can execute and see all unit test packages) + procedure p_exe_any_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure p_exe_any_user_can_run_new; + + --%test(Can execute revoked packages) + procedure p_exe_any_user_can_run_revokd; + + --%test(Cannot execute dropped unit test packages) + procedure p_exe_any_user_cant_run_dropd; + + --%endcontext + + --%context(User owning test packages) + + --%test(Can execute and see all unit test packages) + procedure p_owner_user_can_run_all; + + --%test(Can see newly created packages as they are added) + procedure p_owner_user_can_run_new; + + --%test(Cannot execute dropped unit test packages) + procedure p_owner_user_cant_run_dropped; + + --%endcontext + + + --%endcontext + +end; +/ diff --git a/test/ut3_tester/core/annotations/test_annotation_manager.pkb b/test/ut3_tester/core/annotations/test_annotation_manager.pkb index d564e5d57..8a3efc508 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pkb +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pkb @@ -1,45 +1,34 @@ create or replace package body test_annotation_manager is - procedure disable_ddl_trigger is + procedure exec_autonomous(a_sql varchar2) is pragma autonomous_transaction; begin - execute immediate 'alter trigger ut3.ut_trigger_annotation_parsing disable'; - execute immediate 'begin ut3.ut_trigger_check.is_alive(); end;'; - end; - - procedure enable_ddl_trigger is - pragma autonomous_transaction; - begin - execute immediate 'alter trigger ut3.ut_trigger_annotation_parsing enable'; + execute immediate a_sql; end; procedure create_dummy_package is - pragma autonomous_transaction; begin - execute immediate q'[create or replace package dummy_package as + exec_autonomous(q'[create or replace package dummy_package as procedure some_dummy_procedure; - end;]'; + end;]'); end; procedure drop_dummy_package is - pragma autonomous_transaction; begin - execute immediate q'[drop package dummy_package]'; + exec_autonomous(q'[drop package dummy_package]'); exception when others then null; end; procedure recompile_dummy_package is - pragma autonomous_transaction; begin - execute immediate q'[alter package dummy_package compile]'; + exec_autonomous(q'[alter package dummy_package compile]'); end; procedure create_dummy_test_package is - pragma autonomous_transaction; begin - execute immediate q'[ + exec_autonomous(q'[ /* * Some multiline comments before package spec create or replace package dummy_test_package dummy comment to prove that we pick the right piece of code @@ -55,34 +44,31 @@ create or replace package body test_annotation_manager is --%test(dummy_test) --%beforetest(some_procedure) procedure some_dummy_test_procedure; - end;]'; - execute immediate q'[grant execute on dummy_test_package to public]'; + end;]'); + exec_autonomous(q'[grant execute on dummy_test_package to public]'); end; procedure modify_dummy_test_package is - pragma autonomous_transaction; begin - execute immediate q'[create or replace package dummy_test_package as + exec_autonomous(q'[create or replace package dummy_test_package as --%suite(dummy_test_suite) --%test(dummy_test) procedure some_dummy_test_procedure; - end;]'; + end;]'); end; procedure drop_dummy_test_package is - pragma autonomous_transaction; begin - execute immediate q'[drop package dummy_test_package]'; + exec_autonomous(q'[drop package dummy_test_package]'); exception when others then null; end; procedure recompile_dummy_test_package is - pragma autonomous_transaction; begin - execute immediate q'[alter package dummy_test_package compile]'; + exec_autonomous(q'[alter package dummy_test_package compile]'); end; procedure create_parse_proc_as_ut3$user# is @@ -128,35 +114,31 @@ create or replace package body test_annotation_manager is ut.expect(l_actual).to_be_empty(); end; - procedure assert_dummy_test_package(a_start_date date) is + procedure assert_dummy_test_package(a_start_time timestamp) is l_actual_cache_id integer; + l_data ut3.ut_annotated_objects; + l_result sys_refcursor; l_actual sys_refcursor; l_expected sys_refcursor; begin - select max(cache_id) - into l_actual_cache_id - from ut3.ut_annotation_cache_info - where object_owner = sys_context('USERENV', 'CURRENT_USER') and object_type = 'PACKAGE' and object_name = 'DUMMY_TEST_PACKAGE' - and parse_time >= a_start_date; - ut.expect(l_actual_cache_id).to_be_not_null; - - open l_actual for - select annotation_position, annotation_name, annotation_text, subobject_name - from ut3.ut_annotation_cache where cache_id = l_actual_cache_id - order by annotation_position; - open l_expected for - select 2 as annotation_position, 'suite' as annotation_name, - 'dummy_test_suite' as annotation_text, '' as subobject_name - from dual union all - select 3, 'rollback' , 'manual', '' as subobject_name - from dual union all - select 7, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name - from dual union all - select 8, 'beforetest' , 'some_procedure', 'some_dummy_test_procedure' as subobject_name - from dual; - - ut.expect(l_actual).to_equal(l_expected); + select + ut3.ut_annotated_object( + sys_context('USERENV', 'CURRENT_USER'), + 'DUMMY_TEST_PACKAGE', 'PACKAGE', a_start_time, + ut3.ut_annotations( + ut3.ut_annotation( 2, 'suite', 'dummy_test_suite', null ), + ut3.ut_annotation( 3, 'rollback', 'manual', null ), + ut3.ut_annotation( 7, 'test', 'dummy_test', 'some_dummy_test_procedure' ), + ut3.ut_annotation( 8, 'beforetest', 'some_procedure', 'some_dummy_test_procedure' ) + ) + ) annotated_object + from dual; + + l_result := ut3.ut_annotation_manager.get_annotated_objects(sys_context('USERENV', 'CURRENT_USER'), 'PACKAGE', a_start_time); + fetch l_result bulk collect into l_data; + open l_actual for select value(x) as annotated_object from table(l_data) x where object_name = 'DUMMY_TEST_PACKAGE'; + ut.expect(l_actual).to_equal(l_expected).exclude('ANNOTATED_OBJECT/PARSE_TIME').join_by('ANNOTATED_OBJECT/OBJECT_NAME'); end; @@ -164,10 +146,10 @@ create or replace package body test_annotation_manager is l_actual_cache_id integer; begin --Arrange - disable_ddl_trigger(); + annotation_cache_helper.disable_ddl_trigger(); create_dummy_test_package(); --Act - enable_ddl_trigger(); + annotation_cache_helper.enable_ddl_trigger(); --Assert select max(cache_id) into l_actual_cache_id @@ -182,10 +164,10 @@ create or replace package body test_annotation_manager is l_start_date date; begin --Arrange - disable_ddl_trigger(); + annotation_cache_helper.disable_ddl_trigger(); create_dummy_test_package(); --Act - enable_ddl_trigger(); + annotation_cache_helper.enable_ddl_trigger(); l_start_date := sysdate; recompile_dummy_test_package(); --Assert @@ -197,11 +179,11 @@ create or replace package body test_annotation_manager is l_start_date date; begin --Arrange - disable_ddl_trigger(); + annotation_cache_helper.disable_ddl_trigger(); create_dummy_test_package(); create_dummy_package(); --Act - enable_ddl_trigger(); + annotation_cache_helper.enable_ddl_trigger(); l_start_date := sysdate; ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); --Assert @@ -259,74 +241,123 @@ create or replace package body test_annotation_manager is end; - --%test(Updates annotation cache when package recompiled) - procedure trg_update_modified_package is + procedure trg_populate_cache_after_purge is + l_start_date date; begin - null; + --Arrange + create_dummy_test_package(); + l_start_date := sysdate; + ut3.ut_annotation_manager.purge_cache(sys_context('USERENV', 'CURRENT_USER'), 'PACKAGE'); + --Act & Assert + assert_dummy_test_package(l_start_date); end; - procedure add_new_package is - l_actual_cache_id integer; - l_actual sys_refcursor; - l_start_date date; + procedure add_annotated_package is + l_start_time timestamp := systimestamp; + begin + --Arrange + create_dummy_test_package(); + --Act & Assert + assert_dummy_test_package( l_start_time ); + end; + + procedure remove_annotated_package is + l_start_time timestamp := systimestamp; begin + --Arrange + create_dummy_test_package(); + assert_dummy_test_package( l_start_time ); + --Act - l_start_date := sysdate; - ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); + drop_dummy_test_package(); + --Assert - select max(cache_id) - into l_actual_cache_id - from ut3.ut_annotation_cache_info - where object_owner = sys_context('USERENV', 'CURRENT_USER') and object_type = 'PACKAGE' and object_name = 'DUMMY_PACKAGE' - and parse_time >= l_start_date; + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ), + 'Annotations are empty after package was dropped' + ).to_be_empty(); + end; - ut.expect(l_actual_cache_id).to_be_not_null; + procedure add_not_annotated_package is + l_start_time timestamp := systimestamp; + begin + --Arrange + create_dummy_package(); + --Act & Assert + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ), + 'Annotations are empty for not annotated package' + ).to_be_empty(); + end; - open l_actual for - select * - from ut3.ut_annotation_cache - where cache_id = l_actual_cache_id; + procedure remove_not_annotated_package is + l_start_time timestamp := systimestamp; + begin + --Arrange + create_dummy_package(); + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ), + 'Annotations are empty for non annotated package' + ).to_be_empty(); - ut.expect(l_actual).to_be_empty(); + --Act + drop_dummy_package(); + --Assert + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ), + 'Annotations are empty after non annoteted package was dropped' + ).to_be_empty(); end; - procedure update_modified_package is - l_actual integer; - l_start_date date; + procedure remove_annotations_from_pkg is + l_start_time timestamp := systimestamp; begin --Arrange - l_start_date := sysdate; - ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); - recompile_dummy_package(); - l_start_date := sysdate; - $if dbms_db_version.version >= 18 $then - dbms_session.sleep(1); - $else - dbms_lock.sleep(1); - $end + create_dummy_test_package(); + assert_dummy_test_package( l_start_time ); --Act - ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); + exec_autonomous(q'[create or replace package dummy_test_package as + procedure some_dummy_test_procedure; + end;]'); + --Assert - assert_dummy_package(l_start_date); + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ) + ).to_be_empty(); end; - - procedure add_new_test_package is - l_actual sys_refcursor; - l_expected sys_refcursor; - l_start_date date; + procedure add_annotations_to_package is + l_start_time timestamp := systimestamp; begin --Arrange - l_start_date := sysdate; + exec_autonomous(q'[create or replace package dummy_test_package as + procedure some_dummy_test_procedure; + end;]'); + ut.expect( + ut3.ut_annotation_manager.get_annotated_objects( + sys_context( 'USERENV', 'CURRENT_USER' ), 'PACKAGE', l_start_time + ) + ).to_be_empty(); + --Act - ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); + create_dummy_test_package(); + --Assert - assert_dummy_test_package(l_start_date); + assert_dummy_test_package( l_start_time ); end; - procedure update_modified_test_package is l_actual_cache_id integer; l_actual sys_refcursor; @@ -365,29 +396,35 @@ create or replace package body test_annotation_manager is procedure keep_dropped_data_in_cache is - l_actual sys_refcursor; - l_expected sys_refcursor; - l_start_date date; + l_cache_count integer; + l_start_date date; begin - parse_dummy_test_as_ut3$user#(); l_start_date := sysdate; + parse_dummy_test_as_ut3$user#(); drop_dummy_test_package(); --Act parse_dummy_test_as_ut3$user#(); --Assert - assert_dummy_test_package(l_start_date); + select count(1) + into l_cache_count + from ut3.ut_annotation_cache_info + where object_owner = sys_context('USERENV', 'CURRENT_USER') + and object_type = 'PACKAGE' + and object_name = 'DUMMY_TEST_PACKAGE' + and parse_time > l_start_date; + ut.expect( l_cache_count ).to_equal(1); end; procedure no_data_for_dropped_object is - l_result sys_refcursor; - l_data ut3.ut_annotated_objects; - l_actual sys_refcursor; + l_result sys_refcursor; + l_data ut3.ut_annotated_objects; + l_actual sys_refcursor; + l_start_time timestamp := systimestamp; begin --Arrange - ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); drop_dummy_test_package(); --Act - l_result := ut3.ut_annotation_manager.get_annotated_objects(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); + l_result := ut3.ut_annotation_manager.get_annotated_objects( sys_context('USERENV', 'CURRENT_USER'),'PACKAGE', l_start_time ); fetch l_result bulk collect into l_data; open l_actual for select object_name from table(l_data) where object_name = 'DUMMY_TEST_PACKAGE'; --Assert @@ -396,13 +433,9 @@ create or replace package body test_annotation_manager is procedure cleanup_dropped_data_in_cache is l_cache_count integer; - l_actual sys_refcursor; - l_expected sys_refcursor; - l_start_date date; begin --Arrange ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); - l_start_date := sysdate; drop_dummy_test_package(); --Act ut3.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); @@ -418,5 +451,16 @@ create or replace package body test_annotation_manager is end; + procedure populate_cache_after_purge is + l_start_date date; + begin + --Arrange + create_dummy_test_package(); + l_start_date := sysdate; + ut3.ut_annotation_manager.purge_cache(sys_context('USERENV', 'CURRENT_USER'), 'PACKAGE'); + --Act & Assert + assert_dummy_test_package(l_start_date); + end; + end test_annotation_manager; / diff --git a/test/ut3_tester/core/annotations/test_annotation_manager.pks b/test/ut3_tester/core/annotations/test_annotation_manager.pks index fd5bc3410..5d7464fd2 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pks +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pks @@ -3,13 +3,11 @@ create or replace package test_annotation_manager is --%suite(ut_annotation_manager) --%suitepath(utplsql.ut3_tester.core.annotations) + --%afterall(drop_dummy_test_package) + --%aftereach procedure cleanup_annotation_cache; - procedure disable_ddl_trigger; - - procedure enable_ddl_trigger; - procedure create_dummy_package; procedure drop_dummy_package; @@ -45,30 +43,43 @@ create or replace package test_annotation_manager is --%beforetest(create_dummy_test_package) procedure trg_no_data_for_dropped_object; - --%test(Updates annotation cache when package recompiled) - procedure trg_update_modified_package; + --%test(Objects are populated on scan after cache was purged) + --%beforetest(annotation_cache_helper.disable_ddl_trigger) + --%aftertest(annotation_cache_helper.enable_ddl_trigger) + procedure trg_populate_cache_after_purge; --%endcontext - --%context(Without DDL trigger enabled) + --%context(Without DDL trigger) - --%beforeall(disable_ddl_trigger) + --%beforeall(annotation_cache_helper.disable_ddl_trigger) - --%afterall(enable_ddl_trigger) + --%afterall(annotation_cache_helper.enable_ddl_trigger) --%beforeeach(create_dummy_package) --%aftereach(drop_dummy_package) - --%test(Adds new package to annotation cache info when it is not unit test package) - procedure add_new_package; + --%test(Returns annotations when annotated package was created) + --%aftertest(drop_dummy_test_package) + procedure add_annotated_package; - --%test(Updates annotation cache info for modified package) - procedure update_modified_package; + --%test(Doesn't return annotations when annotated package was removed) + --%aftertest(drop_dummy_test_package) + procedure remove_annotated_package; - --%test(Adds annotations to cache for unit test package) - --%beforetest(create_dummy_test_package) + --%test(Doesn't return annotations when package doesn't contain annotations) + procedure add_not_annotated_package; + + --%test(Doesn't return annotations when package without annotations was dropped) + procedure remove_not_annotated_package; + + --%test(Doesn't return annotations when annotations removed from package) --%aftertest(drop_dummy_test_package) - procedure add_new_test_package; + procedure remove_annotations_from_pkg; + + --%test(Returns annotations when annotations were added to package) + --%aftertest(drop_dummy_test_package) + procedure add_annotations_to_package; --%test(Updates annotations in cache for modified test package) procedure update_modified_test_package; @@ -84,6 +95,9 @@ create or replace package test_annotation_manager is --%test(Remove object from cache when object dropped and user can see whole schema) procedure cleanup_dropped_data_in_cache; + --%test(Objects are populated on scan after cache was purged) + procedure populate_cache_after_purge; + --%endcontext end test_annotation_manager; diff --git a/test/ut3_tester/core/test_suite_builder.pkb b/test/ut3_tester/core/test_suite_builder.pkb index a204a04e6..8eedc1142 100644 --- a/test/ut3_tester/core/test_suite_builder.pkb +++ b/test/ut3_tester/core/test_suite_builder.pkb @@ -649,6 +649,9 @@ create or replace package body test_suite_builder is '' || '%' || '%' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || '' || '%a_contextA contextsome_package.a_context' || '%' || @@ -661,9 +664,6 @@ create or replace package body test_suite_builder is '%' || '' || '' || - '' || - '%suite_level_testIn suitesome_package.suite_level_test' || - '%' || '' || '' || '%some_packagesuite_level_beforeall' || @@ -710,26 +710,14 @@ create or replace package body test_suite_builder is '' || '%' || '%' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || '' || '%a_contextA contextsome_package.a_context' || '%' || '' || - '%nested_context_2nested_context_2some_package.a_context.nested_context_2' || - '%' || - '' || - '%nested_context_#1a_nested_context_3some_package.a_context.nested_context_2.nested_context_#1' || - '%' || - '' || - '%test_in_nested_context_3Test in nested contextsome_package.a_context.nested_context_2.nested_context_#1.test_in_nested_context_3' || - '%' || - '' || - '' || - '%' || - '' || - '%test_in_nested_context_2Test in nested contextsome_package.a_context.nested_context_2.test_in_nested_context_2' || - '%' || - '' || - '' || + '%first_test_in_a_contextFirst test in contextsome_package.a_context.first_test_in_a_context' || '%' || '' || '%a_nested_contextA nested contextsome_package.a_context.a_nested_context' || @@ -743,7 +731,22 @@ create or replace package body test_suite_builder is '%' || '%' || '' || - '%first_test_in_a_contextFirst test in contextsome_package.a_context.first_test_in_a_context' || + '%nested_context_2nested_context_2some_package.a_context.nested_context_2' || + '%' || + '' || + '%test_in_nested_context_2Test in nested contextsome_package.a_context.nested_context_2.test_in_nested_context_2' || + '%' || + '' || + '%nested_context_#1a_nested_context_3some_package.a_context.nested_context_2.nested_context_#1' || + '%' || + '' || + '%test_in_nested_context_3Test in nested contextsome_package.a_context.nested_context_2.nested_context_#1.test_in_nested_context_3' || + '%' || + '' || + '' || + '%' || + '' || + '' || '%' || '' || '%second_test_in_a_contextSecond test in contextsome_package.a_context.second_test_in_a_context' || @@ -754,9 +757,6 @@ create or replace package body test_suite_builder is '%' || '' || '' || - '' || - '%suite_level_testIn suitesome_package.suite_level_test' || - '%' || '' || '' || '%some_packagesuite_level_beforeall' || @@ -767,6 +767,74 @@ create or replace package body test_suite_builder is ); end; + procedure nested_contexts_2 is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite','Cool', null), + ut3.ut_annotation( 2, 'suitepath','path', null), + ut3.ut_annotation( 3, 'context','Level 1', null), + ut3.ut_annotation( 4, 'name','context_1', null), + ut3.ut_annotation( 5, 'context','Level 1.1', null), + ut3.ut_annotation( 6, 'name','context_1_1', null), + ut3.ut_annotation( 7, 'test', 'Test 1.1.1', 'test_1_1_1'), + ut3.ut_annotation( 8, 'test', 'Test 1.1.2', 'test_1_1_2'), + ut3.ut_annotation( 9, 'endcontext', null, null), + ut3.ut_annotation(10, 'endcontext', null, null), + ut3.ut_annotation(11, 'context','Level 2', null), + ut3.ut_annotation(12, 'name','context_2', null), + ut3.ut_annotation(13, 'test', 'Test 2.1', 'test_2_1'), + ut3.ut_annotation(14, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + ''|| + '' || + '%%' || + '' || + '%context_1Level 1path.some_package.context_1' || + '%' || + '' || + '%context_1_1Level 1.1path.some_package.context_1.context_1_1' || + '%' || + '' || + '%test_1_1_1Test 1.1.1path.some_package.context_1.context_1_1.test_1_1_1' || + '%' || + '' || + '%test_1_1_2Test 1.1.2path.some_package.context_1.context_1_1.test_1_1_2' || + '%' || + '' || + '%' || + '%' || + '' || + '%' || + '%' || + '%'|| + '' + ); + -- Test both contexts separately due to ordering + ut.expect(l_actual).to_be_like( + ''|| + '' || + '%%' || + '' || + '%context_2Level 2path.some_package.context_2' || + '%' || + '' || + '%test_2_1Test 2.1path.some_package.context_2.test_2_1' || + '%' || + '%' || + '%' || + '%' || + '%'|| + '' + ); + end; + procedure before_after_in_context is l_actual clob; @@ -791,6 +859,10 @@ create or replace package body test_suite_builder is ''|| '' || '%' || + '%' || + '%suite_level_test' || + '%%suite_level_test%' || + '%' || '%' || '%nested_context_#1A contextsome_package.nested_context_#1' || '%' || @@ -804,10 +876,6 @@ create or replace package body test_suite_builder is '%%context_beforeall%' || '%%context_afterall%' || '%' || - '%' || - '%suite_level_test' || - '%%suite_level_test%' || - '%' || '%' || '%'|| '' @@ -841,6 +909,12 @@ create or replace package body test_suite_builder is ''|| '' || '%' || + '%' || + '%suite_level_test' || + '%%suite_level_beforeeach%' || + '%%suite_level_test%' || + '%%suite_level_aftereach%' || + '%' || '%' || '%nested_context_#1nested_context_#1some_package.nested_context_#1' || '%' || @@ -852,12 +926,6 @@ create or replace package body test_suite_builder is '%' || '%' || '%' || - '%' || - '%suite_level_test' || - '%%suite_level_beforeeach%' || - '%%suite_level_test%' || - '%%suite_level_aftereach%' || - '%' || '%' || '%%suite_level_beforeall%' || '%%suite_level_afterall%' || @@ -893,18 +961,20 @@ create or replace package body test_suite_builder is ''|| '' || '%' || - '%a_contextSome contextsome_package.a_context' || - '%' || - '' || - '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' || - '%' || - '' || - '' || - '%some_packagecontext_setup' || - '%' || '%' || '%suite_level_testIn suitesome_package.suite_level_test' || '%' || + '%' || + '%a_contextSome contextsome_package.a_context' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagecontext_setup' || + '%' || + '%' || '' || '' || '%some_packagesuite_level_beforeall' || @@ -942,6 +1012,9 @@ create or replace package body test_suite_builder is ''|| '' || '%' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || '' || '%a_contextA contextsome_package.a_context' || '%' || @@ -954,9 +1027,6 @@ create or replace package body test_suite_builder is '%' || '' || '' || - '' || - '%suite_level_testIn suitesome_package.suite_level_test' || - '%' || '' || '' || '%some_packagesuite_level_beforeall' || @@ -999,32 +1069,32 @@ create or replace package body test_suite_builder is '' || '%' || '' || - '%nested_context_#2A contextsome_package.nested_context_#2' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '%a_contextA contextsome_package.a_context' || '%' || '' || - '%test_in_duplicated_contextIn duplicated contextsome_package.nested_context_#2.test_in_duplicated_context' || + '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' || '%' || '' || '' || - '%some_packagesetup_in_duplicated_context' || + '%some_packagecontext_setup' || '%' || '' || '' || '' || - '%a_contextA contextsome_package.a_context' || + '%nested_context_#2A contextsome_package.nested_context_#2' || '%' || '' || - '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' || + '%test_in_duplicated_contextIn duplicated contextsome_package.nested_context_#2.test_in_duplicated_context' || '%' || '' || '' || - '%some_packagecontext_setup' || + '%some_packagesetup_in_duplicated_context' || '%' || '' || '' || - '' || - '%suite_level_testIn suitesome_package.suite_level_test' || - '%' || '' || '' || '%some_packagesuite_level_beforeall' || @@ -1273,20 +1343,20 @@ create or replace package body test_suite_builder is '' || '%' || '' || - '%nested_context_#2A contextsome_package.nested_context_#2' || + '%nested_context_#1A contextsome_package.nested_context_#1' || '%' || '' || - '%test_in_a_context2In context2some_package.nested_context_#2.test_in_a_context2' || + '%test_in_a_context1In context1some_package.nested_context_#1.test_in_a_context1' || '%' || '' || '' || '' || '' || '' || - '%nested_context_#1A contextsome_package.nested_context_#1' || + '%nested_context_#2A contextsome_package.nested_context_#2' || '%' || '' || - '%test_in_a_context1In context1some_package.nested_context_#1.test_in_a_context1' || + '%test_in_a_context2In context2some_package.nested_context_#2.test_in_a_context2' || '%' || '' || '' || @@ -1318,24 +1388,6 @@ create or replace package body test_suite_builder is ); end; - procedure throws_value_invalid is - l_actual clob; - l_annotations ut3.ut_annotations; - begin - --Arrange - l_annotations := ut3.ut_annotations( - ut3.ut_annotation(1, 'suite','Cool', null), - ut3.ut_annotation(3, 'test','A test with invalid throws annotation', 'A_TEST_PROCEDURE'), - ut3.ut_annotation(3, 'throws',' -20145 , bad_variable_name ', 'A_TEST_PROCEDURE') - ); - --Act - l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); - --Assert - ut.expect(l_actual).to_be_like( - '%%Invalid parameter value "bad_variable_name" for "--%throws" annotation. Parameter ignored.%%' - ); - end; - procedure before_aftertest_multi is l_actual clob; diff --git a/test/ut3_tester/core/test_suite_builder.pks b/test/ut3_tester/core/test_suite_builder.pks index 7a85cb946..4cbfcad74 100644 --- a/test/ut3_tester/core/test_suite_builder.pks +++ b/test/ut3_tester/core/test_suite_builder.pks @@ -3,6 +3,7 @@ create or replace package test_suite_builder is --%suitepath(utplsql.ut3_tester.core) --%context(--%suite annotation) + --%name(suite) --%test(Sets suite name from package name and leaves description empty) procedure no_suite_description; @@ -105,6 +106,7 @@ create or replace package test_suite_builder is --%endcontext --%context(--%context annotation) + --%name(context) --%test(Creates nested suite for content between context/endcontext annotations) procedure suite_from_context; @@ -112,6 +114,9 @@ create or replace package test_suite_builder is --%test(Creates nested contexts inside a context) procedure nested_contexts; + --%test(Creates multiple nested contexts inside a context) + procedure nested_contexts_2; + --%test(Associates before/after all/each to tests in context only) procedure before_after_in_context; @@ -146,7 +151,7 @@ create or replace package test_suite_builder is --%test(Is ignored when name value is empty) procedure name_empty_value; - --%test(Is ignored when name value is empty) + --%test(Is applied to corresponding context when multiple contexts used) procedure multiple_contexts; --%endcontext @@ -156,9 +161,6 @@ create or replace package test_suite_builder is --%test(Gives warning if --%throws annotation has no value) procedure throws_value_empty; - --%test(Gives warning if --%throws annotation has invalid value) - procedure throws_value_invalid; - --%endcontext --%context(--%beforetest/aftertest annotation) diff --git a/test/ut3_tester/core/test_suite_manager.pkb b/test/ut3_tester/core/test_suite_manager.pkb index e693771b4..fd35adc70 100644 --- a/test/ut3_tester/core/test_suite_manager.pkb +++ b/test/ut3_tester/core/test_suite_manager.pkb @@ -1437,7 +1437,7 @@ end;]'; l_expected := ut3.ut_object_names( ut3.ut_object_name('UT3','SOME_TEST_PACKAGE') ); - l_actual := ut3_tester_helper.run_helper.get_object_name('UT3'); + l_actual := ut3_tester_helper.run_helper.get_schema_ut_packages('UT3'); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; @@ -1458,7 +1458,7 @@ end;]'; l_expected_message varchar2(500); begin l_expected_message := q'[ORA-20217: 'Suitepath exceeds 1000 CHAR on: UT3.DUMMY_LONG_TEST_PACKAGE,UT3.DUMMY_LONG_TEST_PACKAGE1'%]'; - l_actual := ut3_tester_helper.run_helper.get_object_name('UT3'); + l_actual := ut3_tester_helper.run_helper.get_schema_ut_packages('UT3'); ut.fail('Expected exception for suitpaths over 1k for two packages'); exception when others then diff --git a/test/ut3_tester/core/test_ut_suite.pkb b/test/ut3_tester/core/test_ut_suite.pkb index 3ea323a6d..9c52df9e5 100644 --- a/test/ut3_tester/core/test_ut_suite.pkb +++ b/test/ut3_tester/core/test_ut_suite.pkb @@ -19,11 +19,11 @@ create or replace package body test_ut_suite is l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); l_suite.path := 'ut3_tester_helper.ut_example_tests'; l_suite.disabled_flag := ut3.ut_utils.boolean_to_int(true); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); - l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_all)); l_suite.items.extend; l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number', a_line_no=> 1); l_suite.items.extend; @@ -44,9 +44,9 @@ create or replace package body test_ut_suite is l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); l_suite.path := 'ut3_tester_helper.ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_all)); l_suite.items.extend; l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0', a_line_no=> 1); --Act @@ -65,9 +65,9 @@ create or replace package body test_ut_suite is l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_EXAMPLE_TESTS', a_line_no=> 1); l_suite.path := 'ut3_tester_helper.ut_example_tests'; - l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_all)); + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_all)); l_suite.items.extend; l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0', a_line_no=> 1); diff --git a/test/ut3_tester/core/test_ut_test.pkb b/test/ut3_tester/core/test_ut_test.pkb index 2a85a323f..b4dcc2536 100644 --- a/test/ut3_tester/core/test_ut_test.pkb +++ b/test/ut3_tester/core/test_ut_test.pkb @@ -12,7 +12,7 @@ create or replace package body test_ut_test is --Arrange l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_example_tests', a_line_no=> 1); l_suite.path := 'ut3_tester_helper.ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_suite.items.extend; l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); @@ -38,11 +38,11 @@ create or replace package body test_ut_test is --Arrange l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_example_tests', a_line_no=> 1); l_suite.path := 'ut3_tester_helper.ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); - l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_before_test)); - l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_after_test)); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_after_test)); l_suite.items.extend; l_suite.items(l_suite.items.last) := l_test; l_suite.items.extend; @@ -65,10 +65,10 @@ create or replace package body test_ut_test is begin --Arrange l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_example_tests', a_line_no=> 1); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); - l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_before_each)); - l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_after_each)); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_after_each)); l_suite.items.extend; l_suite.items(l_suite.items.last) := l_test; l_suite.items.extend; @@ -90,11 +90,11 @@ create or replace package body test_ut_test is l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'ut_example_tests', a_line_no=> 1); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_example_tests', a_line_no=> 1); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_owner =>'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); - l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_before_test)); - l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); l_suite.items.extend; l_suite.items(l_suite.items.last) := l_test; l_suite.items.extend; @@ -116,15 +116,15 @@ create or replace package body test_ut_test is l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'ut_example_tests', a_line_no=> 1); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); - l_test := ut3.ut_test(a_object_owner => USER,a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); - l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_before_each)); - l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_each)); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_example_tests', a_line_no=> 1); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_test := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'failing_procedure', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_each)); l_suite.items.extend; l_suite.items(l_suite.items.last) := l_test; l_suite.items.extend; - l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => USER,a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); + l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper',a_object_name => 'ut_example_tests',a_name => 'add_1_to_g_number', a_line_no=> 1); --Act l_suite.do_execute(); --Assert @@ -148,8 +148,8 @@ create or replace package body test_ut_test is begin l_test.after_each_list := ut3.ut_executables( ut3.ut_executable( - user, - 'UT_EXAMPLE_TESTS', + 'ut3_tester_helper', + 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_each ) @@ -171,7 +171,7 @@ create or replace package body test_ut_test is ); begin l_test.after_each_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_after_each) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_after_each) ); --Act l_test.do_execute(); @@ -190,7 +190,7 @@ create or replace package body test_ut_test is ); begin l_test.after_each_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', null, ut3.ut_utils.gc_after_each) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', null, ut3.ut_utils.gc_after_each) ); --Act l_test.do_execute(); @@ -325,7 +325,7 @@ create or replace package body test_ut_test is a_line_no => null ); begin - l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(user, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_each)); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_each)); --Act l_test.do_execute(); --Assert @@ -344,7 +344,7 @@ create or replace package body test_ut_test is ); begin l_test.before_each_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_before_each) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_before_each) ); --Act l_test.do_execute(); @@ -363,7 +363,7 @@ create or replace package body test_ut_test is ); begin l_test.before_each_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', null, ut3.ut_utils.gc_before_each) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', null, ut3.ut_utils.gc_before_each) ); --Act l_test.do_execute(); @@ -529,7 +529,7 @@ create or replace package body test_ut_test is a_line_no => null ); begin - l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(user, 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_test)); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'set_g_number_0', ut3.ut_utils.gc_before_test)); --Act l_test.do_execute(); --Assert @@ -547,7 +547,7 @@ create or replace package body test_ut_test is ); begin l_test.before_test_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_before_test) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'invalid setup name', ut3.ut_utils.gc_before_test) ); --Act l_test.do_execute(); @@ -566,7 +566,7 @@ create or replace package body test_ut_test is ); begin l_test.before_test_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', null, ut3.ut_utils.gc_before_test) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', null, ut3.ut_utils.gc_before_test) ); --Act l_test.do_execute(); @@ -584,7 +584,7 @@ create or replace package body test_ut_test is a_line_no => null ); begin - l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(user, 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); --Act l_test.do_execute(); --Assert @@ -602,7 +602,7 @@ create or replace package body test_ut_test is ); begin l_test.after_test_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', 'invalid procedure name', ut3.ut_utils.gc_after_test) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', 'invalid procedure name', ut3.ut_utils.gc_after_test) ); --Act l_test.do_execute(); @@ -621,7 +621,7 @@ create or replace package body test_ut_test is ); begin l_test.after_test_list := ut3.ut_executables( - ut3.ut_executable(user, 'ut_example_tests', null, ut3.ut_utils.gc_after_test) + ut3.ut_executable('ut3_tester_helper', 'ut_example_tests', null, ut3.ut_utils.gc_after_test) ); --Act l_test.do_execute(); diff --git a/test/ut3_tester_helper/annotation_cache_helper.pkb b/test/ut3_tester_helper/annotation_cache_helper.pkb new file mode 100644 index 000000000..0d56823df --- /dev/null +++ b/test/ut3_tester_helper/annotation_cache_helper.pkb @@ -0,0 +1,167 @@ +create or replace package body annotation_cache_helper as + + procedure setup_two_suites is + pragma autonomous_transaction; + begin + execute immediate + 'create or replace package ut3_cache_test_owner.granted_test_suite is + --%suite + + --%test + procedure test1; + --%test + procedure test2; + end;'; + + execute immediate + 'create or replace package body ut3_cache_test_owner.granted_test_suite is + procedure test1 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + procedure test2 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + end;'; + execute immediate + 'create or replace package ut3_cache_test_owner.not_granted_test_suite is + --%suite + + --%test + procedure test1; + --%test + procedure test2; + end;'; + execute immediate + 'create or replace package body ut3_cache_test_owner.not_granted_test_suite is + procedure test1 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + procedure test2 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + end;'; + + execute immediate + 'grant execute on ut3_cache_test_owner.granted_test_suite to + ut3_execute_any_proc_user, ut3_select_any_table_user, ut3_select_catalog_user, ut3_no_extra_priv_user'; + end; + + procedure revoke_granted_suite is + pragma autonomous_transaction; + begin + execute immediate + 'revoke execute on ut3_cache_test_owner.granted_test_suite from + ut3_execute_any_proc_user, ut3_select_any_table_user, ut3_select_catalog_user, ut3_no_extra_priv_user'; + exception + when others then + null; + end; + + + procedure add_new_suite is + pragma autonomous_transaction; + begin + execute immediate + 'create or replace package ut3_cache_test_owner.new_suite is + --%suite + + --%test + procedure test1; + --%test + procedure test2; + end;'; + + execute immediate + 'create or replace package body ut3_cache_test_owner.new_suite is + procedure test1 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + procedure test2 is begin ut3.ut.expect( 1 ).to_equal( 1 ); end; + end;'; + execute immediate + 'grant execute on ut3_cache_test_owner.new_suite to + ut3_execute_any_proc_user, ut3_select_any_table_user, ut3_select_catalog_user, ut3_no_extra_priv_user'; + end; + + procedure cleanup_two_suites is + pragma autonomous_transaction; + begin + begin + execute immediate 'drop package ut3_cache_test_owner.not_granted_test_suite'; + exception + when others then + null; + end; + begin + execute immediate 'drop package ut3_cache_test_owner.granted_test_suite'; + exception + when others then + null; + end; + end; + + procedure cleanup_new_suite is + pragma autonomous_transaction; + begin + execute immediate 'drop package ut3_cache_test_owner.new_suite'; + exception + when others then + null; + end; + + procedure purge_annotation_cache is + begin + ut3.ut_runner.purge_cache( 'UT3_CACHE_TEST_OWNER' ); + end; + + + procedure disable_ddl_trigger is + pragma autonomous_transaction; + begin + execute immediate 'alter trigger ut3.ut_trigger_annotation_parsing disable'; + execute immediate 'begin ut3.ut_trigger_check.is_alive( ); end;'; + end; + + procedure enable_ddl_trigger is + pragma autonomous_transaction; + begin + execute immediate 'alter trigger ut3.ut_trigger_annotation_parsing enable'; + end; + + procedure create_run_function_for_user(a_user varchar2) is + pragma autonomous_transaction; + begin + execute immediate + 'create or replace function ' || a_user || '.ut_run return clob is + l_data ut3.ut_varchar2_list; + l_results clob; + begin + select * bulk collect into l_data from table (ut3.ut.run( ''ut3_cache_test_owner'' )); + return ut3_tester_helper.main_helper.table_to_clob( l_data ); + end; + '; + execute immediate 'grant execute on ' || a_user || '.ut_run to public '; + end; + + procedure drop_run_function_for_user(a_user varchar2) is + pragma autonomous_transaction; + begin + execute immediate 'drop function ' || a_user || '.ut_run'; + end; + + procedure create_run_function_for_users is + begin + create_run_function_for_user( 'ut3_no_extra_priv_user' ); + create_run_function_for_user( 'ut3_select_catalog_user' ); + create_run_function_for_user( 'ut3_select_any_table_user' ); + create_run_function_for_user( 'ut3_execute_any_proc_user' ); + create_run_function_for_user( 'ut3_cache_test_owner' ); + end; + + procedure drop_run_function_for_users is + begin + drop_run_function_for_user( 'ut3_no_extra_priv_user' ); + drop_run_function_for_user( 'ut3_select_catalog_user' ); + drop_run_function_for_user( 'ut3_select_any_table_user' ); + drop_run_function_for_user( 'ut3_execute_any_proc_user' ); + drop_run_function_for_user( 'ut3_cache_test_owner' ); + end; + + function run_tests_as(a_user varchar2) return clob is + l_results clob; + begin + execute immediate 'begin :x := '||a_user||'.ut_run; end;' using out l_results; + return l_results; + end; +end; +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/annotation_cache_helper.pks b/test/ut3_tester_helper/annotation_cache_helper.pks new file mode 100644 index 000000000..d6ab1f8e2 --- /dev/null +++ b/test/ut3_tester_helper/annotation_cache_helper.pks @@ -0,0 +1,21 @@ +create or replace package annotation_cache_helper as + + procedure setup_two_suites; + procedure add_new_suite; + procedure revoke_granted_suite; + + procedure cleanup_two_suites; + procedure cleanup_new_suite; + + procedure purge_annotation_cache; + + procedure disable_ddl_trigger; + procedure enable_ddl_trigger; + + procedure create_run_function_for_users; + procedure drop_run_function_for_users; + + function run_tests_as(a_user varchar2) return clob; + +end; +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index ef6528e04..70695977b 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -550,11 +550,11 @@ create or replace package body run_helper is begin --Arrange execute immediate 'delete from ut$test_table'; - l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_TRANSACTION_CONTROL', a_line_no=> 1); + l_suite := ut3.ut_suite(a_object_owner => 'ut3_tester_helper', a_object_name => 'UT_TRANSACTION_CONTROL', a_line_no=> 1); l_suite.path := 'ut_transaction_control'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_TRANSACTION_CONTROL', 'setup', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable('ut3_tester_helper', 'UT_TRANSACTION_CONTROL', 'setup', ut3.ut_utils.gc_before_all)); l_suite.items.extend; - l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => USER, a_object_name => 'ut_transaction_control',a_name => a_procedure_name, a_line_no=> 1); + l_suite.items(l_suite.items.last) := ut3.ut_test(a_object_owner => 'ut3_tester_helper', a_object_name => 'ut_transaction_control',a_name => a_procedure_name, a_line_no=> 1); l_suite.set_rollback_type(a_rollback_type); --Act @@ -614,7 +614,7 @@ create or replace package body run_helper is execute immediate q'[drop package ut3.some_test_package]'; end; - function get_object_name(a_owner in varchar2) return ut3.ut_object_names is + function get_schema_ut_packages(a_owner in varchar2) return ut3.ut_object_names is begin return ut3.ut_suite_manager.get_schema_ut_packages(ut3.ut_varchar2_rows(a_owner)); end; diff --git a/test/ut3_tester_helper/run_helper.pks b/test/ut3_tester_helper/run_helper.pks index 0f5e5a925..03eb314a7 100644 --- a/test/ut3_tester_helper/run_helper.pks +++ b/test/ut3_tester_helper/run_helper.pks @@ -59,7 +59,7 @@ create or replace package run_helper is procedure drop_dummy_long_test_package; procedure create_ut3_suite; procedure drop_ut3_suite; - function get_object_name(a_owner in varchar2) return ut3.ut_object_names; + function get_schema_ut_packages(a_owner in varchar2) return ut3.ut_object_names; function ut_output_buffer_tmp return t_out_buff_tab pipelined; procedure delete_buffer; diff --git a/test/ut3_user/expectations/test_expectation_anydata.pkb b/test/ut3_user/expectations/test_expectation_anydata.pkb index f94b0efc0..ece5457a7 100644 --- a/test/ut3_user/expectations/test_expectation_anydata.pkb +++ b/test/ut3_user/expectations/test_expectation_anydata.pkb @@ -864,10 +864,8 @@ Rows: [ 60 differences, showing first 20 ] l_expected_message := q'[%Actual: ut3_tester_helper.test_dummy_object_list [ count = 2 ] was expected to equal: ut3_tester_helper.test_dummy_object_list [ count = 2 ] %Diff: %Rows: [ 3 differences ] -%PK 2 - Actual: Something 2 -%PK 2 - Actual: 2 -%PK 2 - Expected: Something 1 -%PK 2 - Expected: 1 +%PK 2 - Actual: Something 22 +%PK 2 - Expected: Something 11 %PK 1 - Extra: 1Something 11 %PK 4 - Missing: 4Something 22]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); diff --git a/test/ut3_user/expectations/test_expectations_cursor.pkb b/test/ut3_user/expectations/test_expectations_cursor.pkb index 1c6f46532..5a0b68f2f 100644 --- a/test/ut3_user/expectations/test_expectations_cursor.pkb +++ b/test/ut3_user/expectations/test_expectations_cursor.pkb @@ -371,7 +371,7 @@ create or replace package body test_expectations_cursor is l_expected_message := q'[Actual: refcursor [ count = 1 ] was expected to equal: refcursor [ count = 1 ] %Diff: %Rows: [ 1 differences ] -%Row No. 1 - Actual: 4030 +%Row No. 1 - Actual: 3040 %Row No. 1 - Expected: 34]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert @@ -394,7 +394,7 @@ create or replace package body test_expectations_cursor is l_expected_message := q'[Actual: refcursor [ count = 1 ] was expected to equal: refcursor [ count = 1 ] %Diff: %Rows: [ 1 differences ] -%Row No. 1 - Actual: 4030 +%Row No. 1 - Actual: 3040 %Row No. 1 - Expected: 34]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert @@ -860,9 +860,9 @@ Columns: Rows: [ 4 differences ] Row No. 1 - Actual: 25000 Row No. 1 - Expected: 10000 - Row No. 2 - Actual: TONYSTARK3100000 + Row No. 2 - Actual: 3TONYSTARK100000 Row No. 2 - Expected: 2LUKESKYWALKER1000 - Row No. 3 - Actual: JESSICAJONES42345 + Row No. 3 - Actual: 4JESSICAJONES2345 Row No. 3 - Expected: 3TONYSTARK100000 Row No. 4 - Extra: MLUKESKYWALKER21000]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); @@ -896,9 +896,9 @@ Columns: Rows: [ 4 differences ] Row No. 1 - Actual: 25000 Row No. 1 - Expected: 10000 - Row No. 2 - Actual: TONYSTARK3100000 + Row No. 2 - Actual: 3TONYSTARK100000 Row No. 2 - Expected: 2LUKESKYWALKER1000 - Row No. 3 - Actual: JESSICAJONES42345 + Row No. 3 - Actual: 4JESSICAJONES2345 Row No. 3 - Expected: 3TONYSTARK100000 Row No. 4 - Extra: MLUKESKYWALKER21000]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); @@ -1237,10 +1237,8 @@ Rows: [ 4 differences ] l_expected_message := q'[Actual: refcursor [ count = 1 ] was expected to equal: refcursor [ count = 1 ] %Diff: %Rows: [ 1 differences ] -%PK 1 - Actual: 30 -%PK 1 - Expected: 3 -%PK 1 - Actual: 40 -%PK 1 - Expected: 4]'; +%PK 1 - Actual: 3040 +%PK 1 - Expected: 34]'; l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); @@ -2511,7 +2509,7 @@ Diff:% begin l_exp_message :='ORA-20218: SQL exception thrown when fetching data from cursor: ORA-01476: divisor is equal to zero -at "UT3$USER#.TEST_EXPECTATIONS_CURSOR%", line 2522 ut3.ut.expect(l_actual).to_equal(l_expected);% +at "UT3$USER#.TEST_EXPECTATIONS_CURSOR%", line % ut3.ut.expect(l_actual).to_equal(l_expected);% Check the query and data for errors.'; open l_actual for @@ -2536,7 +2534,7 @@ Check the query and data for errors.'; l_exp_message :='ORA-20218: SQL exception thrown when fetching data from cursor: ORA-01476: divisor is equal to zero -at "UT3$USER#.TEST_EXPECTATIONS_CURSOR%", line 2547 ut3.ut.expect(l_actual).to_equal(l_expected);% +at "UT3$USER#.TEST_EXPECTATIONS_CURSOR%", line % ut3.ut.expect(l_actual).to_equal(l_expected);% Check the query and data for errors.'; open l_expected for @@ -2639,7 +2637,7 @@ Check the query and data for errors.'; end; - procedure insginificant_whitespace1 is + procedure space_only_vs_empty is l_actual sys_refcursor; l_expected sys_refcursor; begin @@ -2650,79 +2648,65 @@ Check the query and data for errors.'; select column_value t1 from table(ut_varchar2_list(' ')); --Assert ut3.ut.expect( l_actual ).to_equal( l_expected ); - ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); end; - procedure insginificant_whitespace2 is + procedure tab_only_vs_empty is l_actual sys_refcursor; l_expected sys_refcursor; begin open l_expected for - select ' t ' t1 from dual; - + select column_value t1 from table(ut_varchar2_list('')); + open l_actual for - select 't' t1 from dual; + select column_value t1 from table(ut_varchar2_list(chr(9))); --Assert ut3.ut.expect( l_actual ).to_equal( l_expected ); - ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); - end; - - procedure insginificant_whitespace3 is - l_actual sys_refcursor; - l_expected sys_refcursor; - begin - open l_expected for - select 't ' t1 from dual; - - open l_actual for - select 't' t1 from dual; - --Assert - ut3.ut.expect( l_actual ).to_equal( l_expected ); - ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); end; - - procedure insginificant_whitespace4 is + + procedure insignificant_start_end_space is l_actual sys_refcursor; l_expected sys_refcursor; begin open l_expected for - select ' t' t1 from dual; + select ' t ' t1 from dual; open l_actual for select 't' t1 from dual; --Assert ut3.ut.expect( l_actual ).to_equal( l_expected ); - ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); - end; + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); + end; - procedure insginificant_whitespace5 is + procedure double_vs_single_start_end_ws is l_actual sys_refcursor; l_expected sys_refcursor; begin open l_expected for - select ' ' t1 from dual; + select ' t ' t1 from dual; open l_actual for - select '' t1 from dual; + select ' t ' t1 from dual; --Assert ut3.ut.expect( l_actual ).to_equal( l_expected ); ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); - end; + end; - procedure nulltowhitespace is + procedure leading_tab_vs_space is l_actual sys_refcursor; l_expected sys_refcursor; begin open l_expected for - select cast(null as varchar2(2)) t1 from dual; + select ' t' t1 from dual; open l_actual for - select ' ' t1 from dual; + select chr(9)||'t' t1 from dual; --Assert ut3.ut.expect( l_actual ).to_equal( l_expected ); ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); - end; - + end; + procedure number_from_dual is l_actual sys_refcursor; l_expected sys_refcursor; diff --git a/test/ut3_user/expectations/test_expectations_cursor.pks b/test/ut3_user/expectations/test_expectations_cursor.pks index d3dbeb9a7..0ed249a49 100644 --- a/test/ut3_user/expectations/test_expectations_cursor.pks +++ b/test/ut3_user/expectations/test_expectations_cursor.pks @@ -417,31 +417,27 @@ create or replace package test_expectations_cursor is --%test(Check that column name accept non xml characters fix #902) procedure nonxmlchar_part_of_colname; - - - /*Oracle Bug not readin properly in xmltable */ - --%test ( Compare insiginificant whitespaces scenario 1 ) + + + /*Oracle Bug not reading properly in XMLTable - See - Issue #880 */ + --%disabled + --%test ( Space-only string is not equal empty string ) + procedure space_only_vs_empty; + + /*Oracle Bug not reading properly in XMLTable - See - Issue #880 */ --%disabled - procedure insginificant_whitespace1; + --%test ( Tab-only string is not equal empty string ) + procedure tab_only_vs_empty; - --%test ( Compare insiginificant whitespaces scenario 2 ) - procedure insginificant_whitespace2; + --%test ( Insignificant start/end whitespaces are considered ) + procedure insignificant_start_end_space; - --%test ( Compare insiginificant whitespaces scenario 3 ) - procedure insginificant_whitespace3; + --%test ( Double and single leading/trailing space is distinguished ) + procedure double_vs_single_start_end_ws; - --%test ( Compare insiginificant whitespaces scenario 4 ) - procedure insginificant_whitespace4; + --%test ( Leading Tab vs. Space is distinguished ) + procedure leading_tab_vs_space; - /*Oracle Bug not readin properly in xmltable */ - --%test ( Compare insiginificant whitespaces scenario 5 ) - --%disabled - procedure insginificant_whitespace5; - - /*Oracle Bug not readin properly in xmltable */ - --%test ( Compare null to whitespace ) - --%disabled - procedure nulltowhitespace; --%test(Check precision of number from dual #907) procedure number_from_dual; diff --git a/test/ut3_user/reporters/test_teamcity_reporter.pkb b/test/ut3_user/reporters/test_teamcity_reporter.pkb index 71d2f857d..a5261d7f9 100644 --- a/test/ut3_user/reporters/test_teamcity_reporter.pkb +++ b/test/ut3_user/reporters/test_teamcity_reporter.pkb @@ -63,8 +63,8 @@ create or replace package body test_teamcity_reporter as -%##teamcity[testStdErr timestamp='%' name='ut3$user#.test_reporters.erroring_test' out='Test exception:|nORA-06512: at "UT3$USER#.TEST_REPORTERS", line %|nORA-06512: at %|n|n'] -%##teamcity[testFailed timestamp='%' details='Test exception:|nORA-06512: at "UT3$USER#.TEST_REPORTERS", line %|nORA-06512: at %|n|n' message='Error occured' name='ut3$user#.test_reporters.erroring_test'] +%##teamcity[testStdErr timestamp='%' name='ut3$user#.test_reporters.erroring_test' out='Test exception:|nORA-06502: PL/SQL: numeric or value error: character to number conversion error|nORA-06512: at "UT3$USER#.TEST_REPORTERS", line %|nORA-06512: at %|n'] +%##teamcity[testFailed timestamp='%' details='Test exception:|nORA-06502: PL/SQL: numeric or value error: character to number conversion error|nORA-06512: at "UT3$USER#.TEST_REPORTERS", line %|nORA-06512: at %|n' message='Error occured' name='ut3$user#.test_reporters.erroring_test'] %##teamcity[testFinished timestamp='%' duration='%' name='ut3$user#.test_reporters.erroring_test'] %##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.test_reporters.disabled_test'] %##teamcity[testIgnored timestamp='%' name='ut3$user#.test_reporters.disabled_test']