diff --git a/.gitattributes b/.gitattributes index 9cf0f2457..2cda5ad55 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,16 +1,18 @@ +.github export-ignore +.idea export-ignore +development export-ignore +test export-ignore .gitattributes export-ignore .gitignore export-ignore .gitmodules export-ignore -.travis.yml export-ignore mkdocs.yml export-ignore -.travis export-ignore +BUILD_NO export-ignore +mkdocs_offline.yml export-ignore sonar-project.properties export-ignore -tests export-ignore -development export-ignore -node_modules export-ignore -^docs/* linguist-documentation +^docs/** linguist-documentation *.pkb linguist-language=PLSQL *.pks linguist-language=PLSQL *.sql linguist-language=PLSQL *.tpb linguist-language=PLSQL *.tps linguist-language=PLSQL +*.sh text eol=lf diff --git a/.github/DISCUSSION_TEMPLATE/bug-triage.yml b/.github/DISCUSSION_TEMPLATE/bug-triage.yml new file mode 100644 index 000000000..7a5da8d73 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/bug-triage.yml @@ -0,0 +1,69 @@ +title: "[Bug?] " +labels: ["bug-triage"] +body: + - type: markdown + attributes: + value: | + Use this category if you think you have found a bug in the utPLSQL framework and want to discuss it before a formal issue is opened. + + Once a maintainer confirms the bug, they will open an issue and link it back here. You do not need to open an issue yourself. + + If you are certain it is a bug and can provide a reliable reproduction, you may open an issue directly instead. + + - type: input + id: utplsql-version + attributes: + label: utPLSQL version + placeholder: "e.g. 3.1.13" + validations: + required: true + + - type: input + id: oracle-version + attributes: + label: Oracle Database version + placeholder: "e.g. 19c, 21c" + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + placeholder: "e.g. Linux, Windows Server 2019" + validations: + required: false + + - type: textarea + id: description + attributes: + label: What did you observe? + description: Describe the unexpected behaviour. Include any error messages or stack traces. + render: text + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect? + description: Describe what you expected to happen instead. + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Reproduction case + description: Paste the minimal PL/SQL needed to reproduce the problem. The simpler, the better. + render: sql + validations: + required: false + + - type: textarea + id: investigation + attributes: + label: What have you investigated so far? + description: Any findings from your own debugging that might help narrow this down. + validations: + required: false diff --git a/.github/DISCUSSION_TEMPLATE/contributing.yml b/.github/DISCUSSION_TEMPLATE/contributing.yml new file mode 100644 index 000000000..31a213cbb --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/contributing.yml @@ -0,0 +1,43 @@ +title: "" +labels: ["contributing"] +body: + - type: markdown + attributes: + value: | + Use this category if you are working on or considering a contribution to the utPLSQL framework codebase and have questions about the internals, development setup, testing approach, or contribution process. + + Before posting, it is worth checking: + - [CONTRIBUTING.md](https://github.com/utPLSQL/utPLSQL/blob/develop/CONTRIBUTING.md) + - [Development setup guide](https://github.com/utPLSQL/utPLSQL/blob/develop/development/README.md) + + - type: dropdown + id: topic + attributes: + label: Topic + options: + - Development environment setup + - Understanding the codebase + - Test writing and running locally + - CI / build pipeline + - Pull request process + - Architectural question + - Other + validations: + required: true + + - type: textarea + id: question + attributes: + label: Question or topic + description: Describe what you are working on and what you need help with. + validations: + required: true + + - type: textarea + id: context + attributes: + label: Relevant code or context + description: Paste any relevant code, configuration, or error output here. + render: sql + validations: + required: false diff --git a/.github/DISCUSSION_TEMPLATE/feature-requests.yml b/.github/DISCUSSION_TEMPLATE/feature-requests.yml new file mode 100644 index 000000000..b07a043b5 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/feature-requests.yml @@ -0,0 +1,61 @@ +title: "[Feature] " +labels: ["feature-request"] +body: + - type: markdown + attributes: + value: | + Use this category for feature ideas specific to the utPLSQL framework: new matchers, annotation behaviour, reporter output, suite control, coverage, or installation. + + > For ideas that affect multiple components or the project direction overall, use [org-level Feature Requests](https://github.com/orgs/utPLSQL/discussions/categories/feature-requests) instead. + + A maintainer will mark this discussion as answered once the proposal is accepted or declined. + If accepted, a maintainer will convert it to an issue for implementation tracking -- you do not need to open an issue yourself. + + - type: textarea + id: problem + attributes: + label: Problem or motivation + description: What problem does this feature solve? What is the current limitation or pain point? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed solution + description: Describe the feature you have in mind. Include examples of how it would be used, ideally with PL/SQL annotation or API examples. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives or workarounds considered + description: Have you found any alternative approaches or partial workarounds? + validations: + required: false + + - type: dropdown + id: area + attributes: + label: Framework area + options: + - Annotations + - Matchers + - Reporters + - Suite / test control + - Coverage + - Transaction control + - Installation / upgrade + - Performance + - Other + validations: + required: true + + - type: input + id: utplsql-version + attributes: + label: utPLSQL version you are using + placeholder: "e.g. 3.1.13" + validations: + required: false diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml new file mode 100644 index 000000000..30b372760 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/q-a.yml @@ -0,0 +1,61 @@ +title: "[Q&A] " +labels: ["question"] +body: + - type: markdown + attributes: + value: | + ## Question & Answer + Ask anything about **using** utPLSQL — installation, writing tests, CI integration, + reporters, annotations, code coverage, etc. + + **Tips for a fast answer:** + - Include your utPLSQL version and Oracle version. + - Paste a minimal reproducer (test package + expected vs actual output). + - Check the [documentation](https://utplsql.github.io/utPLSQL/) and + [closed Q&A discussions](../../discussions/categories/q-a?discussions_q=is%3Aclosed) first. + + > If this turns out to be a **bug**, a maintainer will convert this discussion into an Issue. + + - type: input + id: utplsql-version + attributes: + label: utPLSQL version + placeholder: "e.g. 3.1.13" + validations: + required: true + + - type: input + id: oracle-version + attributes: + label: Oracle Database version + placeholder: "e.g. 19c, 21c" + validations: + required: true + + - type: textarea + id: question + attributes: + label: Question + description: Describe what you are trying to do and what you have already tried. + validations: + required: true + + - type: textarea + id: reproducer + attributes: + label: Minimal reproducer + description: | + Paste a minimal test package and/or the relevant ut_runner call. + Include the full error message or unexpected output if applicable. + render: sql + validations: + required: false + + - type: textarea + id: output + attributes: + label: Actual output or error + description: Paste any error messages or unexpected output here. + render: text + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4cd72f51c..f0335781e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -23,4 +23,4 @@ Add any other context or screenshots about the feature request here. > Please do not create issues for generic SQL or PL/SQL questions. There are other forums and communities to help you with those. See [ASKTom](https://asktom.oracle.com) for example. **Want to discuss** -If you want to discuss your issue, join [our SLACK chat](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA). +If you want to discuss a problem, please go to [utPLSQL discussions](https://github.com/utPLSQL/utPLSQL/discussions) diff --git a/.github/scripts/create_test_users.sh b/.github/scripts/create_test_users.sh new file mode 100755 index 000000000..6c3030206 --- /dev/null +++ b/.github/scripts/create_test_users.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +set -ev +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR}/../../source + +time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL +set feedback off +whenever sqlerror exit failure rollback + +-------------------------------------------------------------------------------- +PROMPT Adding back create-trigger privilege to $UT3_DEVELOP_SCHEMA for testing +grant administer database trigger to $UT3_DEVELOP_SCHEMA; + +-------------------------------------------------------------------------------- +PROMPT Creating UT3_TESTER - Power-user for testing internal framework code + +create user UT3_TESTER identified by "ut3" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; +grant create session, create procedure, create type, create table to UT3_TESTER; + +# Editioning is enable on test users to validate that utPLSQL works well with editioned schema +alter user UT3_TESTER enable editions; + +grant execute on dbms_lock to UT3_TESTER; + +PROMPT Granting $UT3_DEVELOP_SCHEMA code to UT3_TESTER + +begin + for i in ( + select object_name from all_objects t + where t.object_type in ('PACKAGE','TYPE') + and owner = '$UT3_DEVELOP_SCHEMA' + and generated = 'N' + and object_name not like 'SYS%') + loop + execute immediate 'grant execute on $UT3_DEVELOP_SCHEMA."'||i.object_name||'" to UT3_TESTER'; + end loop; +end; +/ + +PROMPT Granting $UT3_DEVELOP_SCHEMA tables to UT3_TESTER + +begin + for i in ( select table_name from all_tables t where owner = '$UT3_DEVELOP_SCHEMA' and nested = 'NO' and iot_name is null) + loop + execute immediate 'grant select on $UT3_DEVELOP_SCHEMA.'||i.table_name||' to UT3_TESTER'; + end loop; +end; +/ + + +-------------------------------------------------------------------------------- +PROMPT Creating UT3_USER - minimal privileges user for API testing + +create user UT3_USER identified by "ut3" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; +grant create session, create procedure, create type, create table to UT3_USER; + +# Editioning is enable on test users to validate that utPLSQL works well with editioned schema +alter user UT3_USER enable editions; + +PROMPT Grants for starting a debugging session from UT3_USER +grant debug connect session to UT3_USER; +grant debug any procedure to UT3_USER; +begin + \$if dbms_db_version.version <= 11 \$then + null; -- no addition action necessary + \$else + -- necessary on 12c or higher + dbms_network_acl_admin.append_host_ace ( + host =>'*', + ace => sys.xs\$ace_type( + privilege_list => sys.xs\$name_list('JDWP') , + principal_name => 'UT3_USER', + principal_type => sys.xs_acl.ptype_db + ) + ); + \$end +end; +/ + +-------------------------------------------------------------------------------- +PROMPT Creating UT3_TESTER_HELPER - provides functions to allow min grant test user setup tests. + +create user UT3_TESTER_HELPER identified by "ut3" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; +grant create session, create procedure, create type, create table to UT3_TESTER_HELPER; + +PROMPT Grants for testing distributed transactions +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_DEVELOP_SCHEMA 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, + grant any object privilege, grant any privilege, create public synonym, drop public synonym, create any trigger, drop any trigger + 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/.github/scripts/get_project_version.sh b/.github/scripts/get_project_version.sh new file mode 100755 index 000000000..54a30a562 --- /dev/null +++ b/.github/scripts/get_project_version.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +version=`cat VERSION` +#When on develop branch, add "-develop" to the version text +if [[ "${CI_ACTION_REF_NAME}" == "develop" ]]; then + version=`sed -E "s/(v?[0-9]+\.[0-9]+\.[0-9]+).*/\1-develop/" <<< "${version}"` +fi +echo ${version} diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh new file mode 100755 index 000000000..5f034951c --- /dev/null +++ b/.github/scripts/install.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -ev +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR}/../../source + +INSTALL_FILE="install_headless_with_trigger.sql" +if [[ ! -f "${INSTALL_FILE}" ]]; then + INSTALL_FILE="install_headless.sql" +fi + +#install core of utplsql +time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL +whenever sqlerror exit failure rollback +set feedback off +set verify off + +--alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6003,6009,6010,7206)'; +alter session set plsql_optimize_level=0; +@${INSTALL_FILE} $UT3_DEVELOP_SCHEMA $UT3_DEVELOP_SCHEMA_PASSWORD +SQL + +#Enable editioning on the UT3_DEVELOP schema +time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL +whenever sqlerror exit failure rollback +set feedback off +set verify off + +alter user $UT3_DEVELOP_SCHEMA enable editions; +exit +SQL diff --git a/.travis/install_utplsql_release.sh b/.github/scripts/install_utplsql_release.sh similarity index 92% rename from .travis/install_utplsql_release.sh rename to .github/scripts/install_utplsql_release.sh index 12ec50118..e50718c7b 100755 --- a/.travis/install_utplsql_release.sh +++ b/.github/scripts/install_utplsql_release.sh @@ -1,8 +1,8 @@ #!/bin/bash set -ev - -cd $UTPLSQL_DIR/source +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR}/../../${UTPLSQL_DIR}/source "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA < [#1320](https://github.com/utPLSQL/utPLSQL/pull/1320) +# 2. Convert the auto-generated "Full Changelog" bare URL to a labelled link +# e.g. **Full Changelog**: https://.../compare/v3.2.01...v3.2.2 +# -> **Full Changelog**: [v3.2.01...v3.2.2](https://.../compare/v3.2.01...v3.2.2) +FORMATTED_BODY=$(echo "${RELEASE_BODY}" \ + | sed -E 's@https://github\.com/([^/]+)/([^/]+)/(pull|issues)/([0-9]+)@[#\4](https://github.com/\1/\2/\3/\4)@g' \ + | sed -E 's@\*\*Full Changelog\*\*: (https://[^ ]+/compare/([^ ]+))@\*\*Full Changelog\*\*: [\2](\1)@g') + +# Create the post file +cat > "${POST_PATH}/${POST_FILENAME}" <>index.md + echo "layout: default" >>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 + echo "" >>index.md #- 8th line - placeholder for develop branch doc + echo "" >>index.md + echo "## Released Version Doc History" >>index.md + echo "" >>index.md +fi +#If build running on a TAG - it's a new release - need to add it to documentation +if [ "${GITHUB_REF_TYPE}" == "tag" ]; then + sed -i '7s@.*@'" - [Latest ${CI_ACTION_REF_NAME} documentation](latest/) - Created $now"'@' index.md + #add entry to the top of version history (line end of file - ## Released Version Doc History + sed -i '12i'" - [${CI_ACTION_REF_NAME} documentation](${UTPLSQL_VERSION}/) - Created $now" index.md +fi +#replace 4th line in log +sed -i '8s@.*@'" - [Latest development version](develop/) - Created $now"'@' index.md +#Add and Commit the changes back to pages repo. +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:${GITHUB_IO_BRANCH} + diff --git a/.travis/run_examples.sh b/.github/scripts/run_examples.sh similarity index 55% rename from .travis/run_examples.sh rename to .github/scripts/run_examples.sh index 935037245..bfa4f7e1a 100755 --- a/.travis/run_examples.sh +++ b/.github/scripts/run_examples.sh @@ -1,8 +1,8 @@ #!/bin/bash -cd "$(dirname "$(readlink -f "$0")")"/../examples - set -ev +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR}/../../examples "$SQLCLI" $UT3_DEVELOP_SCHEMA/$UT3_DEVELOP_SCHEMA_PASSWORD@//$CONNECTION_STR <> $GITHUB_ENV +echo "UTPLSQL_VERSION=${version}" >> $GITHUB_ENV +echo UTPLSQL_BUILD_VERSION=$(echo ${version} | sed -E "s/(v?[0-9]+\.)([0-9]+\.)([0-9]+)(-.*)?/\1\2\3\.${build_no}\4/") >> $GITHUB_ENV + diff --git a/.github/scripts/set_version_numbers_env.sh b/.github/scripts/set_version_numbers_env.sh new file mode 100755 index 000000000..c61639bf9 --- /dev/null +++ b/.github/scripts/set_version_numbers_env.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +build_no=$( expr ${GITHUB_RUN_NUMBER} + ${UTPLSQL_BUILD_NO_OFFSET} ) +version=$(.github/scripts/get_project_version.sh) + +echo "UTPLSQL_BUILD_NO=${build_no}" >> $GITHUB_ENV +echo "UTPLSQL_VERSION=${version}" >> $GITHUB_ENV +echo UTPLSQL_BUILD_VERSION=$(echo ${version} | sed -E "s/(v?[0-9]+\.)([0-9]+\.)([0-9]+)(-.*)?/\1\2\3\.${build_no}\4/") >> $GITHUB_ENV diff --git a/.github/scripts/uninstall_validate_utplsql.sh b/.github/scripts/uninstall_validate_utplsql.sh new file mode 100755 index 000000000..d0a2070a8 --- /dev/null +++ b/.github/scripts/uninstall_validate_utplsql.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -ev +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR}/../../source + +time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL + set feedback off + set verify off + whenever sqlerror exit failure rollback + + @uninstall_all.sql $UT3_DEVELOP_SCHEMA +SQL +time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL + set feedback off + set verify off + whenever sqlerror exit failure rollback + set serverout on + begin + for i in ( + select o.object_type||' '||o.owner||'.'||o.object_name as obj + from dba_objects o + where owner = '$UT3_DEVELOP_SCHEMA' + union all + select 'SYNONYM '||s.owner||'.'||s.synonym_name||' FOR '||s.table_owner||'.'||s.table_name as obj + from dba_synonyms s + where table_owner = '$UT3_DEVELOP_SCHEMA' + ) loop + dbms_output.put_line(i.obj); + end loop; + end; + / + declare + v_leftover_objects_count integer; + begin + select sum(cnt) + into v_leftover_objects_count + from ( + select count(1) cnt from dba_objects + where owner = '$UT3_DEVELOP_SCHEMA' + and object_name not like 'PLSQL_PROFILER%' and object_name not like 'DBMSPCC_%' + union all + select count(1) cnt from dba_synonyms + where table_owner = '$UT3_DEVELOP_SCHEMA' + and table_name not like 'PLSQL_PROFILER%' and table_name not like 'DBMSPCC_%' + ); + if v_leftover_objects_count > 0 then + raise_application_error(-20000, 'Not all objects were successfully uninstalled - leftover objects count='||v_leftover_objects_count); + end if; + end; + / + drop user $UT3_DEVELOP_SCHEMA; +SQL diff --git a/.github/scripts/update_changelog.sh b/.github/scripts/update_changelog.sh new file mode 100755 index 000000000..011c08819 --- /dev/null +++ b/.github/scripts/update_changelog.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Updates CHANGELOG.md by prepending a new section for the release. +# +# Required environment variables: +# RELEASE_TAG - e.g. v3.2.3 +# RELEASE_BODY - markdown release notes +# RELEASE_DATE - ISO-8601 date string (only the first 10 chars are used) +# TARGET_BRANCH - branch to push the updated changelog to + +set -euo pipefail + +python3 - <<'PYEOF' +import os + +tag = os.environ['RELEASE_TAG'] +body = os.environ['RELEASE_BODY'].strip() +date = os.environ['RELEASE_DATE'][:10] + +new_section = f"## [{tag}] - {date}\n\n{body}\n\n---\n\n" + +with open('CHANGELOG.md', 'r') as f: + content = f.read() + +marker = '\n---\n' +pos = content.index(marker) + len(marker) +updated = content[:pos] + '\n' + new_section + content[pos:] + +with open('CHANGELOG.md', 'w') as f: + f.write(updated) + +print(f"Added {tag} to CHANGELOG.md") +PYEOF + +git add CHANGELOG.md +git commit -m "Add release notes for ${RELEASE_TAG} to CHANGELOG.md [skip ci]" +git push origin HEAD:"${TARGET_BRANCH}" diff --git a/.travis/update_project_version.sh b/.github/scripts/update_project_version.sh similarity index 74% rename from .travis/update_project_version.sh rename to .github/scripts/update_project_version.sh index c4540eef8..66e45125a 100755 --- a/.travis/update_project_version.sh +++ b/.github/scripts/update_project_version.sh @@ -2,10 +2,10 @@ UTPLSQL_VERSION_PATTERN="v?([0-9]+\.){3}[0-9]+[^']*" -echo Current branch is "${CURRENT_BRANCH}" +echo Current branch is "${CI_ACTION_REF_NAME}" echo Update version in project source files -find ${UTPLSQL_SOURCES_DIR} -type f -name '*' -exec sed -i -r "s/${UTPLSQL_VERSION_PATTERN}/${UTPLSQL_BUILD_VERSION}/" {} \; +find source -type f -name '*' -exec sed -i -r "s/${UTPLSQL_VERSION_PATTERN}/${UTPLSQL_BUILD_VERSION}/" {} \; echo Source files updated with version tag: ${UTPLSQL_BUILD_VERSION} echo Update version in documentation files @@ -16,4 +16,5 @@ sed -i -r "s/(sonar\.projectVersion=).*?/\1${UTPLSQL_VERSION}/" sonar-project.pr echo Update VERSION file echo ${UTPLSQL_VERSION} > VERSION +echo ${UTPLSQL_BUILD_NO} > BUILD_NO diff --git a/.travis/validate_report_files.sh b/.github/scripts/validate_report_files.sh similarity index 90% rename from .travis/validate_report_files.sh rename to .github/scripts/validate_report_files.sh index 621297a0b..722a9220a 100755 --- a/.travis/validate_report_files.sh +++ b/.github/scripts/validate_report_files.sh @@ -1,11 +1,13 @@ #!/usr/bin/env bash +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + GL_VALID=1 -XSD_DIR="$TRAVIS_BUILD_DIR/.travis/xsd" -XML_JAR_DIR="$TRAVIS_BUILD_DIR/.travis/lib" +XSD_DIR="${SCRIPT_DIR}/xsd" +XML_JAR_DIR="${SCRIPT_DIR}/lib" #XML Validator XML_VALIDATOR="$XML_JAR_DIR/xml_validator.jar" -HTML_VALIDATOR_URL="https://validator.w3.org/nu/" +HTML_VALIDATOR_URL="http://localhost:8888/" HTML_FILENAME="coverage.html" declare -A XML_FILES diff --git a/.travis/xsd/junit4.xsd b/.github/scripts/xsd/junit4.xsd similarity index 100% rename from .travis/xsd/junit4.xsd rename to .github/scripts/xsd/junit4.xsd diff --git a/.travis/xsd/junit_windy.xsd b/.github/scripts/xsd/junit_windy.xsd similarity index 88% rename from .travis/xsd/junit_windy.xsd rename to .github/scripts/xsd/junit_windy.xsd index fe134662a..320779edd 100644 --- a/.travis/xsd/junit_windy.xsd +++ b/.github/scripts/xsd/junit_windy.xsd @@ -1,6 +1,6 @@ - - @@ -71,10 +71,11 @@ Permission to waive conditions of this license may be requested from Windy Road - - - Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace - + + + + Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace + @@ -93,9 +94,9 @@ Permission to waive conditions of this license may be requested from Windy Road - - Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace - + + Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace + @@ -192,6 +193,11 @@ Permission to waive conditions of this license may be requested from Windy Road The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. + + + The total number of ignored or skipped tests in the suite. + + Time taken (in seconds) to execute the tests in the suite @@ -203,4 +209,4 @@ Permission to waive conditions of this license may be requested from Windy Road - + \ No newline at end of file diff --git a/.github/variables/.env b/.github/variables/.env new file mode 100644 index 000000000..7bb5003e8 --- /dev/null +++ b/.github/variables/.env @@ -0,0 +1,27 @@ +#utPLSQL install env +UT3_DEVELOP_SCHEMA=UT3_DEVELOP +UT3_DEVELOP_SCHEMA_PASSWORD=ut3 +UT3_RELEASE_VERSION_SCHEMA=UT3 +UT3_RELEASE_VERSION_SCHEMA_PASSWORD=ut3 +UT3_TABLESPACE=users + +# Database Env +SQLCLI="sqlplus" +OJDBC_HOME=ojdbc +OJDBC_URL="https://download.oracle.com/otn-pub/otn_software/jdbc/213" +ORACLE_PWD="oracle" +TZ="Europe/London" + +#Build env +UTPLSQL_CLI_VERSION="3.1.8" +UTPLSQL_DIR="utPLSQL_latest_release" +UTPLSQL_BUILD_NO_OFFSET=3563 + +#Git configuration +GIT_AUTHOR_NAME="github-actions[bot]" +GIT_AUTHOR_EMAIL="github-actions[bot]@users.noreply.github.com" +GIT_COMMITTER_NAME="github-actions[bot]" +GIT_COMMITTER_EMAIL="github-actions[bot]@users.noreply.github.com" + +#Docker environment for running utPLSQL install commands +DOCKER_ENV="-e SQLCLI=sqlplus -e UT3_DEVELOP_SCHEMA -e UT3_DEVELOP_SCHEMA_PASSWORD -e UT3_RELEASE_VERSION_SCHEMA -e UT3_RELEASE_VERSION_SCHEMA_PASSWORD -e UT3_TABLESPACE -e ORACLE_PWD -e CONNECTION_STR -e UTPLSQL_DIR" \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..3ca9f34d2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,299 @@ +name: Build, test, deploy documentation +on: + push: + branches-ignore: [ main ] + pull_request: + branches: [ develop ] + schedule: + - cron: '0 8 * * 5' # run scheduled at 8:00 AM every Friday + timezone: "Europe/Nicosia" + + workflow_dispatch: + +concurrency: ${{github.ref}} +defaults: + run: + shell: bash + +jobs: + + build: + name: Build and test on ${{matrix.db_version_name}} DB + runs-on: ubuntu-latest + timeout-minutes: 90 + env: + ORACLE_VERSION: ${{matrix.oracle-version}} + ORACLE_SID: ${{matrix.oracle-sid}} + CONNECTION_STR: ${{ format( '127.0.0.1:1521/{0}', matrix.oracle-sid ) }} + ORACLE_PASSWORD: oracle + DOCKER_VOLUME: ${{matrix.docker-volume}} + strategy: + fail-fast: false + matrix: + include: + - id: 1 + db_version_name: '19se' + oracle-sid: 'ORCLCDB' + oracle-version: "utplsqlv3/oracledb:19c-se2-small" + check-coding-style: true + validate-utplsql-uninstall: true + - id: 2 + db_version_name: '21XE' + oracle-sid: 'XE' + oracle-version: "gvenzl/oracle-xe:21-slim-faststart" + - id: 3 + db_version_name: '23-free' + oracle-sid: 'FREEPDB1' + oracle-version: "gvenzl/oracle-free:23-slim-faststart" + fips-enabled: true + run-sonar-scanner: true + services: + html_checker: + image: ghcr.io/validator/validator:latest + options: >- + -p 8888:8888 + oracle: + image: ${{matrix.oracle-version}} + env: + ORACLE_PASSWORD: oracle + credentials: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + volumes: + - ${{github.workspace}}:/utPLSQL + ports: + - 1521:1521 + options: >- + --health-interval 10s + --health-timeout 5s + --health-retries 10 + --name oracle + --health-cmd healthcheck.sh + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: c-py/action-dotenv-to-setenv@v5 + with: + env-file: .github/variables/.env + - uses: FranzDiebold/github-env-vars-action@v2.8.0 #https://github.com/marketplace/actions/github-environment-variables-action + + - name: Set build version number env variables + id: set-build-version-number-vars + run: .github/scripts/set_version_numbers_env.sh + + - name: Update project version & build number to verify that code is deployable after the update + id: update-project-version + run: .github/scripts/update_project_version.sh + + - name: Download utPLSQL release for testing +# For PR build - test using target branch as framework, for branch build use self as testing framework + run: git clone --depth=1 --branch=${CI_BASE_REF:-$CI_REF_NAME} https://github.com/utPLSQL/utPLSQL.git $UTPLSQL_DIR + + - name: Enable FIPS 140 on Oracle DB + id: enable-fips + if: ${{ matrix.fips-enabled == true }} + run: | + docker exec oracle bash -c " + echo \"ALTER SYSTEM SET DBFIPS_140=TRUE SCOPE=SPFILE; + SHUTDOWN IMMEDIATE; + STARTUP; + PROMPT Should show true for DBFIPS_140 + SELECT name, value FROM v$parameter WHERE name = 'DBFIPS_140'; + EXIT;\" | sqlplus -s / as sysdba + " + docker exec oracle bash -c "until healthcheck.sh; do echo 'Waiting for DB restart...'; sleep 1; done" + - name: Update privileges on sources + run: chmod -R go+w ./{source,test,examples,${UTPLSQL_DIR}/source} + + - name: Cache OJDBC jars + id: cache-ojdbc + uses: actions/cache@v4 + with: + path: ${{ env.OJDBC_HOME }} + key: ${{ runner.os }}-ojdbc-${{ env.OJDBC_URL }}-v1 + + - name: Add OJDBC home + if: steps.cache-ojdbc.outputs.cache-hit != 'true' + id: get-ojdbc + run: mkdir -p ${OJDBC_HOME} && curl -Lk -o ${OJDBC_HOME}/ojdbc8.jar ${OJDBC_URL}/ojdbc8.jar && curl -Lk -o ${OJDBC_HOME}/orai18n.jar ${OJDBC_URL}/orai18n.jar + + - name: Cache utPLSQL-cli + id: cache-utplsql-cli + uses: actions/cache@v4 + with: + path: utPLSQL-cli + key: ${{ runner.os }}-utplsql-cli-v3.1.8 + + - name: Install utPLSQL-cli + if: steps.cache-utplsql-cli.outputs.cache-hit != 'true' + id: install-utplsql-cli + run: curl -Lk -o utPLSQL-cli.zip "https://github.com/utPLSQL/utPLSQL-cli/releases/download/3.1.9/utPLSQL-cli.zip" && unzip utPLSQL-cli.zip && chmod -R u+x utPLSQL-cli + + - name: Install utPLSQL + id: install-utplsql + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/install.sh + + - name: Check code style + if: ${{ matrix.check-style == true }} + id: check-coding-style + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle "$SQLCLI" ${UT3_DEVELOP_SCHEMA}/${UT3_DEVELOP_SCHEMA_PASSWORD}@//${CONNECTION_STR} @/utPLSQL/development/utplsql_style_check.sql + + - name: Validate utPLSQL uninstall + if: ${{ matrix.validate-utplsql-uninstall == true }} + id: validate-uninstall + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/uninstall_validate_utplsql.sh + + - name: Reinstall utPLSQL + if: ${{ matrix.validate-utplsql-uninstall == true }} + id: reinstall-utplsql + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/install.sh + + - name: Create test users + id: create-test-users + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/create_test_users.sh + + - name: Install utPLSQL release + id: install-utplsql-release + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/install_utplsql_release.sh + + - name: Run Examples + id: run-examples + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/run_examples.sh + + - name: Install tests + id: install-tests + run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle test/install_tests.sh + + - name: Run Tests + id: run-tests + run: test/run_tests.sh + +#Start Needed to diagnose occasional failures of DB on test runs + - name: Prepare diagnostic directory + id: preapre-oracle-diag-dir + if: ${{ always() && steps.run-tests.outcome == 'failure' }} + run: | + mkdir database-diag + chmod +777 database-diag + - name: Get ORACLE_BASE/diag data + id: get-oracle-diag-data + if: ${{ always() && steps.run-tests.outcome == 'failure' }} + run: docker exec oracle bash -c "chmod -R +777 ./diag && cp -r ./diag /utPLSQL/database-diag" + - name: Upload ORACLE_BASE/diag data Artifact + id: upload + if: ${{ always() && steps.run-tests.outcome == 'failure' }} + uses: actions/upload-artifact@v4 + with: + name: my-artifact$-${{matrix.db_version_name}} + path: ${{github.workspace}}/database-diag +#End Needed to diagnose occasional failures of DB on test runs + + - name: Validate utPLSQL reports format + id: validate-reports-format + run: bash .github/scripts/validate_report_files.sh + + - name: Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + files: ./cobertura.xml + flags: ${{matrix.db_version_name}} + fail_ci_if_error: true # optional (default = false) + + - name: Publish unit test results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: junit_test_results.xml + + - name: SonarCloud Scan + id: sonar + if: ${{ always() && matrix.run-sonar-scanner == true }} + uses: SonarSource/sonarqube-scan-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.buildString=${{ format( '{0}.{1}', env.UTPLSQL_BUILD_VERSION, matrix.id ) }} + -Dsonar.plsql.jdbc.url=${{ format( 'jdbc:oracle:thin:@//localhost:1521/{0}', env.ORACLE_SID ) }} + -Dsonar.plsql.jdbc.driver.path=${{ format( '{0}/ojdbc8.jar', env.OJDBC_HOME ) }} + + publish: + name: Deploy documentation + needs: [ build ] + concurrency: publish + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + if: | + github.repository == 'utPLSQL/utPLSQL' && + github.base_ref == null && github.ref == 'refs/heads/develop' + steps: + - name: 🔍 API_TOKEN_GITHUB + if: env.API_TOKEN_GITHUB == '' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: echo "API_TOKEN_GITHUB=${GITHUB_TOKEN}" >> $GITHUB_ENV + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ env.API_TOKEN_GITHUB }} + - uses: c-py/action-dotenv-to-setenv@v5 + with: + env-file: .github/variables/.env + - uses: FranzDiebold/github-env-vars-action@v2.8.0 #https://github.com/marketplace/actions/github-environment-variables-action + + - name: Set buid version number env variables + id: set-build-version-number-vars + run: .github/scripts/set_version_numbers_env.sh + + - name: Update project version & build number in source code and documentation + id: update-project-version + run: .github/scripts/update_project_version.sh + + - name: Push version update to repository + id: push-version-number-update + run: | + git add sonar-project.properties VERSION BUILD_NO source/* docs/* + git commit -m 'Updated project version after build [skip ci]' + git push --quiet origin HEAD:${CI_ACTION_REF_NAME} + + - name: Setup git config + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + - name: Build and publish documentation + run: | + pip install mkdocs + pip install mkdocs-git-revision-date-localized-plugin + pip install mkdocs-material + pip install git+https://github.com/jimporter/mike.git + pip install mkdocs-git-committers-plugin-2 + mike deploy -p develop + + dispatch: + name: Dispatch downstream builds + concurrency: trigger + needs: [ build, publish ] + runs-on: ubuntu-latest + timeout-minutes: 15 + if: | + github.repository == 'utPLSQL/utPLSQL' && github.base_ref == null && + ( startsWith( github.ref, 'refs/heads/release/v' ) || github.ref == 'refs/heads/develop' ) + strategy: + matrix: + repo: [ 'utPLSQL/utPLSQL-demo-project', 'utPLSQL/utPLSQL-java-api' ] +# repo: [ 'utPLSQL/utPLSQL-v2-v3-migration' ] + steps: + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v4 + with: + token: ${{ secrets.API_TOKEN_GITHUB }} + repository: ${{ matrix.repo }} + event-type: utPLSQL-build + diff --git a/.github/workflows/rebuild_docs.yml b/.github/workflows/rebuild_docs.yml new file mode 100644 index 000000000..b9f3e4c45 --- /dev/null +++ b/.github/workflows/rebuild_docs.yml @@ -0,0 +1,56 @@ +name: Rebuild older docs versions +on: + workflow_dispatch: + inputs: + versions: + description: 'Space-separated list of versions to rebuild (e.g. "v3.1.14 v3.1.13")' + required: true + default: 'v3.2.2 v3.2.01 v3.1.14 v3.1.13 v3.1.12 v3.1.11 v3.1.10 v3.1.9 v3.1.8 v3.1.6 v3.1.5 v3.1.4 v3.1.3 v3.1.2 v3.1.1 v3.1.0 v3.0.4 v3.0.3 v3.0.2 v3.0.1 v3.0.0 v3.0.0-beta' + +defaults: + run: + shell: bash + +jobs: + rebuild: + name: Rebuild documentation for older versions + concurrency: publish + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + + steps: + - name: 🔍 API_TOKEN_GITHUB + if: env.API_TOKEN_GITHUB == '' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: echo "API_TOKEN_GITHUB=${GITHUB_TOKEN}" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ env.API_TOKEN_GITHUB }} + + - uses: c-py/action-dotenv-to-setenv@v5 + with: + env-file: .github/variables/.env + + - uses: FranzDiebold/github-env-vars-action@v2.8.0 + + - name: Install mkdocs stack + run: | + pip install mkdocs + pip install mkdocs-git-revision-date-localized-plugin + pip install mkdocs-material + pip install git+https://github.com/jimporter/mike.git + pip install mkdocs-git-committers-plugin-2 + + - name: Rebuild specified versions + run: | + for VERSION in ${{ inputs.versions }}; do + echo "=== Rebuilding $VERSION ===" + git checkout "tags/${VERSION}" -- docs/ + mike deploy -p "${VERSION}" + git checkout HEAD -- docs/ mkdocs.yml + done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..7df03680d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,82 @@ +name: Create and publish release artifacts +on: + release: + types: [ released ] +#See: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#example-using-multiple-events-with-activity-types-or-configuration + +defaults: + run: + shell: bash + +jobs: + + upload_artifacts: + name: Upload archives + concurrency: upload + runs-on: ubuntu-latest + timeout-minutes: 60 + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + RELEASE_BODY: ${{ github.event.release.body }} + RELEASE_DATE: ${{ github.event.release.published_at }} + RELEASE_URL: ${{ github.event.release.html_url }} + TARGET_BRANCH: ${{ github.event.release.target_commitish }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ env.API_TOKEN_GITHUB }} + - uses: c-py/action-dotenv-to-setenv@v5 + with: + env-file: .github/variables/.env + - uses: FranzDiebold/github-env-vars-action@v2.8.0 #https://github.com/marketplace/actions/github-environment-variables-action + + - name: Update CHANGELOG.md with release notes + run: .github/scripts/update_changelog.sh + + - name: Set build version number env variables + run: .github/scripts/set_release_version_numbers_env.sh + + - name: Update project version & build number in source code and documentation + run: .github/scripts/update_project_version.sh + + - name: Build and publish documentation + run: | + pip install mkdocs + pip install mkdocs-git-revision-date-localized-plugin + pip install mkdocs-material + pip install git+https://github.com/jimporter/mike.git + pip install mkdocs-git-committers-plugin-2 + mike deploy -p -u ${UTPLSQL_VERSION} latest + mkdocs build --clean -f mkdocs_offline.yml + rm -rf docs/* + cp -r -v site/* docs + git add . + git commit -m "tmp commit of HTML documentation for building a release archive" + + - name: Build release archives + run: | + git archive --prefix=utPLSQL/ -o utPLSQL.zip --format=zip HEAD + git archive --prefix=utPLSQL/ -o utPLSQL.tar.gz --format=tar.gz HEAD + sha256sum utPLSQL.zip --tag > utPLSQL.zip.sha256 + sha256sum utPLSQL.tar.gz --tag > utPLSQL.tar.gz.sha256 + + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: | + utPLSQL.zip + utPLSQL.zip.sha256 + utPLSQL.tar.gz + utPLSQL.tar.gz.sha256 + + - name: Post release announcement to org discussions + env: + GH_TOKEN: ${{ secrets.API_TOKEN_GITHUB }} + run: .github/scripts/post_release_announcement.sh + + - name: Publish release post to utplsql.github.io + run: .github/scripts/publish_release_post.sh + diff --git a/.github/workflows/test_publishing.yml b/.github/workflows/test_publishing.yml new file mode 100644 index 000000000..6f079ac54 --- /dev/null +++ b/.github/workflows/test_publishing.yml @@ -0,0 +1,55 @@ +name: Test publishing +on: + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + + upload_artifacts: + name: Upload archives + concurrency: upload + runs-on: ubuntu-latest + timeout-minutes: 60 + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + RELEASE_TAG: "v3.2.3" + RELEASE_BODY: | + ## What's Changed + * Fix issue with `UT_TAP_REPORTER` on Oracle 23.26 by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1359 + * Fixed support for camelCase in context `--%name` annotation. by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1361 + * utPLSQL can be installed in editioned schema. by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1362 + * Removed coveralls reporter by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1363 + * Clarify behavior of before/after procedures in suitepath by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1364 + * `throws` can reference exception without schema or package name by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1365 + * `throws` works with uninitialized exception variables by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1366 + + + **Full Changelog**: https://github.com/utPLSQL/utPLSQL/compare/v3.2.2...v.3.2.3 + RELEASE_DATE: "2026-07-10 08:30:22" + RELEASE_URL: "https://github.com/utPLSQL/utPLSQL/releases/tag/v3.2.3" + TARGET_BRANCH: "develop" + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ env.API_TOKEN_GITHUB }} + - uses: c-py/action-dotenv-to-setenv@v5 + with: + env-file: .github/variables/.env + - uses: FranzDiebold/github-env-vars-action@v2.8.0 #https://github.com/marketplace/actions/github-environment-variables-action + +# - name: Update CHANGELOG.md with release notes +# run: .github/scripts/update_changelog.sh + +# - name: Post release announcement to org discussions +# env: +# GH_TOKEN: ${{ secrets.API_TOKEN_GITHUB }} +# run: .github/scripts/post_release_announcement.sh + + - name: Publish release post to utplsql.github.io + run: .github/scripts/publish_release_post.sh + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d3299de4e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,136 +0,0 @@ -sudo: required -language: python - -addons: - apt: - packages: - - unzip - # Java9+ Required for Sonar and SQLCL - - openjdk-9-jre-headless - sonarcloud: - organization: utplsql - token: ${SONAR_TOKEN} - -services: - - docker - -env: - global: - - UT3_DEVELOP_SCHEMA=UT3_DEVELOP - - UT3_DEVELOP_SCHEMA_PASSWORD=ut3 - - UT3_RELEASE_VERSION_SCHEMA=UT3 - - UT3_RELEASE_VERSION_SCHEMA_PASSWORD=ut3 - - UT3_USER="UT3\$USER#" - - UT3_USER_PASSWORD=ut3 - - UT3_TESTER=UT3_TESTER - - UT3_TESTER_PASSWORD=ut3 - - UT3_TESTER_HELPER=UT3_TESTER_HELPER - - UT3_TESTER_HELPER_PASSWORD=ut3 - - UT3_TABLESPACE=users - # Environment for building a release - - CURRENT_BRANCH=${TRAVIS_BRANCH} - - UTPLSQL_REPO="utPLSQL/utPLSQL" - - UTPLSQL_BUILD_NO="${TRAVIS_BUILD_NUMBER:-0}" - - UTPLSQL_VERSION=$(. .travis/get_project_version.sh) - - UTPLSQL_BUILD_VERSION=$(. .travis/get_project_build_version.sh) - - UTPLSQL_SOURCES_DIR='source' - - UTPLSQL_BUILD_USER_NAME="Travis CI" - - CACHE_DIR=$HOME/.cache - # Database Env - - SQLCLI="$HOME/sqlcl/bin/sql" - - OJDBC_HOME="$HOME/sqlcl/lib" - - ORACLE_PWD="oracle" - - UT3_DOCKER_REPO="utplsqlv3/oracledb" - - DOCKHER_HUB_REPO="${DOCKER_BASE_TAG:-$UT3_DOCKER_REPO}" - #utPLSQL released version directory - - UTPLSQL_DIR="utPLSQL_latest_release" - - SELFTESTING_BRANCH=${TRAVIS_BRANCH} - - UTPLSQL_CLI_VERSION="3.1.8" - # Maven - - MAVEN_HOME=/usr/local/maven - - 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' - - 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 - directories: - - $CACHE_DIR - - /home/travis/.sonar/cache - - node_modules - - $MAVEN_CFG - -before_install: - #cache to be used between stages. Based on https://github.com/travis-ci/docs-travis-ci-com/issues/1329 - #delete all files in cache that are older than 5 days - - mkdir -p $CACHE_DIR/stages_cache; find $CACHE_DIR/stages_cache/ -mtime +5 -exec rm {} \; - #The update_project_version.sh is done before deployment to validate that the change of project files does not break installation - - bash .travis/update_project_version.sh - #Allow for sonar to blame issues - - git fetch --unshallow - # download latest utPLSQL release - #- curl -LOk $(curl --silent https://api.github.com/repos/utPLSQL/utPLSQL/releases/latest | awk '/browser_download_url/ { print $2 }' | grep ".zip" | sed 's/"//g') - - git clone --depth=1 --branch=${SELFTESTING_BRANCH:-main} https://github.com/utPLSQL/utPLSQL.git $UTPLSQL_DIR - # download latest utPLSQL-cli release - - curl -Lk -o utPLSQL-cli.zip https://github.com/utPLSQL/utPLSQL-cli/releases/download/v${UTPLSQL_CLI_VERSION}/utPLSQL-cli.zip - -install: - #- unzip utPLSQL.zip - - unzip utPLSQL-cli.zip && chmod -R u+x utPLSQL-cli - - bash .travis/install_sqlcl.sh - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/start_db.sh; fi - -before_script: - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/install.sh; fi - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/install_utplsql_release.sh; fi - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/run_examples.sh; fi - -script: - - if [[ ! $TRAVIS_TAG ]]; then bash test/install_and_run_tests.sh; fi - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/validate_report_files.sh; fi - - if [[ ! $TRAVIS_TAG ]] && [[ ("${TRAVIS_REPO_SLUG}" = "${UTPLSQL_REPO}" && "${TRAVIS_PULL_REQUEST}" == false) || ("${TRAVIS_PULL_REQUEST_SLUG}" = "${UTPLSQL_REPO}") ]]; then bash .travis/run_sonar_scanner.sh; fi - - if [[ ! $TRAVIS_TAG ]]; then bash .travis/coveralls_uploader.sh; fi - -notifications: - slack: - rooms: - - secure: "nemt9n2y5sVCAKqa/s7JDQ8AcM59Xu/XbH/RkMOXvPgc+KtR8lBtVD1LkvJ5BaQhqgjuDT7DNt/uisQJ7k6a2OsrVFbnkypZ1hCvntOBpJyoSpD/YL1X8a9GbGojuJcph0BX76KN21LaOVdyOfY0TSlw53XiYWS8iL5HtjpWCbIwHL1SJ8JT8mhdT4hDoUWZjcZ4s4bLH6BQm4un/bMQOmB4sDoCeg4CllJwxgkswVF7OHpOFjgPetvUp7ps8b/Rj8en6zCj9drb0SVbXFgumo2Wd1bC3HHZB8MAZU0kuEV5VgUdum6EelUL5yfB72hssNQA0+CaT3HjBFkTlqWYJmjC4/xyGN3sseiW82T9FDY0g0GAGayNRW+QSiTQ1hbJEcAnNe0GrBUdRsgXdI6COd76YP5VxzRSF6H7niLfgCZdKbIivKUd1o+wBhuyJmqCFIkRWmT38tMGJqJAzbY1jq5gQXbb6E7gt+KdTjlSjcnJYf7XI7zqm+BRr+fbA0ixfXHvfqOBgZN6g08y9nPlDnIjtSF2NSdrj2zqYQAtKuWSOD1bnTyfDJyrtK7OLffZcMYD5Bcss4c8op8tP7MGTjt1S046SJocboh6H4c/nTokpoMRsWs6MKRdebl8C2RObGf5FebSOJg+oh2ZYS5Z/G9GshiY2BD/81+Hc4Miacc=" - on_success: change - on_failure: always - -jobs: - include: - - stage: deploy - env: ORACLE_VERSION="none" - before_install: skip - install: - - pip install mkdocs - before_script: skip - script: - - 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 - - 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: - provider: releases - api_key: $github_api_token - file: - - utPLSQL.zip - - utPLSQL.tar.gz - - utPLSQL.zip.md5 - - utPLSQL.tar.gz.md5 - skip_cleanup: true - on: - repo: ${UTPLSQL_REPO} - tags: true diff --git a/.travis/build_docs.sh b/.travis/build_docs.sh deleted file mode 100755 index db43d7ca9..000000000 --- a/.travis/build_docs.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Change working directory to script directory -cd "${0%/*}" -# Change back to root -cd .. -mkdocs build --clean --strict diff --git a/.travis/build_release_archive.sh b/.travis/build_release_archive.sh deleted file mode 100755 index 9667cba7e..000000000 --- a/.travis/build_release_archive.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# remove markdown documentation -rm -rf docs/* -# and replace it with generated html documentation from the ignored site folder -cp -r -v site/* docs - -git add . -git commit -m "tmp commit for building a release archive" - -# git archive --prefix="utPLSQL${UTPLSQL_BUILD_VERSION}"/ -o "utPLSQL${UTPLSQL_BUILD_VERSION}".zip --format=zip HEAD -# git archive --prefix="utPLSQL${UTPLSQL_BUILD_VERSION}"/ -o "utPLSQL${UTPLSQL_BUILD_VERSION}".tar.gz --format=tar.gz HEAD - -git archive --prefix=utPLSQL/ -o utPLSQL.zip --format=zip HEAD -git archive --prefix=utPLSQL/ -o utPLSQL.tar.gz --format=tar.gz HEAD -md5sum utPLSQL.zip --tag > utPLSQL.zip.md5 -md5sum utPLSQL.tar.gz --tag > utPLSQL.tar.gz.md5 - diff --git a/.travis/coveralls_uploader.js b/.travis/coveralls_uploader.js deleted file mode 100644 index ad6eeee74..000000000 --- a/.travis/coveralls_uploader.js +++ /dev/null @@ -1,31 +0,0 @@ -var fs = require('fs'); -var request = require('request'); -const md5File = require('md5-file'); - -var url -if (process.env.COVERALLS_URL_BASE) { - url = process.env.COVERALLS_URL_BASE+'/api/v1/jobs'; -} else { - url = 'https://coveralls.io/api/v1/jobs'; -} - -fs.readFile('../coverage.json',function (err,data) { - if (err) { - return console.log(err); - } - req = JSON.parse(data); - req.service_job_id = process.env.TRAVIS_JOB_ID; - req.service_name = 'travis-ci'; - if (process.env.COVERALLS_REPO_TOKEN) { - req.repo_token = process.env.COVERALLS_REPO_TOKEN; - } - - for (var i in req.source_files) { - req.source_files[i].source_digest = md5File.sync("../" + req.source_files[i].name); - } - - var requestStr = JSON.stringify(req); - - request.post({url : url, form : { json:requestStr}}, function(err, response, body){process.stdout.write(body);}); - -}); diff --git a/.travis/coveralls_uploader.sh b/.travis/coveralls_uploader.sh deleted file mode 100755 index 8fcbad58d..000000000 --- a/.travis/coveralls_uploader.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -e -echo "coveralls_uploader" -npm install request --save -npm install --save md5-file -cd "$(dirname "$(readlink -f "$0")")" -exec node coveralls_uploader.js diff --git a/.travis/download.js b/.travis/download.js deleted file mode 100644 index 67c45fac6..000000000 --- a/.travis/download.js +++ /dev/null @@ -1,56 +0,0 @@ - -var casper = require("casper").create({ - // verbose: true, - // logLevel: "debug", - pageSettings: { - loadImages: false, - loadPlugins: false - } -}); - -if (casper.cli.args.length < 4) { - casper.echo("Missing parameters: username password agreementUrl downloadUrl").exit(1); -} - -// Script parameters. -var paramUsername = casper.cli.get(0); -var paramPassword = casper.cli.get(1); -var agreementUrl = casper.cli.get(2); -var downloadUrl = casper.cli.get(3); -var downloaded = false; - -phantom.addCookie({ - 'name': 'oraclelicense', - 'value': '152021', - 'domain': 'oracle.com' - }); - -casper.start(); -// TODO: Error handling. - - -// Try to access the download page, wait for redirection and submit the login form. -casper.thenOpen(downloadUrl).waitForUrl(/signon\.jsp$/, function (re) { - //this.echo("Injecting Login Info"); - this.evaluate(function (username, password) { - document.getElementById("sso_username").value = username; - document.getElementById("ssopassword").value = password; - doLogin(document.LoginForm); - }, paramUsername, paramPassword); - // this.capture("Screenshot.png"); -}); - -casper.on("resource.received", function (resource) { - //this.echo("Received something: " + resource.url); - if (resource.url.indexOf("AuthParam") !== -1 && !downloaded) { - //this.echo("DownloadUrl:"); - // Print the download url. - this.echo(resource.url); - downloaded = true; - // TODO: Try to download file from here. this.download is not working because of cross site request. - } -}); - -casper.run(function () { - this.exit(); -}); diff --git a/.travis/download.sh b/.travis/download.sh deleted file mode 100755 index 8dfa1942d..000000000 --- a/.travis/download.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -set -e - -if [ "$ORACLE_OTN_USER" == "" ] || [ "$ORACLE_OTN_PASSWORD" == "" ]; then - echo "Error: Oracle OTN username/password not specified." - exit 1 -fi - -PRODUCT="" - -# Call the casperjs script to return the download url. -# Then download the file using curl. -downloadFile() { - downloadUrl=$(exec casperjs download.js $ORACLE_OTN_USER $ORACLE_OTN_PASSWORD $1 $2) - downloadUrl=${downloadUrl%$'\r'} - echo "DownloadURL: $downloadUrl" - curl -o $3 -L "$downloadUrl" -} - -############################# -########### START ########### -############################# - -while getopts "p:" OPTNAME; do - case "${OPTNAME}" in - "p") PRODUCT="${OPTARG}" ;; - esac -done - -if [ "$PRODUCT" == "se12c" ]; then - agreementUrl="http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html" - downloadUrl="http://download.oracle.com/otn/linux/oracle12c/121020/linuxamd64_12102_database_se2_1of2.zip" - outputFile=linuxamd64_12102_database_se2_1of2.zip - downloadFile $agreementUrl $downloadUrl $outputFile - agreementUrl="http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html" - downloadUrl="http://download.oracle.com/otn/linux/oracle12c/121020/linuxamd64_12102_database_se2_2of2.zip" - outputFile=linuxamd64_12102_database_se2_2of2.zip - downloadFile $agreementUrl $downloadUrl $outputFile - exit 0 -fi - -if [ "$PRODUCT" == "ee12c" ]; then - agreementUrl="http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html" - downloadUrl="http://download.oracle.com/otn/linux/oracle12c/121020/linuxamd64_12102_database_1of2.zip" - outputFile=linuxamd64_12102_database_1of2.zip - downloadFile $agreementUrl $downloadUrl $outputFile - agreementUrl="http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html" - DOWNLOAD_URL="http://download.oracle.com/otn/linux/oracle12c/121020/linuxamd64_12102_database_2of2.zip" - outputFile=linuxamd64_12102_database_2of2.zip - downloadFile $agreementUrl $downloadUrl $outputFile - exit 0 -fi - -if [ "$PRODUCT" == "xe11g" ]; then - agreementUrl="http://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/index.html" - downloadUrl="https://edelivery.oracle.com/akam/otn/linux/oracle11g/xe/oracle-xe-11.2.0-1.0.x86_64.rpm.zip" - outputFile=oracle-xe-11.2.0-1.0.x86_64.rpm.zip - downloadFile $agreementUrl $downloadUrl $outputFile - exit 0 -fi - -if [ "$PRODUCT" == "sqlcl" ]; then - agreementUrl="http://www.oracle.com/technetwork/developer-tools/sqlcl/downloads/index.html" - downloadUrl="https://download.oracle.com/otn/java/sqldeveloper/sqlcl-18.3.0.259.2029.zip" - #downloadUrl="https://download.oracle.com/otn/java/sqldeveloper/sqlcl-19.2.1.206.1649.zip" - outputFile=sqlcl-18.3.0.259.2029.zip - downloadFile $agreementUrl $downloadUrl $outputFile - exit 0 -fi - -echo "Error: invalid product: $PRODUCT" -exit 1 diff --git a/.travis/get_project_build_version.sh b/.travis/get_project_build_version.sh deleted file mode 100755 index 838523b05..000000000 --- a/.travis/get_project_build_version.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -echo `sed -E "s/(v?[0-9]+\.)([0-9]+\.)([0-9]+)(-.*)?/\1\2\3\.${UTPLSQL_BUILD_NO}\4/" <<< "${UTPLSQL_VERSION}"` diff --git a/.travis/get_project_version.sh b/.travis/get_project_version.sh deleted file mode 100755 index 20a658ed9..000000000 --- a/.travis/get_project_version.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -#When building a new version from a release branch, the version is taken from release branch name -if [[ "${CURRENT_BRANCH}" =~ ^release/v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then - version=${CURRENT_BRANCH#release\/} -else - #Otherwise, version is taken from the VERSION file - version=`cat VERSION` - #When on develop branch, add "-develop" to the version text - if [[ "${CURRENT_BRANCH}" == "develop" ]]; then - version=`sed -E "s/(v?[0-9]+\.[0-9]+\.[0-9]+).*/\1-develop/" <<< "${version}"` - fi -fi -echo ${version} diff --git a/.travis/install.sh b/.travis/install.sh deleted file mode 100755 index 537fddb66..000000000 --- a/.travis/install.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/bin/bash - -cd source -set -ev - -INSTALL_FILE="install_headless_with_trigger.sql" -if [[ ! -f "${INSTALL_FILE}" ]]; then - INSTALL_FILE="install_headless.sql" -fi - -#install core of utplsql -time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL -whenever sqlerror exit failure rollback -set feedback off -set verify off - ---alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6003,6009,6010,7206)'; -alter session set plsql_optimize_level=0; -@${INSTALL_FILE} $UT3_DEVELOP_SCHEMA $UT3_DEVELOP_SCHEMA_PASSWORD -SQL - -#Run this step only on second child job (12.1 - at it's fastest) -if [[ "${TRAVIS_JOB_NUMBER}" =~ \.2$ ]]; then - - #check code-style for errors - time "$SQLCLI" $UT3_DEVELOP_SCHEMA/$UT3_DEVELOP_SCHEMA_PASSWORD@//$CONNECTION_STR @../development/utplsql_style_check.sql - - #test install/uninstall process - time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL - set feedback off - set verify off - whenever sqlerror exit failure rollback - - @uninstall_all.sql $UT3_DEVELOP_SCHEMA - whenever sqlerror exit failure rollback - declare - v_leftover_objects_count integer; - begin - select sum(cnt) - into v_leftover_objects_count - from ( - select count(1) cnt from dba_objects where owner = '$UT3_DEVELOP_SCHEMA' - where object_name not like 'PLSQL_PROFILER%' and object_name not like 'DBMSPCC_%' - union all - select count(1) cnt from dba_synonyms where table_owner = '$UT3_DEVELOP_SCHEMA' - where table_name not like 'PLSQL_PROFILER%' and table_name not like 'DBMSPCC_%' - ); - if v_leftover_objects_count > 0 then - raise_application_error(-20000, 'Not all objects were successfully uninstalled - leftover objects count='||v_leftover_objects_count); - end if; - end; - / -SQL - - time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL - set feedback off - set verify off - - alter session set plsql_optimize_level=0; - @install.sql $UT3_DEVELOP_SCHEMA - @install_ddl_trigger.sql $UT3_DEVELOP_SCHEMA - @create_synonyms_and_grants_for_public.sql $UT3_DEVELOP_SCHEMA -SQL - -fi - - -time "$SQLCLI" sys/$ORACLE_PWD@//$CONNECTION_STR AS SYSDBA <<-SQL -set feedback off -whenever sqlerror exit failure rollback - --------------------------------------------------------------------------------- -PROMPT Adding back create-trigger privilege to $UT3_DEVELOP_SCHEMA for testing -grant administer database trigger to $UT3_DEVELOP_SCHEMA; - --------------------------------------------------------------------------------- -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; - -grant execute on dbms_lock to $UT3_TESTER; - -PROMPT Granting $UT3_DEVELOP_SCHEMA code to $UT3_TESTER - -begin - for i in ( - select object_name from all_objects t - where t.object_type in ('PACKAGE','TYPE') - and owner = '$UT3_DEVELOP_SCHEMA' - and generated = 'N' - and object_name not like 'SYS%') - loop - execute immediate 'grant execute on $UT3_DEVELOP_SCHEMA."'||i.object_name||'" to $UT3_TESTER'; - end loop; -end; -/ - -PROMPT Granting $UT3_DEVELOP_SCHEMA tables to $UT3_TESTER - -begin - for i in ( select table_name from all_tables t where owner = '$UT3_DEVELOP_SCHEMA' and nested = 'NO' and iot_name is null) - loop - execute immediate 'grant select on $UT3_DEVELOP_SCHEMA.'||i.table_name||' to $UT3_TESTER'; - end loop; -end; -/ - - --------------------------------------------------------------------------------- -PROMPT Creating $UT3_USER - minimal privileges user for API testing - -create user $UT3_USER identified by "$UT3_USER_PASSWORD" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; -grant create session, create procedure, create type, create table to $UT3_USER; - -PROMPT Grants for starting a debugging session from $UT3_USER -grant debug connect session to $UT3_USER; -grant debug any procedure to $UT3_USER; -begin - \$if dbms_db_version.version <= 11 \$then - null; -- no addition action necessary - \$else - -- necessary on 12c or higher - dbms_network_acl_admin.append_host_ace ( - host =>'*', - ace => sys.xs\$ace_type( - privilege_list => sys.xs\$name_list('JDWP') , - principal_name => '$UT3_USER', - principal_type => sys.xs_acl.ptype_db - ) - ); - \$end -end; -/ - --------------------------------------------------------------------------------- -PROMPT Creating $UT3_TESTER_HELPER - provides functions to allow min grant test user setup tests. - -create user $UT3_TESTER_HELPER identified by "$UT3_TESTER_HELPER_PASSWORD" default tablespace $UT3_TABLESPACE quota unlimited on $UT3_TABLESPACE; -grant create session, create procedure, create type, create table to $UT3_TESTER_HELPER; - -PROMPT Grants for testing distributed transactions -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_DEVELOP_SCHEMA 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, - grant any object privilege, grant any privilege, create public synonym, drop public synonym, create any trigger - 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/install_sqlcl.sh b/.travis/install_sqlcl.sh deleted file mode 100755 index 9743ddac2..000000000 --- a/.travis/install_sqlcl.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -e - -SQLCL_FILE=sqlcl-18.3.0.259.2029.zip -cd .travis - -# Download if not present on cache dir. -if [ ! -f $CACHE_DIR/$SQLCL_FILE ]; then - npm install -g phantomjs-prebuilt casperjs - bash download.sh -p sqlcl - mv $SQLCL_FILE $CACHE_DIR -else - echo "Installing sqlcl from cache..." -fi; - -# Install sqlcl. -unzip -q $CACHE_DIR/$SQLCL_FILE -d $HOME - -# Check if it is installed correctly. -$SQLCLI -v diff --git a/.travis/push_docs_to_github_io.sh b/.travis/push_docs_to_github_io.sh deleted file mode 100755 index 80fd47b43..000000000 --- a/.travis/push_docs_to_github_io.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -# Based on `push_docs_to_gh_pages.sh` -# Significant alterations -# - Support for multiple copies of documentation, -# - only clearing out develop -# - index.md logging doc history - -# How to run: -# - From repository root .travis/push_docs_to_gh_pages.sh - -# Required files / directories (relative from repo root) -# - File: "docs/index.md" with that contains develop docs - -# Required ENV Variables -LATEST_DOCS_BRANCH="develop" -GITHUB_IO_REPO='utPLSQL/utPLSQL.github.io' -GITHUB_IO_BRANCH='main' - -# TRAVIS_* variables are set by travis directly and only need to be if testing externally - -# 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 1; } - # Fail if the markdown documentation is not present. - [[ -f ./docs/index.md ]] || { echo "file docs/index.md not found"; exit 1; } - - # 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 GITHUB_IO_BRANCH branch - mkdir pages - cd ./pages - git clone --depth 1 https://${github_api_token}@github.com/${GITHUB_IO_REPO} -b ${GITHUB_IO_BRANCH} . - - mkdir -p utPLSQL - cd ./utPLSQL - #clear out develop documentation directory and copy docs contents to it. - 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 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` ]]; then - echo "No changes to docs detected." - exit 0 - fi - #Changes where detected, so we need to update the version log. - now=$(date +"%d %b %Y - %r") - if [ ! -f index.md ]; then - echo "---" >>index.md - echo "layout: default" >>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 - echo "" >>index.md #- 8th line - placeholder for develop branch doc - echo "" >>index.md - echo "## Released Version Doc History" >>index.md - echo "" >>index.md - fi - #If build running on a TAG - it's a new release - need to add it to documentation - if [ -n "${TRAVIS_TAG}" ]; then - sed -i '7s@.*@'" - [Latest ${TRAVIS_TAG} documentation](latest/) - Created $now"'@' index.md - #add entry to the top of version history (line end of file - ## Released Version Doc History - sed -i '12i'" - [${TRAVIS_TAG} documentation](${UTPLSQL_VERSION}/) - Created $now" index.md - fi - #replace 4th line in log - sed -i '8s@.*@'" - [Latest development version](develop/) - Created $now"'@' index.md - #Add and Commit the changes back to pages repo. - 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:${GITHUB_IO_BRANCH} -fi - diff --git a/.travis/push_release_version.sh b/.travis/push_release_version.sh deleted file mode 100755 index 082359af4..000000000 --- a/.travis/push_release_version.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -# We are updating version number only when: -# - not a pull request -# - branch name is = develop or branch name is like release/vX.X.X... -if [ "${TRAVIS_REPO_SLUG}" = "${UTPLSQL_REPO}" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [[ "${CURRENT_BRANCH}" =~ ^(release/v[0-9]+\.[0-9]+\.[0-9]+.*|develop)$ ]]; then - echo Current branch is "${CURRENT_BRANCH}" - echo "Committing version & buildNo into branch (${CURRENT_BRANCH})" - git add sonar-project.properties - git add VERSION - git add source/* - git add docs/* - git commit -m 'Updated project version after build [skip ci]' - echo "Pushing to origin" - git push --quiet origin HEAD:${CURRENT_BRANCH} -else - echo "Publishing of version skipped for branch ${CURRENT_BRANCH}, pull request ${TRAVIS_PULL_REQUEST}" -fi diff --git a/.travis/run_sonar_scanner.sh b/.travis/run_sonar_scanner.sh deleted file mode 100755 index 4685eb3f3..000000000 --- a/.travis/run_sonar_scanner.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -#Run Sonar based on conditions - -MAIN_DEV_BRANCH="develop" - -BRANCH_SONAR_PROPERTY="sonar.branch.name" -BRANCH_SONAR_TARGET_PROPERTY="sonar.branch.target" - -PR_SONAR_BRANCH_PROPERTY="sonar.pullrequest.branch" -PR_KEY_PROPERTY="sonar.pullrequest.key" -PR_SONAR_BASE_PROPERTY="sonar.pullrequest.base" -PR_SONAR_TOKEN_PROPERTY="sonar.pullrequest.github.token.secured" - -DB_URL_SONAR_PROPERTY="sonar.plsql.jdbc.url" -DB_DRIVER_PATH="sonar.plsql.jdbc.driver.path" - -#Add property to file -function add_sonar_property { - echo "$1=$2" >> sonar-project.properties -} - - -if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - BRANCH=$TRAVIS_BRANCH; - PR_BRANCH="" - echo "BRANCH=$BRANCH" -else - BRANCH=$TRAVIS_PULL_REQUEST_BRANCH - PR_BRANCH=$TRAVIS_BRANCH - echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$TRAVIS_PULL_REQUEST, BRANCH=$BRANCH" - -fi - - -#Are we running on utPLSQL repo and not an external PR? -echo "Check if we running from develop or on branch" -if [ "${TRAVIS_REPO_SLUG}" = "${UTPLSQL_REPO}" ] && [[ ! "${BRANCH}" =~ ^(release/v[0-9]+\.[0-9]+\.[0-9]+.*|"${MAIN_DEV_BRANCH}")$ ]]; then - - echo "" >> sonar-project.properties - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - echo "Updating sonar properties to include branch ${BRANCH}" - add_sonar_property "${BRANCH_SONAR_PROPERTY}" "${BRANCH}" - add_sonar_property "${BRANCH_SONAR_TARGET_PROPERTY}" "${MAIN_DEV_BRANCH}" - elif [ "${TRAVIS_PULL_REQUEST_SLUG}" = "${TRAVIS_REPO_SLUG}" ]; then - echo "Updating sonar properties to include pull request ${BRANCH}" - add_sonar_property "${PR_SONAR_TOKEN_PROPERTY}" "${GITHUB_TRAVISCI_TOKEN}" - add_sonar_property "${PR_SONAR_BRANCH_PROPERTY}" "${BRANCH}" - add_sonar_property "${PR_KEY_PROPERTY}" "${PR}" - add_sonar_property "${PR_SONAR_BASE_PROPERTY}" "${PR_BRANCH}" - else - echo "PR from external source no changes to properties." - fi -else - echo "No need to update sonar we building on release or develop" -fi - -#Address issue : Could not find ref 'develop' in refs/heads or refs/remotes/origin -git fetch --no-tags https://github.com/utPLSQL/utPLSQL.git +refs/heads/develop:refs/remotes/origin/develop - -echo "Adding OJDBC Driver Path ${OJDBC_HOME}/ojdbc8.jar" -add_sonar_property "${DB_URL_SONAR_PROPERTY}" "jdbc:oracle:thin:@${CONNECTION_STR}" -add_sonar_property "${DB_DRIVER_PATH}" "${OJDBC_HOME}/ojdbc8.jar" - - -#Execute Sonar scanner -echo "Executing sonar scanner" -sonar-scanner diff --git a/.travis/start_db.sh b/.travis/start_db.sh deleted file mode 100755 index e42656987..000000000 --- a/.travis/start_db.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e - -# Private Repo Login -if [ ! -f $CACHE_DIR/.docker/config.json ]; then - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" - mkdir -p $CACHE_DIR/.docker && cp $HOME/.docker/config.json $CACHE_DIR/.docker/ -else - echo "Using docker login from cache..." - mkdir -p $HOME/.docker && cp $CACHE_DIR/.docker/config.json $HOME/.docker/ -fi - -df -h -time docker pull $DOCKHER_HUB_REPO:$ORACLE_VERSION -df -h -docker run -d --name $ORACLE_VERSION $DOCKER_OPTIONS -p 1521:1521 $DOCKHER_HUB_REPO:$ORACLE_VERSION -time docker logs -f $ORACLE_VERSION | grep -m 1 "DATABASE IS READY TO USE!" --line-buffered -df -h diff --git a/.travis/trigger_travis.sh b/.travis/trigger_travis.sh deleted file mode 100755 index 039b00bbb..000000000 --- a/.travis/trigger_travis.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Trigger a new Travis-CI job. -# Ordinarily, a new Travis job is triggered when a commit is pushed to a -# GitHub repository. The trigger-travis.sh script provides a programmatic -# way to trigger a new Travis job. - -# To use this script to trigger a dependent build in Travis, do two things: -# -# 1. Set an environment variable TRAVIS_ACCESS_TOKEN by navigating to -# https://travis-ci.org/MYGITHUBID/MYGITHUBPROJECT/settings -# The TRAVIS_ACCESS_TOKEN environment variable will be set when Travis runs -# the job, but won't be visible to anyone browsing https://travis-ci.org/. -# - -echoerr() { echo "$@" 1>&2; } - -TRAVIS_URL=travis-ci.org -BRANCH=develop -USER="utPLSQL" -RESULT=1 -declare -a REPO_MATRIX=("utPLSQL-java-api" "utPLSQL-v2-v3-migration" "utPLSQL-cli" "utPLSQL-maven-plugin" "utPLSQL-demo-project") - -TOKEN=$1 - -if [ -n "$TRAVIS_REPO_SLUG" ] ; then - MESSAGE=",\"message\": \"Triggered by upstream build of $TRAVIS_REPO_SLUG commit "`git rev-parse --short HEAD`"\"" -else - MESSAGE=",\"message\": \"Triggered manually from shell\"" -fi - -# For debugging: -#echo "MESSAGE=$MESSAGE" - -body="{ -\"request\": { - \"branch\":\"$BRANCH\" - $MESSAGE -}}" - -for DOWNSTREAM_BUILD in "${REPO_MATRIX[@]}"; do - - curl -s -X POST \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -H "Travis-API-Version: 3" \ - -H "Authorization: token ${TOKEN}" \ - -d "$body" \ - https://api.${TRAVIS_URL}/repo/${USER}%2F${DOWNSTREAM_BUILD}/requests \ - | tee ${DOWNSTREAM_BUILD}-output.txt - - if grep -q '"@type": "error"' ${DOWNSTREAM_BUILD}-output.txt; then - RESULT=0 - echoerr "" - echoerr "ERROR: Failed to start ${DOWNSTREAM_BUILD}" - echoerr "" - fi - if grep -q 'access denied' ${DOWNSTREAM_BUILD}-output.txt; then - RESULT=0 - echoerr "" - echoerr "ERROR: Failed to start ${DOWNSTREAM_BUILD} - access denied" - echoerr "" - fi - -done - -if [[ RESULT -eq 0 ]]; then - exit 1 -fi diff --git a/BUILD_NO b/BUILD_NO new file mode 100644 index 000000000..697eb0993 --- /dev/null +++ b/BUILD_NO @@ -0,0 +1 @@ +4532 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..faf1f504a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,630 @@ +# Changelog + +All notable changes to utPLSQL are documented here. +Releases are available on the [GitHub Releases page](https://github.com/utPLSQL/utPLSQL/releases). + +--- + +## [v.3.2.3] - 2026-07-10 + +## What's Changed +* Fix issue with `UT_TAP_REPORTER` on Oracle 23.26 by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1359 +* Fixed support for camelCase in context `--%name` annotation. by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1361 +* utPLSQL can be installed in editioned schema. by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1362 +* Removed coveralls reporter by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1363 +* Clarify behavior of before/after procedures in suitepath by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1364 +* `throws` can reference exception without schema or package name by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1365 +* `throws` works with uninitialized exception variables by @jgebal in https://github.com/utPLSQL/utPLSQL/pull/1366 + + +**Full Changelog**: https://github.com/utPLSQL/utPLSQL/compare/v3.2.2...v.3.2.3 + +--- + + +## [v3.2.2] - 2026-05-24 + +## What's Changed +* Filtering by tags fixed for schemas containing suite-paths and contexts by @jgebal in [1325](https://github.com/utPLSQL/utPLSQL/pull/1325) +* Fixed issue with reporter failing to initialize by @jgebal in [1354](https://github.com/utPLSQL/utPLSQL/pull/1354) +* Fixed issue with duplicate sources from dba_source by @jgebal in [1355](https://github.com/utPLSQL/utPLSQL/pull/1355) +* Allow HTML coverage on FIPS-enabled DB by @jgebal in [1356](https://github.com/utPLSQL/utPLSQL/pull/1356) + + +**Full Changelog**: https://github.com/utPLSQL/utPLSQL/compare/v3.2.01...v3.2.2 + +--- + +## [v3.2.01] - 2026-04-22 + +## Important + - Dropped support for Oracle Database versions older than 19c + +## New features + - Add TAP reporter by @WayneNani in [#1305](https://github.com/utPLSQL/utPLSQL/pull/1305) + +## Enhancements and bug fixes + - Added duplicate detection on cursor comparison using `join_by` by @lwasylow in [#1295](https://github.com/utPLSQL/utPLSQL/pull/1295) + - Fix code causing the object duration Oracle exception on oracle 23.26 by @lwasylow in [#1310](https://github.com/utPLSQL/utPLSQL/pull/1310) + - Packages with annotation-like comments are not test suites by @jgebal in [#1313](https://github.com/utPLSQL/utPLSQL/pull/1313) + - Performance improvements to annotation parsing by @lwasylow in [#1312](https://github.com/utPLSQL/utPLSQL/pull/1319) + - Updated documentation and copyright banners across source files @jgebal in [#1319](https://github.com/utPLSQL/utPLSQL/pull/1319) + +--- + +## [v3.1.14] - 2024-02-19 + +## New features + + - Added support for `and` and `or` operators when running tests by tags by @lwasylow in [#1250](https://github.com/utPLSQL/utPLSQL/pull/1250) + +## Enhancements + + - Allow for test runs over 4 hours by @jgebal in [#1243](https://github.com/utPLSQL/utPLSQL/pull/1243) + - Framework performance improvements by @jgebal in [#1249](https://github.com/utPLSQL/utPLSQL/pull/1249) + - Support multiple expectation failures with teamcity reporter by @jgebal in [#1251](https://github.com/utPLSQL/utPLSQL/pull/1251) + - Update TFS reporter to correct format by @lwasylow in [#1270](https://github.com/utPLSQL/utPLSQL/pull/1270) + +## Bug fixes + + - Address issue where the `not_to(contain)` did not run correctly by @lwasylow in [#1246](https://github.com/utPLSQL/utPLSQL/pull/1246) + - Fix regex to be NLS_SORT independent by @jgebal in [#1253](https://github.com/utPLSQL/utPLSQL/pull/1253) + - Fix output length error and output buffer. by @jgebal in [#1255](https://github.com/utPLSQL/utPLSQL/pull/1255) + - (docs): Update running-unit-tests.md - remove example by @gassenmj in [#1261](https://github.com/utPLSQL/utPLSQL/pull/1261) + - The line-rate is not recorded for packages and classes in cobertura coverage reporter by @lwasylow in [#1269](https://github.com/utPLSQL/utPLSQL/pull/1269) + - (docs): Fix nested list issue by @iamrachid in [#1274](https://github.com/utPLSQL/utPLSQL/pull/1274) + +--- + +## [v3.1.13] - 2022-12-11 + +## New features + + - Add ability to run tests by part of a name. Fixed in [#1203](https://github.com/utPLSQL/utPLSQL/pull/1203), resolves [#470](https://github.com/utPLSQL/utPLSQL/issues/470) + +## Enhancements + + - Added documentation section on creating a custom reporter. Fixed in [#1225](https://github.com/utPLSQL/utPLSQL/pull/1225), resolves [#710](https://github.com/utPLSQL/utPLSQL/issues/701) + - Add ability to specify code coverage objects include/exclude masks as regular expressions. Fixed in [#1186](https://github.com/utPLSQL/utPLSQL/issues/1186), resolves [#1053](https://github.com/utPLSQL/utPLSQL/issues/1053) + +## Bug fixes + + - Comparing collection that have long type names. Fixed in [#1238](https://github.com/utPLSQL/utPLSQL/issues/1238), resolves [#1235](https://github.com/utPLSQL/utPLSQL/issues/1235). + - Code coverage reporting on code with long lines. Fixed in [#1240](https://github.com/utPLSQL/utPLSQL/issues/1240), resolves [#1232](https://github.com/utPLSQL/utPLSQL/issues/1232), [#1087](https://github.com/utPLSQL/utPLSQL/issues/1087). + - Code coverage reporting does not exclude tests. Fixed in [#1226](https://github.com/utPLSQL/utPLSQL/issues/1226), resolves [#1222](https://github.com/utPLSQL/utPLSQL/issues/1222). + - Uninstall script buffer overflow. Fixed in [#1221](https://github.com/utPLSQL/utPLSQL/issues/1221), resolves [#1220](https://github.com/utPLSQL/utPLSQL/issues/1220). + +## Internal improvements + + - Improved process of generating utPLSQL documentation. Implemented in [689bbd0](https://github.com/utPLSQL/utPLSQL/commit/689bbd0e365ed919315c29727bc10fbfc0dadce8), resolves [#1237](https://github.com/utPLSQL/utPLSQL/issues/1237). + - Removed username env variables for internal development. Implemented in [#1201](https://github.com/utPLSQL/utPLSQL/issues/1221), resolves [#1200](https://github.com/utPLSQL/utPLSQL/issues/1200). + - Test execution data of utPLSQL project not showing on SonarCloud. Implemented in [#1199](https://github.com/utPLSQL/utPLSQL/issues/1199), resolves [#1198](https://github.com/utPLSQL/utPLSQL/issues/1198). + +--- + +## [v3.1.12] - 2022-02-25 + +## New features + - Added support for description in the `--%disabled` annotation. See [documentation](https://github.com/utPLSQL/utPLSQL/blob/4515e0b5a3b4b0de0c2198db3235ef8614bff4d8/docs/userguide/annotations.md#disabled). Implemented in: [#1183](https://github.com/utPLSQL/utPLSQL/pull/1183), resolves [#610](https://github.com/utPLSQL/utPLSQL/issues/610). + - Added support for native `JSON` datatype on Oracle `21c`. Implemented in [#1181](https://github.com/utPLSQL/utPLSQL/pull/1181), resolves [#1114](https://github.com/utPLSQL/utPLSQL/issues/1114). + - Added new mather `to_be_within( distance|pct ).of_(expoected)`. See [documentation](https://github.com/utPLSQL/utPLSQL/blob/fd7ef9c14111a48e03fff7851f990c7192539170/docs/userguide/expectations.md#to_be_within-of). Implemented in [#1076](https://github.com/utPLSQL/utPLSQL/pull/1076), resolves [#77](https://github.com/utPLSQL/utPLSQL/issues/77). + +## Enhancements + +- Improved performance of SQL used to retrieve Coverage sources. Implemented in [#1187](https://github.com/utPLSQL/utPLSQL/pull/1187), resolves [#1169](https://github.com/utPLSQL/utPLSQL/issues/1169). +- Added ability for utPLSQL to gather coverage on code invoking DBMS_STATS package. Implemented in [#1184](https://github.com/utPLSQL/utPLSQL/pull/1184), resolves [#1097](https://github.com/utPLSQL/utPLSQL/issues/1097), [#1094](https://github.com/utPLSQL/utPLSQL/issues/1094). +- Fixed typos and improved documentation. Implemented in [#1173](https://github.com/utPLSQL/utPLSQL/pull/1173), [#1171](https://github.com/utPLSQL/utPLSQL/pull/1171) + +## Bug fixes + +- Actual and Expected are now correctly reported when comparing `JSON` data. Implemented in [#1181](https://github.com/utPLSQL/utPLSQL/pull/1181), resolves [#1113](https://github.com/utPLSQL/utPLSQL/issues/1113). +- Packages with removed annotations are now correctly recognized as non-utPLSQL packages. Implemented in [#1180](https://github.com/utPLSQL/utPLSQL/pull/1180), resolves [#1177](https://github.com/utPLSQL/utPLSQL/issues/1177). +- Fixed issues with comparison of nested object structures. Implemented in [#1179](https://github.com/utPLSQL/utPLSQL/pull/1179), resolves [#1082](https://github.com/utPLSQL/utPLSQL/issues/1082), [#1083](https://github.com/utPLSQL/utPLSQL/issues/1083), [#1098](https://github.com/utPLSQL/utPLSQL/issues/1098). + +## Internal improvements + +- Moved build and test process for utPLSQL from Travis to GithubActions. Implemented in [#1175](https://github.com/utPLSQL/utPLSQL/pull/1175) + +--- + +## [v3.1.11] - 2021-11-17 + +## Enhancements + +- utPLSQL can now be used to generate coverage reports for external tools. See [documentation](https://github.com/utPLSQL/utPLSQL/blob/955de5c2dc527a98a6f5dc0dd8bd8d81a44c459f/docs/userguide/coverage.md#reporting-coverage-outside-of-utplsql) + Implemented in: [#1079](https://github.com/utPLSQL/utPLSQL/pull/1079), resolves [#1025](https://github.com/utPLSQL/utPLSQL/issues/1025) +- Enhanced UT_COVERAGE_COBERTURA_REPORTER to better support TFS and GitLab. Implemented in [#1137](https://github.com/utPLSQL/utPLSQL/pull/1137) and [#1140](https://github.com/utPLSQL/utPLSQL/pull/1140), resolves [#1107](https://github.com/utPLSQL/utPLSQL/issues/1107) +- Added support for installation on Oracle 21c - removed dependency on DBMS_OBFUSCATION_TOOLKIT. Implemented in [#1112](https://github.com/utPLSQL/utPLSQL/pull/1112), resolves [#1111](https://github.com/utPLSQL/utPLSQL/issues/1111) and [#1127](https://github.com/utPLSQL/utPLSQL/issues/1127) +- Added support for running utPLSQL framework in parallel-enabled database. Implemented in [#1160](https://github.com/utPLSQL/utPLSQL/pull/1160), resolves [#1134](https://github.com/utPLSQL/utPLSQL/issues/1134) + +## Bug fixes + +- Suite structure is built properly even with other than English TNS settings. Implemented in [#1061](https://github.com/utPLSQL/utPLSQL/pull/1061), resolves [#1060](https://github.com/utPLSQL/utPLSQL/issues/1060) +- Fixed XML content reporting (CDATA) in UT_REALTIME_REPORTER used by SQLDeveloper plugin. Implemented in [#1075](https://github.com/utPLSQL/utPLSQL/pull/1075), resolves [#1073](https://github.com/utPLSQL/utPLSQL/issues/1073) +- Fixed XML content reporting (CDATA) in JUnit reporter - regression. Implemented in [#1085](https://github.com/utPLSQL/utPLSQL/pull/1085), resolves [#1084](https://github.com/utPLSQL/utPLSQL/issues/1084) +- Fixed issue with utPLSQL failing to run coverage reporting when trigger has overlapping name with procedure/function/package/type. Implemented in [#1091](https://github.com/utPLSQL/utPLSQL/pull/1091), resolves [#1086](https://github.com/utPLSQL/utPLSQL/issues/1086) +- Fixed issue with parsing utPLSQL suites with DDL trigger when usign AUTHID clause. Implemented in [#1093](https://github.com/utPLSQL/utPLSQL/pull/1093), resolves [#1088](https://github.com/utPLSQL/utPLSQL/issues/1088) + +## Internal improvements + +- Improved how privilege checks are handled by framework installation. Implemented in [#1056](https://github.com/utPLSQL/utPLSQL/pull/1056), resolves [#1050](https://github.com/utPLSQL/utPLSQL/issues/1050) +- Restructured installation instructions to make it more readable. Implemented in [#1063](https://github.com/utPLSQL/utPLSQL/pull/1063), resolves [#1062](https://github.com/utPLSQL/utPLSQL/issues/1062) +- Updated database requirements in documentation. Implemented in [#1065](https://github.com/utPLSQL/utPLSQL/pull/1065), resolves [#1064](https://github.com/utPLSQL/utPLSQL/issues/1064) +- Removed duplicated call to install profiler tables. Implemented in [#1164](https://github.com/utPLSQL/utPLSQL/pull/1164), resolves [#1149](https://github.com/utPLSQL/utPLSQL/issues/1149) +- Fixed failing internal framework tests on Oracle 21c. Implemented in [#1158](https://github.com/utPLSQL/utPLSQL/pull/1158), resolves [#1151](https://github.com/utPLSQL/utPLSQL/issues/1151) +- Fixed confusing typo in documentation. Resolves [#1154](https://github.com/utPLSQL/utPLSQL/issues/1154) +- Moved build process from travis-ci.org to travis-ci.com. Implemented in [#1152](https://github.com/utPLSQL/utPLSQL/pull/1152) +- Added an example of reporter reporting all expectations, not only the failing ones. Implemented in [#1092](https://github.com/utPLSQL/utPLSQL/pull/1092) + +--- + +## [v3.1.10] - 2020-02-23 + +## Enhancements + +- utPLSQL test runner is now validating arguments of `--%throws` annotations at runtime [#721](https://github.com/utPLSQL/utPLSQL/issues/721) [#1033](https://github.com/utPLSQL/utPLSQL/issues/1033) +- Documented limitations of insignificant spaces comparison in compound data [#880](https://github.com/utPLSQL/utPLSQL/issues/880) +- utPLSQL will now detect empty annotation cache for schema even with DLL trigger enabled [#975](https://github.com/utPLSQL/utPLSQL/issues/975) +- Order of procedures and annotation now determines default order of tests in suite [#1036](https://github.com/utPLSQL/utPLSQL/issues/1036) + +## Bug fixes + +- Nested contexts are now properly identified [#1034](https://github.com/utPLSQL/utPLSQL/issues/1034) +- TeamCity test reporter is now including error message [#1045](https://github.com/utPLSQL/utPLSQL/issues/1045) + +## Internal improvements + +- All self-tests for utPLSQL framework can now be executed using test-owner schema [#969](https://github.com/utPLSQL/utPLSQL/issues/969) +- Misleading rollback warning is no longer showing when running self-tests for utPLSQL [#982](https://github.com/utPLSQL/utPLSQL/issues/982) + +--- + +## [v3.1.9] - 2019-11-10 + +## New features + +- Added ability to define [nested contexts](https://github.com/utPLSQL/utPLSQL/blob/v3.1.9/docs/userguide/annotations.md#context) within test suite [#938](https://github.com/utPLSQL/utPLSQL/issues/938) +- Added ability to [exclude tests/suites by tags](https://github.com/utPLSQL/utPLSQL/blob/v3.1.9/docs/userguide/annotations.md#excluding-testssuites-by-tags) when invoking a test-run [#1007](https://github.com/utPLSQL/utPLSQL/issues/1007) [#983](https://github.com/utPLSQL/utPLSQL/issues/983) +- Added new annotation [`--%name`](https://github.com/utPLSQL/utPLSQL/blob/v3.1.9/docs/userguide/annotations.md#name) to allow for providing custom name for contexts [#1016](https://github.com/utPLSQL/utPLSQL/issues/1016) + +## Important changes + +The value of `--%context` annotation is no longer representing context `name`. +This value is now context description (displayname). +With this change, the `--%context` annotation is now aligned with `--%test` and `--%suite` annotation syntax. + +New annotation `--%name` was introduced to facilitate naming of contexts. + +## Enhancements + +- Improved documentation for running tests +- Improved documentation for tags [#1003](https://github.com/utPLSQL/utPLSQL/issues/1003) +- Improved documentation for annotations + +## Bug fixes + +- Fixed bug with bad stacktrace showing in failing/erroring test [#1000](https://github.com/utPLSQL/utPLSQL/issues/1000) +- Fixed issue with lack of validation for context name [#966](https://github.com/utPLSQL/utPLSQL/issues/966) +- Fixed problem with install script privilege check for installation with DDL trigger [#992](https://github.com/utPLSQL/utPLSQL/issues/992) +- Fixed issue with some common column names causing cursor comparison to fail [#997](https://github.com/utPLSQL/utPLSQL/issues/997) +- Fixed issue with invocation of standalone expectations on cursor [#998](https://github.com/utPLSQL/utPLSQL/issues/998) + +## Internal improvements + +- Fixed runability of utplsql self-tests [#968](https://github.com/utPLSQL/utPLSQL/issues/968) + +--- + +## [v3.1.8] - 2019-09-03 + +## New features + +- Added support for [session context](https://github.com/utPLSQL/utPLSQL/blob/v3.1.8/docs/userguide/annotations.md#sys_context) to provide test and suite information during test run [#963](https://github.com/utPLSQL/utPLSQL/issues/963) [#781](https://github.com/utPLSQL/utPLSQL/issues/781) +- Added ability to [invoke expectations without running framework](https://github.com/utPLSQL/utPLSQL/blob/v3.1.8/docs/userguide/expectations.md#running-expectations-outside-utplsql-framework) [#956](https://github.com/utPLSQL/utPLSQL/issues/956) [#963](https://github.com/utPLSQL/utPLSQL/issues/963) +- Failing expectations are now reported with call stack [#967](https://github.com/utPLSQL/utPLSQL/issues/967) [#963](https://github.com/utPLSQL/utPLSQL/issues/963) + +## Enhancements + +- Improved framework table private data protection [#922](https://github.com/utPLSQL/utPLSQL/issues/922) [#954](https://github.com/utPLSQL/utPLSQL/issues/954) +- Improved install process. It is now unified for installation with both public and private synonyms [#957](https://github.com/utPLSQL/utPLSQL/issues/957) [#954](https://github.com/utPLSQL/utPLSQL/issues/954) +- Improved reporting of warnings for integration with SQLDeveloper [#964](https://github.com/utPLSQL/utPLSQL/issues/964) +- Improved query to retrieve coverage sources [#981](https://github.com/utPLSQL/utPLSQL/issues/981) [#970](https://github.com/utPLSQL/utPLSQL/issues/970) +- Improved security around malicious utPLSQL owner name [#920](https://github.com/utPLSQL/utPLSQL/issues/920) + +## Bug fixes + +- Fixed cursor comparison on Oracle 11.2 [#947](https://github.com/utPLSQL/utPLSQL/issues/947) +- Fixed issue with retrieving suite data [#977](https://github.com/utPLSQL/utPLSQL/issues/977) [#974](https://github.com/utPLSQL/utPLSQL/issues/974) [#978](https://github.com/utPLSQL/utPLSQL/issues/978) +- Application context is now reset in session after test run [#951](https://github.com/utPLSQL/utPLSQL/issues/951) + +--- + +## [v3.1.7] - 2019-06-18 + +## New features + +- Added support for [comparing `json`](https://github.com/utPLSQL/utPLSQL/blob/v3.1.7/docs/userguide/expectations.md#comparing-json-objects) in Oracle 12.2 and above [#924](https://github.com/utPLSQL/utPLSQL/issues/924) +- Introduced [`tag` annotation](https://github.com/utPLSQL/utPLSQL/blob/v3.1.7/docs/userguide/annotations.md#tags) to enable tagging of tests and suites [#66](https://github.com/utPLSQL/utPLSQL/issues/66) +- Added support for [random order](https://github.com/utPLSQL/utPLSQL/blob/v3.1.7/docs/userguide/running-unit-tests.md#random-order) of test execution [#422](https://github.com/utPLSQL/utPLSQL/issues/422) + +## Enhancements + +- Added optional [install with DDL trigger](https://github.com/utPLSQL/utPLSQL/blob/v3.1.7/docs/userguide/install.md#installation-with-ddl-trigger) to speed up framework start [#901](https://github.com/utPLSQL/utPLSQL/issues/901) +- Removed dependency on `dbms_utility.name_resolve` [#569](https://github.com/utPLSQL/utPLSQL/issues/569) [#885](https://github.com/utPLSQL/utPLSQL/issues/885) +- New output buffer table structures improving performance and addressing timeout issues [#915](https://github.com/utPLSQL/utPLSQL/issues/915) + +## Bug fixes + +- Fixed `ut_realtime_reporter` missing warnings in test and suite output structures [#936](https://github.com/utPLSQL/utPLSQL/issues/936) +- Fixed output_buffer purging error [#934](https://github.com/utPLSQL/utPLSQL/issues/934) +- Fixed `join_by / exclude / include` invalid syntax on collection in anydata compare [#912](https://github.com/utPLSQL/utPLSQL/issues/912) +- Fixed `ut_junit_reporter` producing invalid output on failing tests with long failure message [#927](https://github.com/utPLSQL/utPLSQL/issues/927) +- Fixed `ut_sonar_test_reporter` producing invalid output on failing tests with long failure message [#925](https://github.com/utPLSQL/utPLSQL/issues/925) +- Fixed `ut_coverage_cobertura_reporter` producing wrong line breaks which breaks the xml validation against DTD [#917](https://github.com/utPLSQL/utPLSQL/issues/917) +- Fixed `exclude` option for ref cursor where column order was not resolved correctly [#911](https://github.com/utPLSQL/utPLSQL/issues/911) +- Fixed `unordered` option for ref cursor with null values [#914](https://github.com/utPLSQL/utPLSQL/issues/914) +- Fixed number precision when selecting from dual [#907](https://github.com/utPLSQL/utPLSQL/issues/907) +- Fixed ref cursor errors with generated column names [#902](https://github.com/utPLSQL/utPLSQL/issues/902) +- Fixed `ORA-00907` when comparing ref cursors with BINARY_ columns [#899](https://github.com/utPLSQL/utPLSQL/issues/899) +- Fixed wrong results when comparing CLOBs with `to_be_like` in Oracle Database 11.2.0.4 due to Oracle Bug 14402514 [#891](https://github.com/utPLSQL/utPLSQL/issues/891) +- Fixed performance issue with code coverage report on huge PL/SQL code base [#882](https://github.com/utPLSQL/utPLSQL/issues/882) + +## Documentation improvements + +- Added install instructions for DDL trigger [#874](https://github.com/utPLSQL/utPLSQL/issues/874) + +## Internal enhancements + +- Fixed SQL vulnerability on all input parameters used in dynamic SQL and PL/SQL [#921](https://github.com/utPLSQL/utPLSQL/issues/921) +- Fixed message id in output buffer [#916](https://github.com/utPLSQL/utPLSQL/issues/916) +- Included 19c database in self testing [#909](https://github.com/utPLSQL/utPLSQL/issues/909) +- Introduced testing with multiple schemas and different grants [#893](https://github.com/utPLSQL/utPLSQL/issues/893) +- Fixed installation script warnings [#879](https://github.com/utPLSQL/utPLSQL/issues/879) + +--- + +## [v3.1.6] - 2019-03-24 + +Bugfix release for `v3.1.5` + +## Bug fixes + +- Fixed a bug in release `3.1.5` where `to_equal` matcher was failing due to privileges when comparing non sql diffable types [#870](https://github.com/utPLSQL/utPLSQL/issues/870) + +## Improvements +- Reduced number of information displaying about user defined type. We will now display only type name instead of full structure [#866](https://github.com/utPLSQL/utPLSQL/issues/866) + +--- + +## [v3.1.5] - 2019-03-20 + +Bugfix release for `v3.1.4` + +## Bug fixes + +- Fixed a bug in release `3.1.4` where `to_be_empty` matcher was failing due to privileges [#864](https://github.com/utPLSQL/utPLSQL/issues/864) + +--- + +## [v3.1.4] - 2019-03-19 + +**This release contains a bug that is fixed by release 3.1.5** +Please use release 3.1.5 rather than this release. + +## New features + +- Added `to_contain` matcher for collections and cursors [#79](https://github.com/utPLSQL/utPLSQL/issues/79) +- Added `unordered_columns` (`uc`) option for cursor comparison to ignore the order of the columns [#779](https://github.com/utPLSQL/utPLSQL/issues/779) +- Added `ut_debug_reporter` for debug logging [#480](https://github.com/utPLSQL/utPLSQL/issues/480) +- Added `ut_realtime_reporter` for utPLSQL-SQLDeveloper extension [#795](https://github.com/utPLSQL/utPLSQL/issues/795) + +## Important Changes + +- Due to improvements of the cursor comparison, it is now necessary to use `ut.set_nls()` before creating the cursor and `ut.reset_nls()` after the __expectation__ when comparing dates. [More info in the docs](https://github.com/utPLSQL/utPLSQL/blob/v3.1.4/docs/userguide/expectations.md#comparing-cursor-data-containing-date-fields) + +## Enhancements + +- Improved performance of cursor comparison [#780](https://github.com/utPLSQL/utPLSQL/issues/780) +- Added support for installation on databases with block size < 8KB [#848](https://github.com/utPLSQL/utPLSQL/issues/848) +- Added initial timeout to `ut_output_buffer` [#840](https://github.com/utPLSQL/utPLSQL/issues/840) +- Enhanced performance of `get_reporters_list` function [#814](https://github.com/utPLSQL/utPLSQL/issues/814) +- Moved calls of `dbms_lock.sleep` to `dbms_session` for newer DB versions [#806](https://github.com/utPLSQL/utPLSQL/issues/806) +- utPLSQL coverage will now work without re-install after DB-upgrade from 12.1 to 12.2 [#803](https://github.com/utPLSQL/utPLSQL/issues/803) + +## Bug fixes + +- Fixed problem with REGEXP in annotation parsing with NLS CANADIAN FRENCH [#844](https://github.com/utPLSQL/utPLSQL/issues/844) +- Fixed issue with Rollback to savepoint failing on distributed transaction [#839](https://github.com/utPLSQL/utPLSQL/issues/839) +- Fixed reporting of differences when comparing collections scalar values [#835](https://github.com/utPLSQL/utPLSQL/issues/835) +- Fixed issue with test run failing due to too many transaction invalidators [#834](https://github.com/utPLSQL/utPLSQL/issues/834) +- Fixed randomly occurring error during cursor comparison [#827](https://github.com/utPLSQL/utPLSQL/issues/827) +- utPLSQL install script will now support special characters in passwords [#804](https://github.com/utPLSQL/utPLSQL/issues/804) + +## Documentation improvements + +- Fixed documentation examples for context annotation [#851](https://github.com/utPLSQL/utPLSQL/issues/851) +- Added description on how to check version of utPLSQL [#822](https://github.com/utPLSQL/utPLSQL/issues/822) + +## Internal enhancements + +- Implemented Sonar analysis on DBA Views [#850](https://github.com/utPLSQL/utPLSQL/issues/850) +- Finished migration from old-tests [#475](https://github.com/utPLSQL/utPLSQL/issues/475) +- Fixed shell scripts to support multiple unix dialects (especially for macOS) [#796](https://github.com/utPLSQL/utPLSQL/issues/796) +- Added info on project support from Redgate [#841](https://github.com/utPLSQL/utPLSQL/issues/841) +- Added `code_of_conduct` [#836](https://github.com/utPLSQL/utPLSQL/issues/836) +- Added issue templates [#842](https://github.com/utPLSQL/utPLSQL/issues/842) +- Added utPLSQL logo [#845](https://github.com/utPLSQL/utPLSQL/issues/845) + +--- + +## [v3.1.3] - 2018-11-20 + +## New features + +- added function `ut_runner.is_test` [#788](https://github.com/utPLSQL/utPLSQL/issues/788) +- added function `ut_runner.is_suite` [#787](https://github.com/utPLSQL/utPLSQL/issues/787) +- added function `ut_runner.has_suites` [#786](https://github.com/utPLSQL/utPLSQL/issues/786) +- added ability to disable automatic rollback for a test-run [#784](https://github.com/utPLSQL/utPLSQL/issues/784) +- when invoked with package name, utPLSQL will now run only tests from specified package even if package has child packages by suitepath [#776](https://github.com/utPLSQL/utPLSQL/issues/776) + +## Enhancements + +- Improved performance of schema-scanning and utPLSQL startup [#778](https://github.com/utPLSQL/utPLSQL/issues/778) +- Improved performance of output-buffer [#777](https://github.com/utPLSQL/utPLSQL/issues/777) +- Improved documentation to mention ability to pass client encoding for HTML & XML reports [#775](https://github.com/utPLSQL/utPLSQL/issues/775) +- Improved documentation for cursor comparison to mention challenges with TIMESTAMP bind variables + +## Bug-fixes + +- utPLSQL code coverage will now work properly with long object names [#716](https://github.com/utPLSQL/utPLSQL/issues/716) +- utPLSQL installation will now also work properly, when user performing the install has `ANY` grants [#737](https://github.com/utPLSQL/utPLSQL/issues/737) +- fixed documentation bug for `--%context` with `--%displayname` [#726](https://github.com/utPLSQL/utPLSQL/issues/726) +- fixed Teamcity reporter issues with missing escape for some characters and long messages [#747](https://github.com/utPLSQL/utPLSQL/issues/747) +- fixed issue with sonar test results reporter when contexts are used [#749](https://github.com/utPLSQL/utPLSQL/issues/749) +- fixed issue with ORA-07455 getting thrown on cursor comparison [#752](https://github.com/utPLSQL/utPLSQL/issues/752) +- fixed issue with wrong failure message for unordered data [#764](https://github.com/utPLSQL/utPLSQL/issues/764) +- fixed missing privilege issue for unordered/join-by cursor data comparison [#765](https://github.com/utPLSQL/utPLSQL/issues/765) [#770](https://github.com/utPLSQL/utPLSQL/issues/770) + +## Internal enhancements + +- added suite-level cache to allow for faster retrieval of suite contents and enable implementation of additional features [#783](https://github.com/utPLSQL/utPLSQL/issues/783) + +--- + +## [v3.1.2] - 2018-07-22 + +## New features + +- Added ability to join and compare cursor content by specific columns (PK/UK) [#453](https://github.com/utPLSQL/utPLSQL/issues/453) +- Added support for comma separated list of suite paths/packages when calling `ut.run` [#479](https://github.com/utPLSQL/utPLSQL/issues/479) +- Added ability to run a test package that got invalidated due to dependency invalidation [#489](https://github.com/utPLSQL/utPLSQL/issues/489) +- Added support for package level constants and predefined exceptions in `--%throws` annotation [#685](https://github.com/utPLSQL/utPLSQL/issues/685) +- Added support for standalone `--%beforeall`, `--%beforeeach`, `--%afterall`, `--%aftereach` annotations with list of procedures to execute [#649](https://github.com/utPLSQL/utPLSQL/issues/649) +- Added support for list of procedure names in before/after annotations [#649](https://github.com/utPLSQL/utPLSQL/issues/649) +- Added support for BLOB/CLOB in `is_empty()` matcher [#707](https://github.com/utPLSQL/utPLSQL/issues/707) + +## Enhancements + +- utPLSQL will now provide additional warnings, when unsupported annotations are found in a unit test suite package [#624](https://github.com/utPLSQL/utPLSQL/issues/624) +- utPLSQL will now produce valid XML in UT_JUNIT_REPORTER when dbms_output or test results contain ` **Important notice:** > We do our best to keep utPLSQL covered with unit tests. @@ -145,8 +145,8 @@ Every new functionality needs to be documented by unit tests that cover both hap To run a full suite of unit tests execute: ```bash -development/env.sh -test/install_and_run_tests.sh +. ./development/env.sh +./test/install_and_run_tests.sh ``` You can review the results of tests as well as see information about code coverage in `./coverage.html` file. @@ -162,45 +162,20 @@ Dependencies to `ut_util` are not shown as most of modules are depending on it. ## Build Environment -We are using private docker images to test utPLSQL for our Travis CI builds. The following versions of Oracle Database are being used. +We are using a combination of private and publicly available docker images to test utPLSQL. The following versions of Oracle Database are used for testing: -* 11g XE R2 -* 12c SE R1 -* 12c SE R2 -* 18c SE +* 19c-Standard Edition (private image) +* 21-free +* 23-free -These images are based on the slimmed versions [official dockerfiles released by Oracle](https://github.com/utPLSQL/docker-scripts), but due to licensing restrictions, we can't make the images public. -You can build your own and use it locally, or push to a private docker repository. - -The build steps are simple if you already have some experience using Docker. You can find detailed information about how to build your own image with a running database in: [example of creating an image with pre-built DB](https://github.com/oracle/docker-images/blob/master/OracleDatabase/samples/prebuiltdb/README.md) - -> You can find more info about the official Oracle images on the [Oracle Database on Docker](https://github.com/oracle/docker-images/tree/master/OracleDatabase) GitHub page. - -> If you are new to Docker, you can start by reading the [Getting Started With Docker](https://docs.docker.com/engine/getstarted/) docs. - -### Docker Build Notes - -* You need to comment out the VOLUME line. This step is required, because volumes are not saved when using `docker commit` command. -* When the build process is complete, you will run the container to install the database. Once everything is set up and you see the message "DATABASE IS READY!", you may change the password and stop the running container. After the container is stopped, you can safely commit the container. -* You can use the --squash experimental docker tag to reduce the image size. Example: -``` -docker build --force-rm --no-cache --squash -t oracle/db-prebuilt . -``` - -Travis will use your Docker Hub credentials to pull the private images, and the following secure environment variables must be defined. - -Variable | Description ----------|------------ -**DOCKER_USER**
**DOCKER_PASSWORD** | _Your Docker Hub website credentials. They will be used to pull the private database images._ +The Free varsions are publicly available and you can set up your local development environment to use one of from [here](https://github.com/gvenzl/oci-oracle-free) -### SQLCL +These [private image]((https://github.com/utPLSQL/docker-scripts) is a slimmed version of [the official Oracle database dockerfiles](https://github.com/oracle/docker-images/tree/master/OracleDatabase) . Due to licensing restrictions, the image cannot be public. -Our build configuration uses SQLCL to run the scripts, and you need to configure a few additional secure environment variables. After the first build, the downloaded file will be cached. - -Variable | Description ----------|------------ -**ORACLE_OTN_USER
ORACLE_OTN_PASSWORD** | _Your Oracle website credentials. They will be used to download SQLCL._ +You can build your own and use it locally, or push to a private docker repository. +The build steps are simple if you already have some experience using Docker. You can find detailed information about how to build your own image with a running database in: [example of creating an image with pre-built DB](https://github.com/oracle/docker-images/blob/master/OracleDatabase/samples/prebuiltdb/README.md) +> If you are new to Docker, you can start by reading the [Getting Started With Docker](https://docs.docker.com/get-started/) docs. ## New to GIT diff --git a/VERSION b/VERSION index 571f2ad11..19db2fb66 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v3.1.11-develop +v3.2.4-develop diff --git a/development/cleanup.sh b/development/cleanup.sh index c13ddb4aa..d36f21540 100755 --- a/development/cleanup.sh +++ b/development/cleanup.sh @@ -1,9 +1,9 @@ -#!/usr/bin/env bash +#!/bin/bash #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -. development/env.sh +. ./development/env.sh "${SQLCLI}" sys/${ORACLE_PWD}@//${CONNECTION_STR} AS SYSDBA <<-SQL set echo on @@ -20,20 +20,22 @@ end; drop user ${UT3_DEVELOP_SCHEMA} cascade; 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_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; +set serveroutput on begin for i in ( select decode(owner,'PUBLIC','drop public synonym "','drop synonym "'||owner||'"."')|| synonym_name ||'"' drop_orphaned_synonym, owner||'.'||synonym_name syn from dba_synonyms a where not exists (select 1 from dba_objects b where (a.table_name=b.object_name and a.table_owner=b.owner or b.owner='SYS' and a.table_owner=b.object_name) ) - and a.table_owner not in ('SYS','SYSTEM') + and a.table_owner not in (select username from dba_users where oracle_maintained = 'Y') + and a.origin_con_id = sys_context('userenv', 'con_id') ) loop execute immediate i.drop_orphaned_synonym; dbms_output.put_line('synonym '||i.syn||' dropped'); diff --git a/development/install.sh b/development/install.sh index ff112392f..468bafd38 100755 --- a/development/install.sh +++ b/development/install.sh @@ -1,20 +1,24 @@ -#!/usr/bin/env bash +#!/bin/bash #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -. development/env.sh +. ./development/env.sh header="******************************************************************************************" if ! development/cleanup.sh; then echo -e ${header}"\nFailed to cleanup utPLSQL environment\n"${header} exit 1 fi -if ! .travis/install.sh; then +if ! .github/scripts/install.sh; then echo -e ${header}"\nFailed to install utPLSQL from current branch into ${UT3_DEVELOP_SCHEMA} schema\n"${header} exit 1 fi -if ! .travis/install_utplsql_release.sh; then +if ! .github/scripts/create_test_users.sh; then + echo -e ${header}"\nFailed to create test users from current branch\n"${header} + exit 1 +fi +if ! .github/scripts/install_utplsql_release.sh; then echo -e ${header}"\nFailed to install utPLSQL from branch ${SELFTESTING_BRANCH} into ${UT3_RELEASE_VERSION_SCHEMA}\n"${header} exit 1 fi diff --git a/development/refresh_sources.sh b/development/refresh_sources.sh index 7af3aaec0..87869d581 100755 --- a/development/refresh_sources.sh +++ b/development/refresh_sources.sh @@ -1,18 +1,19 @@ -#!/usr/bin/env bash +#!/bin/bash #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -. development/env.sh +. ./development/env.sh # remove sub-direcotry containing main branch shallow copy rm -rf ${UTPLSQL_DIR:-utPLSQL_latest_release} # clone utPLSQL main branch from upstream into utPLSQL sub-directory of your project git clone --depth=1 --branch=${SELFTESTING_BRANCH:-main} https://github.com/utPLSQL/utPLSQL.git ${UTPLSQL_DIR:-utPLSQL_latest_release} +rm -rf ${UTPLSQL_DIR:-utPLSQL_latest_release}/.git rm -rf utPLSQL-cli/* # download latest release version of utPLSQL-cli -curl -Lk -o utPLSQL-cli.zip https://github.com/utPLSQL/utPLSQL-cli/releases/download/v${UTPLSQL_CLI_VERSION}/utPLSQL-cli.zip +curl -Lk -o utPLSQL-cli.zip https://github.com/utPLSQL/utPLSQL-cli/releases/download/${UTPLSQL_CLI_VERSION}/utPLSQL-cli.zip # unzip utPLSQL-cli and remove the zip file unzip utPLSQL-cli.zip && chmod u+x utPLSQL-cli/bin/utplsql && rm utPLSQL-cli.zip diff --git a/development/refresh_ut3.sh b/development/refresh_ut3.sh index 570d3c86a..827af827d 100755 --- a/development/refresh_ut3.sh +++ b/development/refresh_ut3.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" diff --git a/development/releasing.md b/development/releasing.md index f85e18c3c..99c3f7114 100644 --- a/development/releasing.md +++ b/development/releasing.md @@ -1,29 +1,25 @@ -## Release process +# 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, follow the below steps. -## To create a release +## Release preparation + - Create a **draft** of a Release with a new tag number `vX.Y.X` sourced from the `develop` branch on [github releases page](https://github.com/utPLSQL/utPLSQL/releases) + - Populate release description using the `Generate release notes` button + - Review the auto-generated release notes and update tem if needed + - Optionally, split the default `## What's Changed` list into `## New features`, `## Enhancements`, `## Bug fixes`. See previous release notes for details - - 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 - - merge the release branch to main and wait for main build to complete successfully (do not use Squash/rebase for merge operation) - - create a Github release from the main branch using [github releases page](https://github.com/utPLSQL/utPLSQL/releases) and populate release description using information found on the issues and pull requests since previous release. - To find issues closed after certain date use [advanced filters](https://help.github.com/articles/searching-issues-and-pull-requests/#search-by-open-or-closed-state). - Example: [`is:issue closed:>2018-07-22`](https://github.com/utPLSQL/utPLSQL/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%3E2018-07-22+) - - After A build was completed on a TAG (github release) was successful, merge main branch back into develop branch. - - At this point, main branch and release tag should be at the same commit version and artifacts should be uploaded into Github release. - - After develop branch was built, update version number in `VERSION` file to represent next planned release version. - - Clone `utplsql.githug.io` project and add a new announcement about next version being released in `_posts`. Use previous announcements as a template. Make sure to set date, time and post title properly. +## Performing a release + - Publish [the previously prepared](#release-preparation) release draft. + - Wait for the [Github Actions `Release`](https://github.com/utPLSQL/utPLSQL/actions/workflows/release.yml) process to complete successfully. The process will do the following: + - create a new tag + - generate html documentation to be included in the release file. + - publish documentation for the new version into utPLSQL site + - upload release artifacts (`zip` and `tar.gz` files along with `sha256` files) + - update the CHANGELOG.md file with the release details + - publish an announcement about the release in the utPLSQL Organization Discussions + - publish a release post on utplsql.github.io (new post in `docs/_posts`, updated `mkdocs.yml` nav and `docs/index.md`) + - After the release process is finished, manually increase the version number in the `VERSION` file on `develop` branch to start the next release version. + - Send an announcement about the release on:Twitter(X), BlueSky, LinkedIn account about the utPLSQL release. -The following will happen: - - build executed on branch `release/vX.Y.Z-[something]` updates files `sonar-project.properties`, `VERSION` with project version derived from the release branch name - - changes to those two files are committed and pushed back to release branch by Travis - - builds on main branch are **not getting executed** - - when a Github release is created, a new tag is added in on the repository and a tag build is executed - - the documentation for new release is published on `utplsql.github.io` and installation archives are added to the tag. - -Note: -The sources for release are provided in separate zip files delivered from the Travis build process. -The built zip files include HTML documentation generated from MD files. +# Note: +The release artifacts include HTML documentation generated from MD files, sources and tests diff --git a/development/template.env.sh b/development/template.env.sh index 5e1aaad1a..fdbed4b6b 100755 --- a/development/template.env.sh +++ b/development/template.env.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/bin/bash -export SQLCLI=sql # For sqlcl client -#export SQLCLI=sqlplus # For sqlplus client -export CONNECTION_STR=127.0.0.1:1521/xe # Adjust the connect string -export ORACLE_PWD=oracle # Adjust your local SYS password -export UTPLSQL_CLI_VERSION="3.1.6" +export SQLCLI=sql # For sqlcl client +#export SQLCLI=sqlplus # For sqlplus client +export CONNECTION_STR=127.0.0.1:1521/xe # Adjust the connect string +export ORACLE_PWD=oracle # Adjust your local SYS password +export UTPLSQL_CLI_VERSION="v3.1.8" export SELFTESTING_BRANCH=develop export UTPLSQL_DIR="utPLSQL_latest_release" @@ -12,10 +12,4 @@ export UT3_DEVELOP_SCHEMA=UT3_DEVELOP export UT3_DEVELOP_SCHEMA_PASSWORD=ut3 export UT3_RELEASE_VERSION_SCHEMA=UT3 export UT3_RELEASE_VERSION_SCHEMA_PASSWORD=ut3 -export UT3_TESTER=UT3_TESTER -export UT3_TESTER_PASSWORD=ut3 -export UT3_TESTER_HELPER=UT3_TESTER_HELPER -export UT3_TESTER_HELPER_PASSWORD=ut3 export UT3_TABLESPACE=users -export UT3_USER="UT3\$USER#" -export UT3_USER_PASSWORD=ut3 diff --git a/docs/about/authors.md b/docs/about/authors.md deleted file mode 100644 index 8fb6b36a4..000000000 --- a/docs/about/authors.md +++ /dev/null @@ -1,26 +0,0 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -### utPLSQL v3 Major Contributors - -**Listed Alphabetically** - -| Name | GitHub account -| ---------------- | -------------- -| David Pyke | [Shoelace](https://github.com/Shoelace) -| Jacek Gebal | [jgebal](https://github.com/jgebal) -| Lukasz Wasylow | [lwasylow](https://github.com/lwasylow/) -| Pavel Kaplya | [Pazus](https://github.com/Pazus) -| Robert Love | [rlove](https://github.com/rlove) -| Samuel Nitsche | [pesse](https://github.com/pesse/) -| Vinicius Avellar | [viniciusam](https://github.com/viniciusam/) - - - -Many thanks to all the [contributors](https://github.com/utPLSQL/utPLSQL/graphs/contributors) - -### Special thanks to prior major contributors - -- Steven Feuerstein - Original Author -- Chris Rimmer -- Patrick Barel -- Paul Walker diff --git a/docs/about/license.md b/docs/about/license.md deleted file mode 100644 index 9e97f9a38..000000000 --- a/docs/about/license.md +++ /dev/null @@ -1,17 +0,0 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# Version Information - -**utPLSQL version 3** is licensed under -[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) - -External code used in the development of this project, but is not required for use. - -| Tool | License | Purpose | -| ---- | --------| --------| -| [Travis-Oracle](https://github.com/cbandy/travis-oracle) | [ISC](https://github.com/cbandy/travis-oracle/blob/master/LICENSE) | Install Oracle for Travis Builds | -| [mkDocs](http://www.mkdocs.org/) | [BSD](http://www.mkdocs.org/about/license/) | Produce HTML version of documentation | - -**Note:** - -Version 1 & 2 of utPLSQL were licensed under GPL, version 3 was a complete rewrite from scratch which a allowed us to change the license to be more permissive. diff --git a/docs/about/project-details.md b/docs/about/project-details.md deleted file mode 100644 index 3576d6137..000000000 --- a/docs/about/project-details.md +++ /dev/null @@ -1,18 +0,0 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# utPLSQL Project Details - -[![chat](http://img.shields.io/badge/GitHub_Project-Active-blue.svg)](https://github.com/utPLSQL/utPLSQL) -[![license](http://img.shields.io/badge/license-apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -[![chat](http://img.shields.io/badge/chat-slack-blue.svg)](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA) -[![twitter](https://img.shields.io/twitter/follow/utPLSQL.svg?style=social&label=Follow)](https://twitter.com/utPLSQL) - - -utPLSQL is open source project [hosted on GitHub](https://github.com/utPLSQL/utPLSQL). - -Contributions, help and constructive feedback is always appreciated. - -If you are interested in helping please read our [contributing guide](https://github.com/utPLSQL/utPLSQL/blob/develop/CONTRIBUTING.md) - - - diff --git a/docs/about/support.md b/docs/about/support.md deleted file mode 100644 index 1acbd4f25..000000000 --- a/docs/about/support.md +++ /dev/null @@ -1,6 +0,0 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# How to get support - -- Feel free to post questions, bugs or issues in the [issues area of GitHub](https://github.com/utPLSQL/utPLSQL/issues) -- [Join](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA) developers team on utPLSQL [Slack](https://utplsql.slack.com/) diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png new file mode 100644 index 000000000..7c34109d0 Binary files /dev/null and b/docs/assets/favicon.png differ diff --git a/docs/assets/icon-transparent.png b/docs/assets/icon-transparent.png new file mode 100644 index 000000000..6e4534aad Binary files /dev/null and b/docs/assets/icon-transparent.png differ diff --git a/docs/compare_version2_to_3.md b/docs/compare_version2_to_3.md index 5f53e41d4..121fafc59 100644 --- a/docs/compare_version2_to_3.md +++ b/docs/compare_version2_to_3.md @@ -37,7 +37,6 @@ There is a [migration tool](https://github.com/utPLSQL/utPLSQL-v2-v3-migration) | **Code Coverage Reporting** | No | Yes | | Html Coverage Report | No | Yes | | Sonar XML Coverage Report | No | Yes | -| Coveralls Json Coverage Report | No | Yes | | Framework Transaction Control | No | Yes - Annotations1 | | **Test Output** | | | | Real-time test execution progress reporting | No | Yes | diff --git a/docs/images/tap_reporter_colored.png b/docs/images/tap_reporter_colored.png new file mode 100644 index 000000000..d4a5e9326 Binary files /dev/null and b/docs/images/tap_reporter_colored.png differ diff --git a/docs/images/tap_reporter_no_color.png b/docs/images/tap_reporter_no_color.png new file mode 100644 index 000000000..e7379a889 Binary files /dev/null and b/docs/images/tap_reporter_no_color.png differ diff --git a/docs/images/tap_reporter_suitepath.png b/docs/images/tap_reporter_suitepath.png new file mode 100644 index 000000000..488c7cfbf Binary files /dev/null and b/docs/images/tap_reporter_suitepath.png differ diff --git a/docs/index.md b/docs/index.md index a2c178ad7..a7255f8f8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,39 +1,23 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) -# Introduction to utPLSQL +## What is utPLSQL utPLSQL is a Unit Testing framework for Oracle PL/SQL. The framework follows industry standards and best patterns of modern Unit Testing frameworks like [JUnit](http://junit.org/junit4/) and [RSpec](http://rspec.info/) + +## Demo project + +Have a look at [utPLSQL demo project](https://github.com/utPLSQL/utPLSQL-demo-project/) to see: + +- sample code and tests +- demo of deployment automation that leverages: + - Flyway / Liquidbase for scripting and deployment of DB changes + - Docker container with Oracle XE Database + - GitHub Actions and Azure Pipelines to orchestrate the deployment and testing process + - utPLSQL framework for writhing, execution of tests as well as reporting test results and code coverage + - [Sonar]((https://sonarcloud.io/project/overview?id=utPLSQL:utPLSQL-demo-project).) for code quality gate, test results and code coverage reporting - - User Guide - - [Installation](userguide/install.md) - - [Getting Started](userguide/getting-started.md) - - [Annotations](userguide/annotations.md) - - [Expectations](userguide/expectations.md) - - [Advanced data comparison](userguide/advanced_data_comparison.md) - - [Running unit tests](userguide/running-unit-tests.md) - - [Querying for test suites](userguide/querying_suites.md) - - [Testing best practices](userguide/best-practices.md) - - [Upgrade utPLSQL](userguide/upgrade.md) - - Reporting - - [Using reporters](userguide/reporters.md) - - [Reporting errors](userguide/exception-reporting.md) - - [Code coverage](userguide/coverage.md) - - [Cheat-sheet](https://www.cheatography.com/jgebal/cheat-sheets/utplsql-v3-1-2/#downloads) - - About - - [Project Details](about/project-details.md) - - [License](about/license.md) - - [Support](about/support.md) - - [Authors](about/authors.md) - - [Version 2 to Version 3 Comparison](compare_version2_to_3.md) - -# Demo project - -Have a look at our [demo project](https://github.com/utPLSQL/utPLSQL-demo-project/). - -It uses [Travis CI](https://travis-ci.org/utPLSQL/utPLSQL-demo-project) to build on every commit, runs all tests, publishes test results and code coverage to [SonarQube](https://sonarqube.com/dashboard?id=utPLSQL%3AutPLSQL-demo-project%3Adevelop). - -# Three steps +## Three steps With just three simple steps you can define and run your unit tests for PLSQL code. @@ -48,11 +32,12 @@ Here is how you can simply create tested code, unit tests and execute the tests Check out the sections on [annotations](userguide/annotations.md) and [expectations](userguide/expectations.md) to see how to define your tests. -# Command line +## Command line You can use the utPLSQL command line client [utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli) to run tests without the need for Oracle Client or any IDE like SQLDeveloper/TOAD etc. Amongst many benefits they provide ability to: + * see the progress of test execution for long-running tests - real-time reporting * use many reporting formats simultaneously and save reports to files (publish) * map your project source files and test files into database objects @@ -60,10 +45,9 @@ Amongst many benefits they provide ability to: Download the [latest client](https://github.com/utPLSQL/utPLSQL-cli/releases/latest) and you are good to go. See [project readme](https://github.com/utPLSQL/utPLSQL-cli/blob/develop/README.md) for details. -# Coverage - -If you want to have code coverage gathered on your code , it's best to use `ut_run` to execute your tests with multiple reporters and have both test execution report as well as coverage report saved to a file. +## Coverage +It is best to use utPLSQL-cli or execute tests and gather code coverage from command line. Check out the [coverage documentation](userguide/coverage.md) for options of coverage reporting diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..fdfe1a3be --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{# + Sub-repository template override. + Loads the shared utPLSQL.org top navigation bar from the central org site. + The topbar.js file is the single source of truth — update nav links there only. +#} + +{% block extrahead %} + {{ super() }} + + +{% endblock %} diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 000000000..7f05ab831 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,12 @@ +[data-md-color-scheme="default"] { + --md-primary-fg-color: #2f8bff; + --md-accent-fg-color: #1f5db0; + --md-accent-fg-color--transparent: #1f5db0; +} + +[data-md-color-scheme="slate"] { + --md-hue: 200; + --md-primary-fg-color: #2f8bff; + --md-accent-fg-color: #1f5db0; + --md-accent-fg-color--transparent: #1f5db0; +} diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index 7b85ca542..413cf27b3 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.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) # Advanced data comparison @@ -48,7 +48,7 @@ When specifying column/attribute names, keep in mind that the names are **case s ## Excluding elements from data comparison Consider the following examples -```sql +```sql linenums="1" declare l_expected sys_refcursor; l_actual sys_refcursor; @@ -86,7 +86,7 @@ The actual data is equal/contains expected, when those columns are excluded. ## Selecting columns for data comparison Consider the following example -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -125,7 +125,7 @@ The actual data is equal/contains expected, when only those columns are included You can chain the advanced options in an expectation and mix the `varchar2` with `ut_varchar2_list` arguments. When doing so, the final list of items to include/exclude will be a concatenation of all items. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -163,7 +163,7 @@ SUCCESS Example of `include / exclude` for anydata.convertCollection -```sql +```sql linenums="1" create or replace type person as object( name varchar2(100), age integer @@ -219,7 +219,7 @@ Unordered option allows for quick comparison of two compound data types without Result of such comparison will be limited to only information about row existing or not existing in given set without actual information about exact differences. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -250,8 +250,9 @@ FAILURE at "anonymous block", line 15 ``` **Note** -> `join_by` matcher is much faster on performing data comparison, consider using `join_by` over unordered -> `contain` matcher is not considering order of compared data-sets. Using `unordered` makes no difference (it's default) +> Consider using `join_by( columns... )` over `unordered()` with the `equal` matcher. The `join_by` method is much faster at performing data comparison. +> +> The `contain` matcher is not considering the order of the compared data-sets. Using `unordered` makes no difference (it's default). ## Join By option @@ -266,7 +267,7 @@ The extra or missing rows will be presented to user as well as all non-matching Join by option can be used in conjunction with include or exclude options. However if any of the join keys is part of exclude set, comparison will fail and report to user that sets could not be joined on specific key, as the key was excluded. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -304,7 +305,7 @@ FAILURE You can use `join_by` syntax in combination with `contain` matcher. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -321,7 +322,7 @@ end; ``` Above test will indicate that in actual data-set -```sql +```sql linenums="1" FAILURE Actual: refcursor [ count = 28 ] was expected to contain: refcursor [ count = 29 ] Diff: @@ -334,7 +335,7 @@ FAILURE You can specify multiple columns in `join_by` -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -370,7 +371,7 @@ To reference attribute as PK, use slash symbol `/` to separate nested elements. In the below example, cursors are joined using the `NAME` attribute of object in column `SOMEONE` -```sql +```sql linenums="1" create or replace type person as object( name varchar2(100), age integer @@ -413,7 +414,7 @@ FAILURE > `join_by` does not support joining on individual elements of nested table. You can still use data of the nested table as a PK value. > When collection is referenced in `join_by`, test will fail with appropriate message, as it cannot perform a join. -```sql +```sql linenums="1" create or replace type person as object( name varchar2(100), age integer @@ -462,7 +463,7 @@ You may provide items for `include`/`exclude`/`join_by` as a a ut_varchar2_list - nested table and varray items type attributes are nested under `` elements Example of a valid parameter to include columns: `RN`, `A_Column`, `SOME_COL` in data comparison. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -493,7 +494,7 @@ Expectations that compare compound data type data with `unordered_columns` optio This option can be useful whn we have no control over the ordering of the column or the column order is not of importance from testing perspective. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 7d6801320..838fc0890 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1,31 +1,30 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# Annotations +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) Annotations are used to configure tests and suites in a declarative way similar to modern OOP languages. This way, test configuration is stored along with the test logic inside the test package. No additional configuration files or tables are needed for test cases. The annotation names are based on popular testing frameworks such as JUnit. The framework runner searches for all the suitable annotated packages, automatically configures suites, forms the suite hierarchy, executes it and reports results in specified formats. Annotation is defined by: + - single line comment `--` (double hyphen) - followed directly by a `%` (percent) - followed by annotation name - followed by optional annotation text placed in single brackets. -All of text between first opening bracket and last closing bracket in annotation line is considered to be annotation text +All text between first opening bracket and last closing bracket in annotation line is considered to be annotation text -Examples: -`--%suite(The name of my test suite)` - represents `suite` annotation with text `The name of my test suite` +For example `--%suite(The name of my test suite)` represents `suite` annotation with `The name of my test suite` as annotation text. -utPLSQL interprets the whole line of annotation and will treat all the text from the first opening bracket in the line to the last closing bracket +utPLSQL interprets the whole line of annotation and will treat text from the first opening bracket in the line to the last closing bracket in that line as annotation text -Example: - `--%suite(Stuff) -- we should name this ( correctly )` - represents `suite` annotation with text `Stuff) -- we should name this ( correctly ` +In below example we have a `suite` annotation with `Stuff) -- we should name this ( correctly ` as the annotation text + +`--%suite(Stuff) -- we should name this ( correctly )` Do not place comments within annotation line to avoid unexpected behaviors. -**Note:** ->Annotations are interpreted only in the package specification and are case-insensitive. We strongly recommend using lower-case annotations as described in this documentation. +!!! note + Annotations are interpreted only in the package specification and are case-insensitive. We strongly recommend using lower-case annotations as described in this documentation. There are two distinct types of annotations, identified by their location in package. - package annotations @@ -38,7 +37,7 @@ There **can not** be any empty lines or comments between annotation line and pro There can be many annotations for a procedure. Valid procedure annotations example: -```sql +```sql linenums="1" package test_package is --%suite @@ -47,7 +46,7 @@ package test_package is --%disabled procedure my_first_procedure; - $if dbms_db_version.version >= 12 $then --This is ok - annotation before procedure + $if dbms_db_version.version >= 19 $then --This is ok - annotation before procedure --%test() procedure my_first_procedure; $end @@ -59,7 +58,7 @@ end; ``` Invalid procedure annotations examples: -```sql +```sql linenums="1" package test_package is --%suite @@ -72,7 +71,7 @@ package test_package is procedure proc1; --%test() --This is wrong as there is a compiler directive between procedure and annotation - $if dbms_db_version.version >= 12 $then + $if dbms_db_version.version >= 19 $then procedure proc_12; $end @@ -90,7 +89,7 @@ Those annotations placed at any place in package except directly before procedur We strongly recommend putting package level annotations at the very top of package except for the `--%context` annotations (described below) Valid package annotations example: -```sql +```sql linenums="1" package test_package is --%suite @@ -108,7 +107,7 @@ end; ``` Invalid package annotations examples: -```sql +```sql linenums="1" package test_package is --%suite --This is wrong as suite annotation is not a procedure annotation procedure irrelevant; @@ -122,29 +121,29 @@ end; ## Supported annotations -| Annotation |Level| Description | -| --- | --- | --- | -| `--%suite()` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | -| `--%suitepath()` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | -| `--%displayname()` | Package/procedure | Human-readable and meaningful description of a context/suite/test. Overrides the `` provided with `suite`/`test`/`context` annotation. This annotation is redundant and might be removed in future releases. | -| `--%test()` | Procedure | Denotes that the annotated procedure is a unit test procedure. Optional test description can by provided (see `displayname`). | -| `--%throws([,...])`| Procedure | Denotes that the annotated test procedure must throw one of the exceptions provided. Supported forms of exceptions are: numeric literals, numeric constant names, exception constant names, predefined Oracle exception names. | -| `--%beforeall` | Procedure | Denotes that the annotated procedure should be executed once before all elements of the suite. | -| `--%beforeall([[.].][,...])` | Package | Denotes that the mentioned procedure(s) should be executed once before all elements of the suite. | -| `--%afterall` | Procedure | Denotes that the annotated procedure should be executed once after all elements of the suite. | -| `--%afterall([[.].][,...])` | Package | Denotes that the mentioned procedure(s) should be executed once after all elements of the suite. | -| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | -| `--%beforeeach([[.].][,...])` | Package | Denotes that the mentioned procedure(s) should be executed before each `%test` procedure in the suite. | -| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | -| `--%aftereach([[.].][,...])` | Package | Denotes that the mentioned procedure(s) should be executed after each `%test` procedure in the suite. | -| `--%beforetest([[.].][,...])` | Procedure | Denotes that mentioned procedure(s) should be executed before the annotated `%test` procedure. | -| `--%aftertest([[.].][,...])` | Procedure | Denotes that mentioned procedure(s) should be executed after the annotated `%test` procedure. | -| `--%rollback()` | Package/procedure | Defines transaction control. Supported values: `auto`(default) - a savepoint is created before invocation of each "before block" is and a rollback to specific savepoint is issued after each "after" block; `manual` - rollback is never issued automatically. Property can be overridden for child element (test in suite) | -| `--%disabled` | Package/procedure | Used to disable a suite or a test. Disabled suites/tests do not get executed, they are however marked and reported as disabled in a test run. | -| `--%context()` | Package | Denotes start of a named context (sub-suite) in a suite package an optional description for context can be provided. | -| `--%name()` | Package | Denotes name for a context. Must be placed after the context annotation and before start of nested context. | -| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | -| `--%tags` | Package/procedure | Used to label a test or a suite for purpose of identification | +| Annotation | Level | Description | +|------------------------------------------------------------|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `--%suite( )` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | +| `--%suitepath( )` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | +| `--%displayname( )` | Package/procedure | Human-readable and meaningful description of a context/suite/test. Overrides the `` provided with `suite`/`test`/`context` annotation. This annotation is redundant and might be removed in future releases. | +| `--%test( )` | Procedure | Denotes that the annotated procedure is a unit test procedure. Optional test description can be provided (see `displayname`). | +| `--%throws( [,...] )` | Procedure | Denotes that the annotated test procedure must throw one of the exceptions provided. Supported forms of exceptions are: numeric literals, numeric constant names, exception constant names, predefined Oracle exception names. | +| `--%beforeall` | Procedure | Denotes that the annotated procedure should be executed once before all elements of the suite. | +| `--%beforeall( [[.].][,...] )` | Package | Denotes that the mentioned procedure(s) should be executed once before all elements of the suite. | +| `--%afterall` | Procedure | Denotes that the annotated procedure should be executed once after all elements of the suite. | +| `--%afterall( [[.].][,...] )` | Package | Denotes that the mentioned procedure(s) should be executed once after all elements of the suite. | +| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | +| `--%beforeeach( [[.].][,...] )` | Package | Denotes that the mentioned procedure(s) should be executed before each `%test` procedure in the suite. | +| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | +| `--%aftereach( [[.].][,...] )` | Package | Denotes that the mentioned procedure(s) should be executed after each `%test` procedure in the suite. | +| `--%beforetest( [[.].][,...] )` | Procedure | Denotes that mentioned procedure(s) should be executed before the annotated `%test` procedure. | +| `--%aftertest( [[.].][,...] )` | Procedure | Denotes that mentioned procedure(s) should be executed after the annotated `%test` procedure. | +| `--%rollback( )` | Package/procedure | Defines transaction control. Supported values: `auto`(default) - a savepoint is created before invocation of each "before block" is and a rollback to specific savepoint is issued after each "after" block; `manual` - rollback is never issued automatically. Property can be overridden for child element (test in suite) | +| `--%disabled( )` | Package/procedure | Used to disable a suite, whole context or a test. Disabled suites/contexts/tests do not get executed, they are however marked and reported as disabled in a test run. The reason that will be displayed next to disabled tests is decided based on hierarchy suites -> context -> test | +| `--%context( )` | Package | Denotes start of a named context (sub-suite) in a suite package an optional description for context can be provided. | +| `--%name( )` | Package | Denotes name for a context. Must be placed after the context annotation and before start of nested context. | +| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | +| `--%tags` | Package/procedure | Used to label a test or a suite for purpose of identification | ### Suite @@ -152,25 +151,23 @@ The `--%suite` annotation denotes PLSQL package as a unit test suite. It accepts an optional description that will be visible when running the tests. When description is not provided, package name is displayed on report. -**Note** ->Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. -> ->Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. -Values for parameters should be provided without any quotation marks. -If the parameters are placed without brackets or with incomplete brackets, they will be ignored. -> ->Example: `--%suite(The name of suite without closing bracket` ->Example: `--%suite The name of suite without brackets` +!!! note + Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. + Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. + Values for parameters should be provided without any quotation marks. + If the parameters are placed without brackets or with incomplete brackets, they will be ignored. + Example: `--%suite(The name of suite without closing bracket` + Example: `--%suite The name of suite without brackets` Suite package without description. -```sql +```sql linenums="1" create or replace package test_package as --%suite end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -181,13 +178,13 @@ Finished in .002415 seconds ``` Suite package with description. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -198,14 +195,14 @@ Finished in .001646 seconds ``` When multiple `--%suite` annotations are specified in package, the first annotation will be used and a warning message will appear indicating duplicate annotation. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) --%suite(Bad annotation) end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -223,14 +220,14 @@ Finished in .003318 seconds ``` When `--%suite` annotation is bound to procedure, it is ignored and results in package not getting recognized as test suite. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) procedure some_proc; end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -257,7 +254,7 @@ If `--%test` raises an unhandled exception the following will happen: - test execution will continue uninterrupted for rest of the suite Test procedure without description. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -270,7 +267,7 @@ create or replace package body test_package as end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -282,7 +279,7 @@ Finished in .004109 seconds ``` Test procedure with description. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -296,7 +293,7 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -308,7 +305,7 @@ Finished in .006828 seconds ``` When multiple `--%test` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -323,7 +320,7 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -343,12 +340,13 @@ Finished in .008815 seconds ### Disabled Marks annotated suite package or test procedure as disabled. +You can provide the reason why the test is disabled that will be displayed in output. Disabling suite. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) - --%disabled + --%disabled(Reason for disabling suite) --%test(Description of tested behavior) procedure some_test; @@ -366,20 +364,65 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` Tests for a package - Description of tested behavior [0 sec] (DISABLED) - Description of another behavior [0 sec] (DISABLED) + Description of tested behavior [0 sec] (DISABLED - Reason for disabling suite) + Description of another behavior [0 sec] (DISABLED - Reason for disabling suite) Finished in .001441 seconds 2 tests, 0 failed, 0 errored, 2 disabled, 0 warning(s) ``` +Disabling the context(s). +```sql linenums="1" +create or replace package test_package as + --%suite(Tests for a package) + + --%context(Context1) + + --%test(Description of tested behavior) + procedure some_test; + + --%endcontext + + --%context(Context2) + + --%disabled(Reason for disabling context2) + + --%test(Description of another behavior) + procedure other_test; + + --%endcontext +end; +/ +create or replace package body test_package as + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql linenums="1" +exec ut.run('test_package'); +``` +``` +Tests for a package + Context1 + Description of tested behavior [.002 sec] + Context2 + Description of another behavior [0 sec] (DISABLED - Reason for disabling context2) + +Finished in .005079 seconds +2 tests, 0 failed, 0 errored, 1 disabled, 0 warning(s) +``` + Disabling individual test(s). -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -387,7 +430,7 @@ create or replace package test_package as procedure some_test; --%test(Description of another behavior) - --%disabled + --%disabled(Reason for disabling test) procedure other_test; end; / @@ -400,13 +443,13 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` Tests for a package Description of tested behavior [.004 sec] - Description of another behavior [0 sec] (DISABLED) + Description of another behavior [0 sec] (DISABLED - Reason for disabling test) Finished in .005868 seconds 2 tests, 0 failed, 0 errored, 1 disabled, 0 warning(s) @@ -414,10 +457,10 @@ Finished in .005868 seconds ### Beforeall -There are two possible ways to use the `--%beforeall` annotation. +There are two possible ways to use the `--%beforeall` annotation. As a procedure level annotation: -```sql +```sql linenums="1" --%suite(Some test suite) --%beforeall @@ -429,7 +472,7 @@ procedure some_test; Marks annotated procedure to be executed before all test procedures in a suite. As a package level annotation (not associated with any procedure). -```sql +```sql linenums="1" --%suite(Some test suite) --%beforeall(to_be_executed_before_all, other_package.some_setup) @@ -446,6 +489,7 @@ Indicates that the procedure(s) mentioned as the annotation parameter are to be If `--%beforeall` raises an exception, suite content cannot be safely executed as the setup was not executed successfully for the suite. If `--%beforeall` raises an exception the following will happen: + - the `--%beforeall` procedures that follow the failed one, **will not be executed** - all `--%test` procedures and their `--%beforeeach`, `--%aftereach`, `--%beforetest` and `--%aftertest` procedures within suite package **will not be executed** - all `--%test` procedures **will be marked as failed** @@ -456,7 +500,7 @@ When multiple `--%beforeall` procedures are defined in a suite package, all of t For multiple `--%beforeall` procedures order of execution is defined by annotation position in the package specification. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -484,9 +528,10 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` + ``` Tests for a package --- SETUP_STUFF invoked --- @@ -498,9 +543,9 @@ Finished in .012292 seconds ``` In the below example a combination pacakge and procedure level `--%beforeall` annotations is used. -The order of execution of the beforeall procedures is determined by the annotation position in package. +The execution order of the `--%beforeall` procedures is determined by the annotation position in the package. All of the `--%beforeall` procedures get invoked before any test is executed in a suite. - ```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -549,12 +594,13 @@ All of the `--%beforeall` procedures get invoked before any test is executed in procedure other_test is begin null; end; end; / - ``` +``` - ```sql +```sql linenums="1" exec ut.run('test_package'); - ``` - ``` +``` + +``` Tests for a package --- INITIAL_SETUP invoked --- --- ANOTHER_SETUP invoked --- @@ -565,11 +611,11 @@ Tests for a package Finished in .018944 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) - ``` +``` When multiple `--%beforeall` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. When procedure is annotated as both `--%beforeall` and `--%test`, the procedure will become a test and a warning message will appear indicating invalid annotation combination. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -600,10 +646,11 @@ When procedure is annotated as both `--%beforeall` and `--%test`, the procedure / ``` - ```sql +```sql linenums="1" exec ut.run('test_package'); - ``` - ``` +``` + +``` Tests for a package --- INITIAL_SETUP invoked --- Description of tested behavior [.003 sec] @@ -621,15 +668,17 @@ Warnings: Finished in .012158 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 2 warning(s) - ``` +``` +!!! note "Suite scope" + The `--%beforeall` procedrues in parent suite package are always part of all suite packages that are children on the suitepath. ### Afterall There are two possible ways to use the `--%afterall` annotation. As a procedure level annotation: -```sql +```sql linenums="1" --%suite(Some test suite) --%afterall @@ -641,7 +690,7 @@ procedure some_test; Marks annotated procedure to be executed after all test procedures in a suite. As a package level annotation (not associated with any procedure). -```sql +```sql linenums="1" --%suite(Some test suite) --%afterall(to_be_executed_after_all, other_package.some_cleanup) @@ -650,11 +699,12 @@ As a package level annotation (not associated with any procedure). procedure some_test; procedure to_be_executed_after_all; - ``` + Indicates that the procedure(s) mentioned as the annotation parameter are to be executed after all test procedures in a suite. If `--%afterall` raises an exception the following will happen: + - a warning will be raised, indicating that `--%afterall` procedure has failed - execution will continue uninterrupted for rest of the suite @@ -667,7 +717,7 @@ For multiple `--%afterall` procedures order of execution is defined by annotatio All rules defined for `--%beforeall` also apply for `--%afterall` annotation. See [beforeall](#Beforeall) for more details. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -695,9 +745,10 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` + ``` Tests for a package Description of tested behavior [.003 sec] @@ -708,6 +759,10 @@ Finished in .014161 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` +!!! note "Suite scope" + The `--%afterall` procedrues in parent suite package are always part of all suite packages that are children on the suitepath. + + ### Beforeeach The procedure annotated as `--%beforeeach` is getting executed before each test in a suite. @@ -716,7 +771,7 @@ That means that the procedure will be executed as many times as there are test i There are two possible ways to use the `--%beforeeach` annotation. As a procedure level annotation: -```sql +```sql linenums="1" --%suite(Some test suite) --%beforeeach @@ -728,7 +783,7 @@ procedure some_test; Marks annotated procedure to be executed before each test procedures in a suite. As a package level annotation (not associated with any procedure). -```sql +```sql linenums="1" --%suite(Some test suite) --%beforeeach(to_be_executed_before_each, other_package.some_setup) @@ -737,14 +792,15 @@ As a package level annotation (not associated with any procedure). procedure some_test; procedure to_be_executed_before_each; - ``` + Indicates that the procedure(s) mentioned as the annotation parameter are to be executed before each test procedure in a suite. If a test is marked as disabled the `--%beforeeach` procedure is not invoked for that test. If `--%beforeeach` raises an unhandled exception the following will happen: + - the following `--%beforeeach` as well as all `--%beforetest` for that test **will not be executed** - the test will be marked as errored and exception stack trace will be captured and reported - the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test @@ -757,7 +813,7 @@ When multiple `--%beforeeach` procedures are defined in a suite, all of them wil For multiple `--%beforeeach` procedures order of execution is defined by annotation position in the package specification. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -798,9 +854,10 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` + ``` Tests for a package ---SETUP_STUFF invoked --- @@ -815,6 +872,9 @@ Finished in .014683 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` +!!! note "Package scope" + Unlike `--%beforeall`, the `--%beforeeach` annotation is only applicable within the scope of current test suite package. The `--%beforeeach` annotations from parent suite package are ignored. + See [beforeall](#Beforeall) for more examples. ### Aftereach @@ -827,7 +887,7 @@ That means that the procedure will be executed as many times as there are test i There are two possible ways to use the `--%aftereach` annotation. As a procedure level annotation: -```sql +```sql linenums="1" --%suite(Some test suite) --%aftereach @@ -839,7 +899,7 @@ procedure some_test; Marks annotated procedure to be executed after each test procedures in a suite. As a package level annotation (not associated with any procedure). -```sql +```sql linenums="1" --%suite(Some test suite) --%aftereach(to_be_executed_after_each, other_package.some_setup) @@ -848,13 +908,14 @@ As a package level annotation (not associated with any procedure). procedure some_test; procedure to_be_executed_after_each; - ``` + Indicates that the procedure(s) mentioned as the annotation parameter are to be executed after each test procedure in a suite. If a test is marked as disabled the `--%aftereach` procedure is not invoked for that test. If `--%aftereach` raises an unhandled exception the following will happen: + - the test will be marked as errored and exception stack trace will be captured and reported - the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test - the `--%afterall` procedures **will be executed** @@ -866,7 +927,7 @@ For multiple `--%aftereach` procedures order of execution is defined by the anno As a rule, the `--%aftereach` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftereach` procedures have raised unhandled exceptions. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -906,7 +967,7 @@ create or replace package body test_package as end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -923,11 +984,15 @@ Finished in .018115 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` +!!! note "Package scope" + Unlike `--%afterall`, the `--%aftereach` annotation is only applicable within the scope of current test suite package. The `--%aftereach` annotations from parent suite package are ignored. + See [beforeall](#Beforeall) for more examples. ### Beforetest Indicates specific setup procedure(s) to be executed for a test. The procedure(s) can be located either: + - within current package (package name is optional) - within another package @@ -938,6 +1003,7 @@ The `--%beforetest` procedures are executed after invoking all `--%beforeeach` f If a test is marked as disabled the `--%beforetest` procedures are not invoked for that test. If `--%beforetest` raises an unhandled exception the following will happen: + - the following `--%beforetest` for that test **will not be executed** - the test will be marked as errored and exception stack trace will be captured and reported - the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test @@ -947,12 +1013,13 @@ If `--%beforetest` raises an unhandled exception the following will happen: When multiple `--%beforetest` procedures are defined for a test, all of them will be executed before invoking the test. The order of execution for `--%beforetest` procedures is defined by: + - position of procedure on the list within single annotation - annotation position As a rule, the `--%beforetest` execution gets aborted if preceding `--%beforeeach` or `--%beforetest` failed. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -994,7 +1061,7 @@ create or replace package body test_package as end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -1016,6 +1083,7 @@ Finished in .015185 seconds ### Aftertest Indicates specific cleanup procedure(s) to be executed for a test. The procedure(s) can be located either: + - within current package (package name is optional) - within another package @@ -1024,20 +1092,22 @@ The annotation need to be placed alongside `--%test` annotation. If a test is marked as disabled the `--%aftertest` procedures are not invoked for that test. If `--%aftertest` raises an unhandled exception the following will happen: + - the test will be marked as errored and exception stack trace will be captured and reported - the following `--%aftertest` and all `--%aftereach` procedures **will be executed** for the errored test - the `--%afterall` procedures **will be executed** - test execution will continue uninterrupted for rest of the suite -When multiple `--%aftertest` procedures are defined for a test, all of them will be executed before invoking the test. +When multiple `--%aftertest` procedures are defined for a test, all of them will be executed after invoking the test. The order of execution for `--%aftertest` procedures is defined by: + - position of procedure on the list within single annotation - annotation position As a rule, the `--%aftertest` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftertest` procedures have raised unhandled exceptions. -```sql +```sql linenums="1" create or replace package test_package as --%suite(Tests for a package) @@ -1079,7 +1149,7 @@ create or replace package body test_package as end; / ``` -```sql +```sql linenums="1" exec ut.run('test_package'); ``` ``` @@ -1114,6 +1184,7 @@ Contexts allow for creating sub-suites within a suite package and they allow for In essence, context behaves like a suite within a suite. Context have following characteristics: + - context starts with the `--%context` annotation and ends with `--%endcontext`. Everything placed between those two annotations belongs to that context - can have a description provided as parameter for example `--%context(Some interesting stuff)`. - can have a name provided with `--%name` annotation. This is different than with `suite` and `test` annotations, where name is taken from `package/procedure` name. @@ -1131,7 +1202,7 @@ Context have following characteristics: The below example illustrates usage of `--%context` for separating tests for individual procedures of package. Sample tables and code -```sql +```sql linenums="1" create table rooms ( room_key number primary key, name varchar2(100) not null @@ -1188,11 +1259,12 @@ end; ``` Below test suite defines: + - `--%beforeall` outside of context, that will be executed before all tests - `--%context(remove_rooms_by_name)` to group tests related to `remove_rooms_by_name` functionality - `--%context(add_rooms_content)` to group tests related to `add_rooms_content` functionality -```sql +```sql linenums="1" create or replace package test_rooms_management is gc_null_value_exception constant integer := -1400; @@ -1311,8 +1383,8 @@ end; / ``` -When te tests are executed -```sql +When the tests are executed +```sql linenums="1" exec ut.run('test_rooms_management'); ``` The following report is displayed @@ -1334,7 +1406,7 @@ Finished in .035261 seconds Example of nested contexts test suite specification. *Source - [slide 145](https://www.slideshare.net/Kevlin/structure-and-interpretation-of-test-cases/145?src=clipshare) of Structure and Interpretation of Test Cases by Kevlin Henney* -```sql +```sql linenums="1" create or replace package queue_spec as --%suite(Queue specification) @@ -1424,7 +1496,7 @@ The `--%name` can be useful when you would like to run only a specific context o Consider the below example. -```sql +```sql linenums="1" create or replace package queue_spec as --%suite(Queue specification) @@ -1457,22 +1529,22 @@ end; In the above code, suitepaths, context names and context descriptions will be as follows. -| suitepath | description | name | -|-----------|------------|------| -| queue_spec | Queue specification | queue_spec | -| queue_spec.context_#1 | A new queue | context_#1 | -| queue_spec.context_#2 | An empty queue | context_#2 | -| queue_spec.context_#3 | A non empty queue | context_#3 | -| queue_spec.context_#3.context_#1 | that is not full | context_#1 | -| queue_spec.context_#3.context_#2 | that is full | context_#2 | +| suitepath | description | name | +|----------------------------------|----------------------|------------| +| queue_spec | Queue specification | queue_spec | +| queue_spec.context_#1 | A new queue | context_#1 | +| queue_spec.context_#2 | An empty queue | context_#2 | +| queue_spec.context_#3 | A non empty queue | context_#3 | +| queue_spec.context_#3.context_#1 | that is not full | context_#1 | +| queue_spec.context_#3.context_#2 | that is full | context_#2 | In order to run only the tests for the context `A non empty queue that is not full` you will need to call utPLSQL as below: -```sql +```sql linenums="1" exec ut.run(':queue_spec.context_#3.context_#1'); ``` You can use `--%name` annotation to explicitly name contexts on suitepath. -```sql +```sql linenums="1" create or replace package queue_spec as --%suite(Queue specification) @@ -1510,23 +1582,25 @@ end; In the above code, suitepaths, context names and context descriptions will be as follows. -| suitepath | description | name | -|-----------|------------|------| -| queue_spec | Queue specification | queue_spec | -| queue_spec.a_new_queue | A new queue | a_new_queue | -| queue_spec.an_empty_queue | An empty queue | an_empty_queue | -| queue_spec.a_non_empty_queue | A non empty queue | a_non_empty_queue | -| queue_spec.a_non_empty_queue.that_is_not_full | that is not full | that_is_not_full | -| queue_spec.a_non_empty_queue.that_is_full | that is full | that_is_full | +| suitepath | description | name | +|-----------------------------------------------|-----------------------|-------------------| +| queue_spec | Queue specification | queue_spec | +| queue_spec.a_new_queue | A new queue | a_new_queue | +| queue_spec.an_empty_queue | An empty queue | an_empty_queue | +| queue_spec.a_non_empty_queue | A non empty queue | a_non_empty_queue | +| queue_spec.a_non_empty_queue.that_is_not_full | that is not full | that_is_not_full | +| queue_spec.a_non_empty_queue.that_is_full | that is full | that_is_full | The `--%name` annotation is only relevant for: + - running subsets of tests by given context suitepath - some of test reports, like `ut_junit_reporter` that use suitepath or test-suite element names (not descriptions) for reporting #### Name naming convention The value of `--%name` annotation must follow the following naming rules: + - cannot contain spaces - cannot contain a `.` (full stop/dot) - is case-insensitive @@ -1539,106 +1613,38 @@ It allows for grouping of tests / suites using various categorization and place e.g. -```sql +```sql linenums="1" --%tags(batch,daily,csv) ``` or -```sql +```sql linenums="1" --%tags(online,json) --%tags(api) ``` Tags are defined as a comma separated list within the `--%tags` annotation. -When executing a test run with tag filter applied, the framework will find all tests associated with the given tags and execute them. -The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run. - -When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent. Parent suite tests are not executed, but a suitepath hierarchy is kept. - - -Sample test suite package with tags. -```sql -create or replace package ut_sample_test is - - --%suite(Sample Test Suite) - --%tags(api) - - --%test(Compare Ref Cursors) - --%tags(complex,fast) - procedure ut_refcursors1; - - --%test(Run equality test) - --%tags(simple,fast) - procedure ut_test; - -end ut_sample_test; -/ - -create or replace package body ut_sample_test is - - procedure ut_refcursors1 is - v_actual sys_refcursor; - v_expected sys_refcursor; - begin - open v_expected for select 1 as test from dual; - open v_actual for select 2 as test from dual; - - ut.expect(v_actual).to_equal(v_expected); - end; - - procedure ut_test is - begin - ut.expect(1).to_equal(0); - end; - -end ut_sample_test; -/ -``` - -Execution of the test is done by using the parameter `a_tags` - -```sql -select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api')); -``` -The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api` - -```sql -select * from table(ut.run(a_tags => 'complex')); -``` -The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex` - -```sql -select * from table(ut.run(a_tags => 'fast')); -``` -The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast` +When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent, unless they are excluded explicitly at runtime with a negated tag expression. +See [running unit tests](running-unit-tests.md) for more information on using tags to filter test suites that are to be executed. #### Tag naming convention Tags must follow the below naming convention: - tag is case sensitive -- tag can contain special characters like `$#/\?-!` etc. -- tag cannot be an empty string +- tag must not contain any of the following reserved characters: + - comma (,) + - left or right parenthesis ((, )) + - ampersand (&) + - vertical bar (|) + - exclamation point (!) +- tag cannot be null or blank - tag cannot start with a dash, e.g. `-some-stuff` is **not** a valid tag - tag cannot contain spaces, e.g. `test of batch`. To create a multi-word tag use underscores or dashes, e.g. `test_of_batch`, `test-of-batch` - leading and trailing spaces are ignored in tag name, e.g. `--%tags( tag1 , tag2 )` becomes `tag1` and `tag2` tag names - - -#### Excluding tests/suites by tags - -It is possible to exclude parts of test suites with tags. -In order to do so, prefix the tag name to exclude with a `-` (dash) sign when invoking the test run. - -Examples (based on above sample test suite) - -```sql -select * from table(ut.run(a_tags => 'api,fast,-complex')); -``` -The above call will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex`. -Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed. - +- tag cannot be one of two reserved words: `none` and `any`. `none` and `any` as a tag will be treated as no tag ### Suitepath @@ -1661,7 +1667,7 @@ If you want to create tests for your application it is recommended to structure The `--%suitepath` annotation is used for such grouping. Even though test packages are defined in a flat structure the `--%suitepath` is used by the framework to form them into a hierarchical structure. Your payments recognition test package might look like: -```sql +```sql linenums="1" create or replace package test_payment_recognition as --%suite(Payment recognition tests) @@ -1680,7 +1686,7 @@ end test_payment_recognition; ``` And payments set off test package: -```sql +```sql linenums="1" create or replace package test_payment_set_off as --%suite(Payment set off tests) @@ -1699,7 +1705,7 @@ When you execute tests for your application, the framework constructs a test sui The test report indicates which expectation has failed on the payments module. The payments recognition submodule is causing the failure as `recognize_by_num` has not met the expectations of the test. Grouping tests into modules and submodules using the `--%suitepath` annotation allows you to logically organize your project's flat structure of packages into functional groups. An additional advantage of such grouping is the fact that every element level of the grouping can be an actual unit test package containing a common module level setup for all of the submodules. So in addition to the packages mentioned above you could have the following package. -```sql +```sql linenums="1" create or replace package payments as --%suite(Payments) @@ -1714,10 +1720,13 @@ end payments; ``` When executing tests, `path` for executing tests can be provided in three ways: + * schema - execute all tests in the schema * [schema]:suite1[.suite2][.suite3]...[.procedure] - execute all tests by `suitepath` in all suites on path suite1[.suite2][.suite3]...[.procedure]. If schema is not provided, then the current schema is used. Example: `:all.rooms_tests` * [schema.]package[.procedure] - execute all tests in the specified test package. The whole hierarchy of suites in the schema is built before all before/after hooks or part suites for the provided suite package are executed as well. Example: `tests.test_contact.test_last_name_validator` or simply `test_contact.test_last_name_validator` if `tests` is the current schema. +!!! note + Only the `beforeall` and `afterall` hooks of the parent suite are executed for the suite package. The `beforeeach` and `aftereach` hooks of the suite package are not executed. ### Rollback @@ -1743,23 +1752,24 @@ Doing so allows your tests to use the framework's automatic transaction control When you are testing code that performs explicit or implicit commits, you may set the test procedure to run as an autonomous transaction with `pragma autonomous_transaction`. Keep in mind that when your test runs as autonomous transaction it will not see the data prepared in a setup procedure unless the setup procedure committed the changes. -**Note** -> The `--%suitepath` annotation, when used, must be provided with a value of path. -> The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. -> -> Example: `--%suitepath(org.utplsql.core.utils)` -> +!!! note + The `--%rollback(manual)` annotation is only relevant for suites that use shared setup/cleanup with beforeall, + The `--%suitepath` annotation, when used, must be provided with a value of path. + The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. + Example: `--%suitepath(org.utplsql.core.utils)` + ### Throws The `--%throws` annotation allows you to specify a list of exceptions as one of: + - number literals - example `--%throws(-20134)` -- variables of type exception defined in a package specification - example `--%throws(exc_pkg.c_exception_No_variable)` -- variables of type number defined in a package specification - example `--%throws(exc_pkg.c_some_exception)` -- [predefined oracle exceptions](https://docs.oracle.com/cd/E11882_01/timesten.112/e21639/exceptions.htm#CIHFIGFE) - example `--%throws(no_data_found)` +- variables of type `exception` defined in a package specification - example `--%throws(exc_pkg.c_exception_no_variable)` +- variables or constants of containing a valid negative exception number defined in a package specification - example `--%throws(exc_pkg.c_some_exception)` +- [predefined oracle exceptions](https://docs.oracle.com/en//database/oracle/oracle-database/19/lnpls/predefined-exceptions.html) - example `--%throws(no_data_found)` -The annotation is ignored, when no valid arguments are provided. Examples of invalid annotations `--%throws()`,`--%throws`, `--%throws(abe, 723pf)`. +The annotation is ignored when no valid arguments are provided. Examples of invalid annotations `--%throws()`,`--%throws`, `--%throws(abe, 723pf)`. If `--%throws` annotation is specified with arguments and no exception is raised, the test is marked as failed. @@ -1770,22 +1780,30 @@ The framework will raise a warning, when `--%throws` annotation has invalid argu Annotation `--%throws(7894562, operaqk, -=1, -20496, pow74d, posdfk3)` will be interpreted as `--%throws(-20496)`. Please note that `NO_DATA_FOUND` exception is a special case in Oracle. To capture it use `NO_DATA_FOUND` named exception or `-1403` exception No. -​ + +Syntax: `--%throws( [[schema.]package.]exception [, ... ])` + +The exception name can be provided with or without the schema and package name. The package name is required only when the exception variable is located in another package than the unit test package. The schema name is required only when the exception variable is located in a package in another schema. + Example: -```sql +```sql linenums="1" create or replace package exc_pkg is c_e_option1 constant number := -20200; c_e_option2 constant varchar2(10) := '-20201'; - c_e_option3 number := -20202; - - e_option4 exception; - pragma exception_init(e_option4, -20203); + c_e_option3 integer := -20202; + e_initialized_exception exception; + pragma exception_init(e_initialized_exception, -20203); + + e_uninitialized_exception exception; end; / create or replace package example_pgk as + e_local_exception exception; + e_local_exception_num constant integer := -20123; + --%suite(Example Throws Annotation) --%test(Throws one of the listed exceptions) @@ -1816,10 +1834,22 @@ create or replace package example_pgk as --%throws(exc_pkg.c_e_option3) procedure raised_option3_exception; - --%test(Throws package exception option4) - --%throws(exc_pkg.e_option4) - procedure raised_option4_exception; + --%test(Throws exception associated with exception number) + --%throws(exc_pkg.e_initialized_exception) + procedure raised_initialized_exception; + --%test(Throws uninitialized exception) + --%throws(exc_pkg.e_uninitialized_exception) + procedure raised_uninitialized_exception; + + --%test(Throws exception local to unit tests package) + --%throws(e_local_exception) + procedure raised_local_exception; + + --%test(Throws exception local to unit tests package with exception number) + --%throws(e_local_exception_num) + procedure raised_local_exception_num; + --%test(Raise name exception) --%throws(DUP_VAL_ON_INDEX) procedure raise_named_exc; @@ -1830,6 +1860,7 @@ create or replace package example_pgk as end; / + create or replace package body example_pgk is procedure raised_one_listed_exception is begin @@ -1866,11 +1897,28 @@ create or replace package body example_pgk is raise_application_error(exc_pkg.c_e_option3, 'Test error'); end; - procedure raised_option4_exception is + procedure raised_initialized_exception is begin - raise exc_pkg.e_option4; + raise exc_pkg.e_initialized_exception; end; + procedure raised_uninitialized_exception is + begin + raise exc_pkg.e_uninitialized_exception; + end; + + procedure raised_local_exception is + pragma autonomous_transaction; + begin + raise e_local_exception; + end; + + procedure raised_local_exception_num is + pragma autonomous_transaction; + begin + raise_application_error(e_local_exception_num, 'Test error'); + end; + procedure raise_named_exc is begin raise DUP_VAL_ON_INDEX; @@ -1889,53 +1937,60 @@ exec ut3.ut.run('example_pgk'); Running the test will give report: ``` Example Throws Annotation - Throws one of the listed exceptions [.002 sec] - Throws different exception than expected [.002 sec] (FAILED - 1) - Throws different exception than listed [.003 sec] (FAILED - 2) - Gives failure when an exception is expected and nothing is thrown [.002 sec] (FAILED - 3) - Throws package exception option1 [.003 sec] - Throws package exception option2 [.002 sec] - Throws package exception option3 [.002 sec] - Throws package exception option4 [.002 sec] - Raise name exception [.002 sec] - Invalid throws annotation [.002 sec] - + Throws one of the listed exceptions [,026 sec] + Throws different exception than expected [,009 sec] (FAILED - 1) + Throws different exception than listed [,014 sec] (FAILED - 2) + Gives failure when an exception is expected and nothing is thrown [,016 sec] (FAILED - 3) + SUCCESS + Actual: 1 (number) was expected to equal: 1 (number) + Throws package exception option1 [,007 sec] + Throws package exception option2 [,008 sec] + Throws package exception option3 [,007 sec] + Throws exception associated with exception number [,006 sec] + Throws uninitialized exception [,006 sec] + Throws exception local to unit tests package [,009 sec] + Throws exception local to unit tests package with exception number [,011 sec] + Raise name exception [,006 sec] + Invalid throws annotation [,004 sec] + Failures: - + 1) raised_different_exception Actual: -20143 was expected to equal: -20144 ORA-20143: Test error - ORA-06512: at "UT3.EXAMPLE_PGK", line 9 - ORA-06512: at "UT3.EXAMPLE_PGK", line 9 - ORA-06512: at line 6 - + ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 9 + ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 9 + ORA-06512: at line 7 + 2) raised_unlisted_exception Actual: -20143 was expected to be one of: (-20144, -1, -20145) ORA-20143: Test error - ORA-06512: at "UT3.EXAMPLE_PGK", line 14 - ORA-06512: at "UT3.EXAMPLE_PGK", line 14 - ORA-06512: at line 6 - + ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 14 + ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 14 + ORA-06512: at line 7 + 3) nothing_thrown Expected one of exceptions (-20459, -20136, -20145) but nothing was raised. - - + + Warnings: - - 1) example_pgk + + 1) example_pgk.raised_one_listed_exception Invalid parameter value "bad" for "--%throws" annotation. Parameter ignored. - at "UT3.EXAMPLE_PGK.RAISED_ONE_LISTED_EXCEPTION", line 6 + at package "UT3_TESTER.EXAMPLE_PGK.RAISED_ONE_LISTED_EXCEPTION", line 8 + 2) example_pgk "--%throws" annotation requires a parameter. Annotation ignored. - at "UT3.EXAMPLE_PGK.BAD_THROWS_ANNOTATION", line 42 - -Finished in .025784 seconds -10 tests, 3 failed, 0 errored, 0 disabled, 2 warning(s) + at package "UT3_TESTER.EXAMPLE_PGK.BAD_THROWS_ANNOTATION", line 57 + +Finished in ,138276 seconds +13 tests, 3 failed, 0 errored, 0 disabled, 2 warning(s) + ``` ## Order of execution -```sql +```sql linenums="1" create or replace package test_employee_pkg is --%suite(Employee management) @@ -2057,45 +2112,50 @@ When processing the test suite `test_employee_pkg` defined in [Example of annota rollback to savepoint 'before-suite' ``` -**Note** ->utPLSQL does not guarantee ordering of tests in suite. On contrary utPLSQL might give random order of tests/contexts in suite. -> ->Order of execution within multiple occurrences of `before`/`after` procedures is determined by the order of annotations in specific block (context/suite) of package specification. +!!! note + utPLSQL does not guarantee ordering of tests in suite. On contrary utPLSQL might give random order of tests/contexts in suite.
+ Order of execution within multiple occurrences of `before`/`after` procedures is determined by the order of annotations in specific block (context/suite) of package specification. ## sys_context It is possible to access information about currently running suite. The information is available by calling `sys_context( 'UT3_INFO', attribute )`. -It can be accessed from any procecure invoked as part of utPLSQL test execution. +It can be accessed from any procedure invoked as part of utPLSQL test execution. -**Note:** -> Context name is derived from schema name where utPLSQL is installed. -> The context name in below examples represents the default install schema -> `UT3` -> If you install utPLSQL into another schema the context name will be different. -> For example if utPLSQL is installed into `HR` schema, the context name will be `HR_INFO` +!!! note + Context name is derived from schema name where utPLSQL is installed.
+ The context name in below examples represents the default install schema -> `UT3`
+ If you install utPLSQL into another schema the context name will be different.
+ For example if utPLSQL is installed into `HR` schema, the context name will be `HR_INFO` Following attributes are populated: -- Always: - - `sys_context( 'UT3_INFO', 'RUN_PATHS' );` - list of suitepaths / suitenames used as input parameters for call to `ut.run(...)` or `ut_runner.run(...)` - - `sys_context( 'UT3_INFO', 'SUITE_DESCRIPTION' );` - the description of test suite that is currently being executed - - `sys_context( 'UT3_INFO', 'SUITE_PACKAGE' );` - the owner and name of test suite package that is currently being executed - - `sys_context( 'UT3_INFO', 'SUITE_PATH' );` - the suitepath for the test suite package that is currently being executed - - `sys_context( 'UT3_INFO', 'SUITE_START_TIME' );` - the execution start timestamp of test suite package that is currently being executed - - `sys_context( 'UT3_INFO', 'CURRENT_EXECUTABLE_NAME' );` - the owner.package.procedure of currently running test suite executable - - `sys_context( 'UT3_INFO', 'CURRENT_EXECUTABLE_TYPE' );` - the type of currently running test suite executable (one of: `beforeall`, `beforeeach`, `beforetest`, `test`, `aftertest`, `aftereach`, `afterall` - -- When running in suite context - - `sys_context( 'UT3_INFO', 'CONTEXT_DESCRIPTION' );` - the description of test suite context that is currently being executed - - `sys_context( 'UT3_INFO', 'CONTEXT_NAME' );` - the name of test suite context that is currently being executed - - `sys_context( 'UT3_INFO', 'CONTEXT_PATH' );` - the suitepath for the currently executed test suite context - - `sys_context( 'UT3_INFO', 'CONTEXT_START_TIME' );` - the execution start timestamp for the currently executed test suite context -- When running a suite executable procedure that is a `test` or `beforeeach`, `aftereach`, `beforetest`, `aftertest` - - `sys_context( 'UT3_INFO', 'TEST_DESCRIPTION' );` - the description of test for which the current executable is being invoked - - `sys_context( 'UT3_INFO', 'TEST_NAME' );` - the name of test for which the current executable is being invoked - - `sys_context( 'UT3_INFO', 'TEST_START_TIME' );` - the execution start timestamp of test that is currently being executed (the time when first `beforeeach`/`beforetest` was called for that test) + +| Name | Scope | Description | +|-------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| COVERAGE_RUN_ID | run | Value of COVERAGE_RUN_ID used by utPLSQL internally for coverage gathering | +| RUN_PATHS | run | list of suitepaths / suitenames used as input parameters for call to `ut.run(...)` or `ut_runner.run(...)` | +| SUITE_DESCRIPTION | run | the description of test suite that is currently being executed | +| SUITE_PACKAGE | run | the owner and name of test suite package that is currently being executed | +| SUITE_PATH | run | the suitepath for the test suite package that is currently being executed | +| SUITE_START_TIME | run | the execution start timestamp of test suite package that is currently being executed | +| CURRENT_EXECUTABLE_NAME | run | the owner.package.procedure of currently running test suite executable | +| CURRENT_EXECUTABLE_TYPE | run | the type of currently running test suite executable (one of: `beforeall`, `beforeeach`, `beforetest`, `test`, `aftertest`, `aftereach`, `afterall` | + | CONTEXT_DESCRIPTION | suite context | the description of test suite context that is currently being executed | + | CONTEXT_NAME | suite context | the name of test suite context that is currently being executed | + | CONTEXT_PATH | suite context | the suitepath for the currently executed test suite context | + | CONTEXT_START_TIME | suite context | the execution start timestamp for the currently executed test suite context | +| TEST_DESCRIPTION | test* | the description of test for which the current executable is being invoked | +| TEST_NAME | test* | the name of test for which the current executable is being invoked | +| TEST_START_TIME | test* | the execution start timestamp of test that is currently being executed (the time when first `beforeeach`/`beforetest` was called for that test) | + +!!! note "Scopes" + - run - context information is available in any element of test run
+ - suite context - context information is available in any element nested within a suite context
+ - test* - context information is available when executing following procedure types: test, beforetest, aftertest, beforeeach or aftereach + Example: -```sql +```sql linenums="1" create or replace procedure which_procecure_called_me is begin dbms_output.put_line( @@ -2154,7 +2214,7 @@ end; / ``` -```sql +```sql linenums="1" exec ut.run('test_call'); ``` @@ -2187,14 +2247,14 @@ If you are in a situation where your database is controlled via CI/CD server and To build the annotation cache without actually invoking any tests, call `ut_runner.rebuild_annotation_cache(a_object_owner)` for every unit test owner for which you want to have the annotation cache prebuilt. Example: -```sql +```sql linenums="1" exec ut_runner.rebuild_annotation_cache('HR'); ``` To purge the annotation cache call `ut_runner.purge_cache(a_object_owner, a_object_type)`. Both parameters are optional and if not provided, all owners/object_types will be purged. Example: -```sql +```sql linenums="1" exec ut_runner.purge_cache('HR', 'PACKAGE'); ``` diff --git a/docs/userguide/best-practices.md b/docs/userguide/best-practices.md index 4be85696a..c3f026d6f 100644 --- a/docs/userguide/best-practices.md +++ b/docs/userguide/best-practices.md @@ -1,6 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# Best Practices +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) The following are best practices we at utPLSQL have learned about PL/SQL and Unit Testing. @@ -17,9 +15,9 @@ The following are best practices we at utPLSQL have learned about PL/SQL and Uni - Tests should not mimic / duplicate the logic of tested code - Tests should contain zero logic (or as close to zero as possible) - The 3A rule: - - Arrange (setup inputs/data/environment for the tested code) - - Act (execute code under test) - - Assert (validate the outcomes of the execution) + - Arrange (setup inputs/data/environment for the tested code) + - Act (execute code under test) + - Assert (validate the outcomes of the execution) - Each tested procedure/function/trigger (code block) should have more than one test - Each test should check only one behavior (one requirement) of the code block under test - Tests should be maintained as thoroughly as production code diff --git a/docs/userguide/coverage.md b/docs/userguide/coverage.md index 3698f4076..a069fb598 100644 --- a/docs/userguide/coverage.md +++ b/docs/userguide/coverage.md @@ -1,28 +1,34 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) -# Coverage -utPLSQL comes with a built-in coverage reporting engine. The code coverage reporting uses DBMS_PROFILER package provided with Oracle database. +utPLSQL comes with a built-in coverage reporting engine. The code coverage reporting combines data from package DBMS_PROFILER and DBMS_PLSQL_CODE_COVERAGE. Code coverage is gathered for the following source types: + * package bodies * type bodies * triggers * procedures * functions -**Note** +!!! note -> The package and type specifications are excluded from code coverage analysis. This limitation is introduced to avoid false-negatives. Typically package specifications contain no executable code. The only exception is initialization of global constants and variables in package specification. Since most package specifications are not executable at all, there is no information available on the number of lines covered and those would be reported as 0% covered, which is not desirable. + The package and type specifications are excluded from code coverage analysis. + This limitation is introduced to avoid false-negatives. + Typically package specifications contain no executable code. + The only exception is initialization of global constants and variables in package specification. + Since most package specifications are not executable at all, there is no information available + on the number of lines covered and those would be reported as 0% covered, which is not desirable. To obtain information about code coverage for unit tests, run utPLSQL with one of built-in code coverage reporters. The following code coverage reporters are supplied with utPLSQL: -* `ut_coverage_html_reporter` - generates a HTML coverage report providing summary and detailed information on code coverage. The HTML reporter is based on the open-source [simplecov-html](https://github.com/colszowka/simplecov-html) reporter for Ruby. It includes source code of the code that was covered (if possible) -* `ut_coveralls_reporter` - generates a [Coveralls compatible JSON](https://coveralls.zendesk.com/hc/en-us/articles/201774865-API-Introduction) coverage report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by cloud services like [Coveralls](https://coveralls.io) -* `ut_coverage_sonar_reporter` - generates a [Sonar Compatible XML](https://docs.sonarqube.org/display/SONAR/Generic+Test+Data) coverage report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by services like [SonarQube/SonarCloud](https://about.sonarcloud.io/) + +* `ut_coverage_html_reporter` - generates a HTML coverage report providing summary and detailed information on code coverage. The HTML reporter is based on the open-source [simplecov-html](https://github.com/colszowka/simplecov-html) reporter for Ruby. It includes source code of the code that was covered (if the code is accessible for test user) +* `ut_coverage_sonar_reporter` - generates a [Sonar Compatible XML](https://docs.sonarqube.org/latest/analysis/generic-test/) coverage report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by services like [SonarQube](https://www.sonarqube.org/) and [SonarCloud](https://about.sonarcloud.io/) * `ut_coverage_cobertura_reporter` - generates a basic Cobertura coverage (http://cobertura.sourceforge.net/xml/coverage-04.dtd) report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by services like TFS and Jenkins. Check this link for an example of XML generated by Java: https://raw.githubusercontent.com/jenkinsci/cobertura-plugin/master/src/test/resources/hudson/plugins/cobertura/coverage-with-data.xml ## Security model -utPLSQL code coverage uses DBMS_PROFILER to gather information about the execution of code under test and therefore follows the [DBMS_PROFILER's Security Model](https://docs.oracle.com/database/121/ARPLS/d_profil.htm#ARPLS67465). +utPLSQL code coverage uses DBMS_PROFILER to gather information about the execution of code under test and therefore follows the [DBMS_PROFILER's Security Model](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_PROFILER.html#GUID-DEE5EA97-14AA-4AF2-A5F7-4AB1D004B99F). In order to be able to gather coverage information, the user executing unit tests needs to be either: + * The owner of the code that is being tested * Have the following privileges to be able to gather coverage on code owned by other users: * `create any procedure` system privilege @@ -30,20 +36,21 @@ In order to be able to gather coverage information, the user executing unit test If you have `execute` privilege on the code that is being tested, but do not have `create any procedure` system privilege, then the code that is being tested will be reported as not covered (coverage = 0%). If you have `execute` privilege only on the unit tests, but do not have `execute` privilege on the code that is being tested, the code will not be reported by coverage - as if it did not exist in the database. -If the code that is being tested is complied as NATIVE, the code coverage will not be reported as well. +If the code that is being tested is compiled as NATIVE, the code coverage will not be reported as well. -## Running unit tests with coverage -Using the code coverage functionality is as easy as using any other [reporter](reporters.md) for the utPLSQL test-run. You just run your tests from your preferred SQL tool and save the reporter results to a file. -All you need to do is pass the constructor of the reporter to the `ut.run` procedure call. +## Manually running unit tests with coverage +Using the code coverage functionality is as easy as using any other [reporter](reporters.md) for the utPLSQL test-run. Run your tests from your preferred SQL tool and save the reporter results to a file. +All you need to do, is pass the constructor of the reporter to the `ut.run` procedure call. Example: -```sql +```sql linenums="1" +set serveroutput on begin ut.run(ut_coverage_html_reporter()); end; / ``` -The above command executes all unit tests in the **current schema**, gathers information about code coverage and outputs the HTML report as text into DBMS_OUTPUT. +The above command executes all unit tests in the **current schema**, gathers information about code coverage for all sources in that schema and outputs the HTML report as text into DBMS_OUTPUT. The `ut_coverage_html_reporter` will produce an interactive HTML report. You can see a sample of code coverage for the utPLSQL project [here](https://utplsql.github.io/utPLSQL-coverage-html/) The report provides summary information with a list of source code that should be covered. @@ -55,16 +62,18 @@ The report allow you to navigate to each source file and inspect line by line co ![Coverage Details page](../images/coverage_html_details.png) -#### Oracle 12.2 extended coverage with profiler and block coverage -Using data collected from profiler and block coverage running parallel we are able to enrich information about coverage. -For every line recorded by the profiler if we have a partially covered same line in block coverage we will display that information -presenting line as partially covered, displaying number of block and how many blocks have been covered in that line.The feature will be automatically enabled in the Oracle database version 12.2 and higher, for older versions current profiler will be used. +### Partial Coverage -utPLSQL installation automatically creates tables needed by `dbms_plsql_code_coverage` on databases in versions above 12c Release 1. -Due to security model of `dbms_plsql_code_coverage` package, utPLSQL grants access to those tables and creates synonyms for those tables. -The access and synonyms will be public when using the headless installation. This approach avoids complexity of forcing every user of utPLSQL framework to create tables on their own. +#### What it does +Partial coverage combines two data sources — the profiler and block coverage — to give you a more detailed picture of which parts of your code have been tested. +When a line appears in both sources, and block coverage shows it was only partially covered, the display will reflect that: you'll see the line marked as partially covered, along with how many blocks on that line were executed out of the total. + +#### Setup and permissions +utPLSQL automatically creates the tables required by Oracle's dbms_plsql_code_coverage package — you don't need to create them manually. +Because of how dbms_plsql_code_coverage handles security, utPLSQL also takes care of granting access to those tables and creating the necessary synonyms. In a headless installation, both the grants and synonyms are made public, keeping things simple without requiring every utPLSQL user to set up their own tables. Sample output: + ![Package Coverage Summary](../images/extended_coverage_html_summary.png) ![Line Coverage Details](../images/extended_coverage_html_line.png) @@ -78,15 +87,15 @@ There are two distinct ways to gather code coverage: - Coverage on project files Those two options are mutually exclusive and cannot be mixed. -By default, when using one of coverage reporters, coverage is gathered on schema(s). +By default, when using one of coverage reporters, coverage is gathered on schema(s). +The database schema(s) containing the tests that were executed during the run will be reported on by coverage reporter. The parameters used to execute tests determine if utPLSQL will be using one approach or the other. +If parameter `a_source_file_mappings` or `a_source_files` is provided, then coverage is gathered on project files provided, otherwise coverage is gathered on schemas. -The database schema(s) containing the tests that were executed during the run will be reported on by coverage reporter. - -**Note** -> Regardless of the options provided, all unit test packages are excluded from the coverage report. Coverage reports provide information only about the **tested** code. +!!! note + Regardless of the options provided, all unit test packages are excluded from the coverage report. Coverage reports provide information only about the **tested** code. The default behavior of coverage reporting can be altered using invocation parameters. @@ -95,17 +104,16 @@ The default behavior of coverage reporting can be altered using invocation param To gather coverage for all objects in the **current schema** execute tests with coverage report as argument. This is the default reporting option and therefore additional coverage options don't need to be provided. -```sql +```sql linenums="1" exec ut.run(ut_coverage_html_reporter()); ``` -**Note** +!!! note -> When no filters are used, the size of the coverage report will depend two factors: -> - the type of report (does the report include source code or not) -> - the amount of source code in the database schema -> ->Keep in mind that for schemas containing a lot of code, it can take quite some time to produce the coverage report. + When no filters are used, the size of the coverage report will depend two factors:
+ - the type of report (does the report include source code or not)
+ - the amount of source code in the database schema
+ Keep in mind that for schemas containing a lot of code, it can take quite some time to produce the coverage report. #### Setting coverage schema(s) @@ -113,7 +121,7 @@ By default, coverage is gathered on the schema(s) derived from suite paths provi This is a valid approach as long as your test packages and tested code share the same schema. So when you run: -```sql +```sql linenums="1" exec ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter()); ``` Coverage will be gathered on both `user_1` and `user_2` objects. @@ -122,31 +130,140 @@ If your tests live in a different schema from the tested code you may override t In the example below, coverage will still be gathered for `user_1` and `user_2` objects, even thought we run the tests located in schema `unit_test_schema` -```sql -exec ut.run('unit_test_schema', ut_coverage_html_reporter(), a_coverage_schemes => ut_varchar2_list('user_1','user_2') ); +```sql linenums="1" +begin + ut.run('unit_test_schema', ut_coverage_html_reporter(), + a_coverage_schemes => ut_varchar2_list('user_1','user_2') + ); +end; ``` #### Filtering objects in coverage reports -There are two options that can be used to narrow down the scope of coverage report: -- `a_include_objects` - list of `[object_owner.].object_name` to be included in the coverage report -- `a_exclude_objects` - list of `[object_owner.].object_name` to be excluded from the coverage report +Multiple parameters can be used to define the scope of coverage report. + +- `a_source_file_mappings ( ut_file_mappings )` - map of filenames to database objects. It is used for file-based coverage - see below. +- `a_include_schema_expr ( varchar(4000) )` - string of regex expression of schemas to be included in the coverage report. Case-insensitive. +- `a_include_object_expr ( varchar(4000) )` - string of regex expression of objects ( without schema name ) to be included in the coverage report. Case-insensitive. +- `a_exclude_schema_expr ( varchar(4000) )` - string of regex expression of schemas to be excluded from the coverage report. Case-insensitive. +- `a_exclude_object_expr ( varchar(4000) )` - string of regex expression of objects ( without schema name ) to be excluded from the coverage report. Case-insensitive. +- `a_coverage_schemes ( ut_varchar2_list )` - List of database schema names to gather coverage on. +- `a_include_objects ( ut_varchar2_list )` - list of `[object_owner.]object_name` to be included in the coverage report. +- `a_exclude_objects ( ut_varchar2_list )` - list of `[object_owner.]object_name` to be excluded from the coverage report. + +You may specify both _include_ and _exclude_ options to gain more control over what needs to be included / excluded from the coverage report. + + +!!! warning "Important note" + + The order of priority is for evaluation of include/exclude filter parameters is as follows.
+ - if `a_source_file_mappings` is defined then all include/exclude parameters are ignored (see section below for usage of `a_source_file_mappings` parameter )
+ - else if `a_include_schema_expr` or `a_include_object_expr` parameter is specified then parameters `a_coverage_schemes` and `a_include_objects` are ignored
+ - else if `a_include_objects` is specified then the coverage is gathered only on specified database objects.
+ - if `a_coverage_schemes` is specified then those schemas are used for objects in `a_include_objects` without schema name
+ - if `a_coverage_schemes` is not specified then schema from paths (`a_paths`) parameter are used for objects in `a_include_objects` without schema name
+ - else if, only the `a_coverage_schemes` is specified then the coverage is gathered only on specified database schemas
+ - else if no coverage specific parameters are provided coverage is gathered on all schemas specified in paths passed to run procedure
+ - else if no paths were specified, the coverage is gathered on current schema of the session running the tests + +The exclude parameters are not mutually-exclusive and can be mixed together. All of exclude parameters are always applied. + +Example: Limiting coverage by schema regex. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_include_schema_expr => '^ut3_develop' + ); +end; +``` +Will result in showing coverage for all schemas that match regular expression `^ut3_develop` + +Example: Limiting coverage by schema regex with parameter `a_include_objects` ignored. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_include_schema_expr => '^ut3_develop', a_include_objects => ut_varchar2_list( 'ut3_tester_helper.regex_dummy_cov' ) + ); +end; +``` +Will result in showing coverage for all schemas that match regular expression `^ut3_develop`. + +Example: Limiting coverage by object regex. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_include_object_expr => 'regex123' + ); +end; +``` +Will result in showing coverage for all objects that name match regular expression `regex123`. + +Example: Limiting coverage by object regex with parameter `a_include_objects` ignored. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_include_object_expr => 'utl', a_include_objects => ut_varchar2_list( 'user_2.utils_package' ) + ); +end; +``` +Will result in showing coverage for all objects that name match regular expression `utl`. + +Example: Limiting coverage by excluding schema with regex. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_exclude_schema_expr => 'er_1$' + ); +end; +``` +Will result in showing coverage for objects in all schema except schemas that are matching regular expression `er_1$` -You may specify both _include_ and _exclude_ object lists to gain more control over what needs to be included / excluded from the coverage report. +Example: Limiting coverage by excluding schema with regex and excluding specific object. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_exclude_schema_expr => 'er_1$', a_exclude_objects => ut_varchar2_list( 'user_2.utils_package' ) + ); +end; +``` +Will result in showing coverage for objects in all schemas except schemas that are matching regular expression `er_1$` +Will also exclude object `user_2.utils_package` from coverage report + +Example: Limiting coverage by excluding objects with regex. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_exclude_object_expr => 'utl' + ); +end; +``` +Will result in showing coverage for all objects that name is not matching regular expression `utl`. -The object owner is optional in the object list. -If you do not provide an object owner, the _include_/_exclude_ list will be considered for every schema used for coverage gathering (as described above). +Example: Limiting coverage by excluding objects with regex with parameter `a_exclude_objects` ignored. +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_exclude_object_expr => 'utl', a_exclude_objects => ut_varchar2_list( 'user_2.utils_package' ) + ); +end; +``` +Will result in showing coverage for all objects that name is not matching regular expression `utl`. +Will also exclude object `user_2.utils_package` from coverage report Example: Limiting coverage by object name, for tested code located in the same schema as the unit tests. -```sql -exec ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), a_include_objects=>ut_varchar2_list('award_bonus')); +```sql linenums="1" +begin + ut.run(ut_varchar2_list('user_1','user_2'), ut_coverage_html_reporter(), + a_include_objects=>ut_varchar2_list('award_bonus') + ); +end; ``` Executes all tests in schemas: `user_1` and `user_2`. Coverage will only be reported on objects `user_1.award_bonus`, `user_2.award_bonus` Example: Limiting coverage by object name, for tested code located in different schemas than the unit tests. -```sql +```sql linenums="1" begin ut.run( 'unit_test_schema', ut_coverage_html_reporter(), @@ -159,7 +276,7 @@ Executes all tests in schema `unit_test_schema`. Coverage will only be reported Objects that do not exist in the database but were specified in `a_include_objects` will be ignored. Example: Limiting coverage by object owner and name. -```sql +```sql linenums="1" begin ut.run( 'unit_test_schema', ut_coverage_html_reporter(), @@ -172,7 +289,7 @@ Executes all tests in schema `unit_test_schema`. Coverage will only be reported The `a_exclude_objects` can be used in the same way as `a_include_objects`. Example: Excluding objects from coverage report by providing a list of object owner/name to be excluded. -```sql +```sql linenums="1" begin ut.run( 'unit_test_schema.test_award_bonus', ut_coverage_html_reporter(), @@ -182,25 +299,27 @@ end; ``` Executes test `test_award_bonus` in schema `unit_test_schema`. Coverage will be reported on all objects in schema `ut3_user` except the `betwnstr` object. -**Note** -> Filtering using `a_include_objects` and `a_exclude_objects` is only applicable when gathering coverage for a schema. Those filters are not applied when reporting coverage on project files. +!!! note + Filtering using `a_include_objects` and `a_exclude_objects` is only applicable when gathering coverage for a schema. Those filters are not applied when reporting coverage on project files. + +!!! note + When running coverage on schema objects, all source code of package bodies, functions, procedures, type bodies and triggers that were not executed will be reported as having 0% code coverage and all source code lines will show as uncovered. + This is different from the behavior when gathering coverage on project files. -**Note** -> When running coverage on schema objects, all source code of package bodies, functions, procedures, type bodies and triggers that were not executed will be reported as having 0% code coverage and all source code lines will show as uncovered. -> This is different than the behavior when gathering coverage on project files. ### Project based Coverage -utPLSQL provides reporters that produce reports consumable by external tools like `Sonar`/`SonarCloud` & `Coveralls`. +utPLSQL provides reporters that produce reports consumable by external tools like `Sonar`/`SonarCloud`. -Services like Sonar, Coveralls and others perform analysis based on source code in project files. +Services like Sonar and others perform analysis based on source code in project files. They are abstracted from database, schema names, packages, procedures and functions, and operate on a more generic concept of project source code. To be able to effectively use reporters dedicated for those tools, utPLSQL provides functionality for mapping database object names to project files. There are a few significant differences when running coverage on project files compared to running coverage on schema(s). + - Coverage is only reported on objects that were successfully mapped to project files. -- Project files (database objects) that were not executed at all are not reported as fully uncovered. It is up to the consumer (Sonar/Coveralls) to determine if project file should be considered as 0% coverage or just ignored. +- Project files (database objects) that were not executed at all are not reported as fully uncovered. It is up to the consumer (Sonar) to determine if project file should be considered as 0% coverage or just ignored. In order to successfully use coverage on project files, those files must be mapped to database objects. @@ -234,21 +353,23 @@ C: ``` By default, utPLSQL will convert file paths into database objects using the following regular expression `/(((\w|[$#])+)\.)?((\w|[$#])+)\.(\w{3})$` + - object owner (if it is present) is identified by the expression in the second set of brackets - object name is identified by the expression in the fourth set of brackets - object type is identified by the expression in the sixth set of brackets -**Note** -> utPLSQL will replace any '\\' with '/' for the purpose of mapping files to objects. The paths shown in the results will remain (contain '\' where it was present). -> This is done to simplify the syntax of regular expressions. Regular expression will always use '/' as a directory separator on a file path regardless of whether you're on a Windows or Unix system. +!!! note + utPLSQL will replace any '\\' with '/' for the purpose of mapping files to objects. The paths shown in the results will remain (contain '\' where it was present). + This is done to simplify the syntax of regular expressions. Regular expression will always use '/' as a directory separator on a file path regardless of whether you're on a Windows or Unix system. -**Note** -> Below examples assume that you have downloaded latest version of [utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli/releases) and extracted it into your projects root directory (my_project). -> The examples assume that you run the utPLSQL-cli from `my_project` directory. +!!! note + Below examples assume that you have downloaded latest version of [utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli/releases) + and extracted it into your projects root directory + and that you run the utPLSQL-cli from that directory. Windows: -``` +```pwsh utPLSQL-cli\bin\utplsql run test_runner/pass@db_host:db_port/db_service_name ^ -p=hr,hotel ^ -source_path=sources ^ @@ -268,6 +389,7 @@ utPLSQL-cli/bin/utplsql run test_runner/pass@db_host:db_port/db_service_name \ ``` The above commands will: + - connect as user `test_runner` - run all utPLSQL v3 tests for users `hr`, `hotel` - map database code to project files in `sources` directory and save code coverage results into `coverage.html` @@ -318,7 +440,7 @@ Note that the owner/name/type subexpressions don't need to be explicitly specifi In the below example, they were specified explicitly only for `source_path`, `test_path` doesn't have subexpressions specified and so they are default (2/3/4). Windows: -``` +```pwsh utPLSQL-cli\bin\utplsql run test_runner/pass@db_url ^ -p=hr,hotel ^ -source_path=sources ^ @@ -381,7 +503,7 @@ For the database objects mapped to `souces` directory user `code_owner` will be For the database objects mapped to `tests` directory user `tests_owner` will be used. Windows: -``` +```pwsh utPLSQL-cli\bin\utplsql run test_runner/pass@db_url ^ -p=tests_owner ^ -source_path=sources -owner=code_owner ^ @@ -400,9 +522,9 @@ utPLSQL-cli/bin/utplsql run test_runner/pass@db_url \ -f=ut_sonar_test_reporter -o=test_results.xml ``` -**Note** -> When the project folder structure does not provide any information about source code owner and test owner, you can specify the owner for tests and owner for code explicitly. -> Such project configuration supports only single-owner for source code and single owner for tests. +!!! note + When the project folder structure does not provide any information about source code owner and test owner, you can specify the owner for tests and owner for code explicitly. + Such project configuration supports only single-owner for source code and single owner for tests. Tested code is mapped to files in `coverage.html` @@ -479,7 +601,7 @@ C: Windows: -``` +```pwsh utPLSQL-cli\bin\utplsql run test_runner/pass@db_url ^ -p=hr,hotel ^ -source_path=sources ^ @@ -530,7 +652,8 @@ Unit test code is mapped to files in `test_results.xml` #### Object-file mapping rules -In order to allow deterministic and accurate mapping of database source-code into project files, the project directory and file structure needs to meet certain criteria. +In order to allow deterministic and accurate mapping of database source-code into project files, the project directory and file structure needs to meet certain criteria. + - Source code is kept separate from test code (separate directories) - Each database (source-code) object is stored in an individual file. Package/type specification is kept separate from its body. - File name (file path) contains the name of database object @@ -544,6 +667,7 @@ In order to allow deterministic and accurate mapping of database source-code int The `ut.run` command provides interface to map project into database objects when executing tests. While it is much easier to perform mapping directly from command line, it is possible to achieve similar functionality from any SQL client. The main differences when using the `ut.run(...)` command, will be: + - you can only use single reporter and therefore will get only one report from test execution - you need to provide fill list of project files rather than point to `sources` and `tests` directories @@ -595,7 +719,7 @@ C: To execute all tests and map database source code into source file names you could use the following command in any SQL client: -```sql +```sql linenums="1" begin ut.run( ut_varchar2_list('hr','hotel'), @@ -627,7 +751,7 @@ end; ``` To execute all tests and map database tests code into test file names you could use the following command in any SQL client: -```sql +```sql linenums="1" begin ut.run( ut_varchar2_list('hr','hotel'), @@ -660,7 +784,7 @@ begin end; ``` -## Reporting coverage outside of utPLSQL +## Reporting coverage outside utPLSQL and in parallel sessions utPSLQL allows fo standalone reporting code coverage across multiple database sessions. This functionality enables coverage reporting for external testing tools. @@ -672,7 +796,7 @@ Following API calls enable the standalone coverage reporting. - `.get_report_cursor( ... )` - coverage reporters function producing coverage report as ref-cursor Example: -```sql +```sql linenums="1" --SESSION 1 -- gather coverage on code using specific coverage_run_id value declare @@ -689,7 +813,7 @@ end; / ``` -```sql +```sql linenums="1" --SESSION 2 -- alternative approach -- gather coverage on code using specific coverage_run_id value @@ -701,9 +825,9 @@ exec ut_runner.coverage_stop(); ``` -```sql +```sql linenums="1" --SESSION 1 or SESSION2 2 or SESSION 3 --- run after calls in SESSION 1 & 2 are finshed +-- run after calls in SESSION 1 & 2 are finished -- retrieve coverage report in HTML format coverage_run_id value select * from table( @@ -715,9 +839,9 @@ select * ); ``` -```sql +```sql linenums="1" --SESSION 1 or SESSION2 2 or SESSION 3 --- run after calls in SESSION 1 & 2 are finshed +-- run after calls in SESSION 1 & 2 are finished declare l_results_cursor sys_refcursor; begin @@ -733,26 +857,30 @@ end; ``` Specification of parameters for `get_report` and `get_report_cursor` -```sql +```sql linenums="1" function get_report( a_coverage_options ut_coverage_options, a_client_character_set varchar2 := null ) return ut_varchar2_rows pipelined ``` -```sql +```sql linenums="1" function get_report_cursor( a_coverage_options ut_coverage_options, a_client_character_set varchar2 := null ) return sys_refcursor ``` -```sql +```sql linenums="1" ut_coverage_options( coverage_run_id raw, schema_names ut_varchar2_rows := null, exclude_objects ut_varchar2_rows := null, include_objects ut_varchar2_rows := null, file_mappings ut_file_mappings := null + include_schema_expr varchar2(4000) := null, + include_object_expr varchar2(4000) := null, + exclude_schema_expr varchar2(4000) := null, + exclude_object_expr varchar2(4000) := null ); ``` @@ -768,6 +896,10 @@ The `a_coverage_options` parameter is used to control the scope and formatting o - `exclude_objects` - optional - list of object names to exclude from report - data-type `UT_VARCHAR2_ROWS` - `include_objects` - optional - list of object names to gather coverage on - data-type `UT_VARCHAR2_ROWS` - `file_mappings` - optional - list of schema names to gather coverage on - data-type `UT_FILE_MAPPINGS` +- `include_schema_expr` - optional - regular expression to match against schema name to include in coverage - data-type `VARCHAR2(4000)` +- `include_object_expr` - optional - regular expression to match against object name to include in coverage - data-type `VARCHAR2(4000)` +- `exclude_schema_expr` - optional - regular expression to match against schema name to exclude in coverage - data-type `VARCHAR2(4000)` +- `exclude_object_expr` - optional - regular expression to match against object name to exclude in coverage - data-type `VARCHAR2(4000)` `coverage_run_id` parameter identifies a common coverage run. The valid value type for that parameter is RAW(32). It is recommended to use `sys_guid()` to generate a common, unique identifier for a specific coverage run. @@ -775,4 +907,4 @@ If the identifier is not unique, previous runs of coverage that used the same `c For details on the meaning of `schema_names`, `exclude_objects`, `include_objects`, `file_mappings` see sections above. Note that data-types of include/exclude/schema lists are different when calling `ut.run` vs. calling `get_report/get_report_cursor`. - + diff --git a/docs/userguide/exception-reporting.md b/docs/userguide/exception-reporting.md index 6da6a5fbe..2a98ce0b4 100644 --- a/docs/userguide/exception-reporting.md +++ b/docs/userguide/exception-reporting.md @@ -1,23 +1,28 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) -# Exception handling and reporting - -The utPLSQL is responsible for handling exceptions wherever they occur in the test run. utPLSQL is trapping most of the exceptions so that the test execution is not affected by individual tests or test packages throwing an exception. -The framework provides a full stacktrace for every exception that was thrown. The stacktrace is clean and does not include any utPLSQL library calls in it. +utPLSQL is responsible for handling exceptions wherever they occur in the test run. The framework is trapping most of the exceptions so that the test execution is not affected by individual tests or test packages throwing an exception. +The framework provides a full stacktrace for every exception that was thrown. The reported stacktrace does not include any utPLSQL library calls in it. To achieve rerunability, the package state invalidation exceptions (ORA-04068, ORA-04061) are not handled and test execution will be interrupted if such exceptions are encountered. This is because of how Oracle behaves on those exceptions. Test execution can fail for different reasons. The failures on different exceptions are handled as follows: -* A test package without body - each `--%test` is reported as failed with exception, nothing is executed -* A test package with _invalid body_ - each `--%test` is reported as failed with exception, nothing is executed -* A test package with _invalid spec_ - package is not considered a valid unit test package and is excluded from execution. When trying to run a test package with invalid spec explicitly, exception is raised. Only valid specifications are parsed for annotations -* A test package that is raising an exception in `--%beforeall` - each `--%test` is reported as failed with exception, `--%test`, `--%beforeeach`, `--%beforetest`, `--%aftertest` and `--%aftereach` are not executed. `--%afterall` is executed to allow cleanup of whatever was done in `--%beforeall` -* A test package that is raising an exception in `--%beforeeach` - each `--%test` is reported as failed with exception, `--%test`, `--%beforetest` and `--%aftertest` is not executed. The `--%aftereach` and `--%afterall` blocks are getting executed to allow cleanup of whatever was done in `--%before...` blocks -* A test package that is raising an exception in `--%beforetest` - the `--%test` is reported as failed with exception, `--%test` is not executed. The `--%aftertest`, `--%aftereach` and `--%afterall` blocks are getting executed to allow cleanup of whatever was done in `--%before...` blocks -* A test package that is raising an exception in `--%test` - the `--%test` is reported as failed with exception. The execution of other blocks continues normally -* A test package that is raising an exception in `--%aftertest` - the `--%test` is reported as failed with exception. The execution of other blocks continues normally -* A test package that is raising an exception in `--%aftereach` - each `--%test` is reported as failed with exception. -* A test package that is raising an exception in `--%afterall` - all blocks of the package are executed, as the `--%afterall` is the last step of package execution. Exception in `--%afterall` is not affecting test results. A warning with exception stacktrace is displayed in the summary +| Problem / error | Framework behavior | +|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A test package **without body** | each `--%test` is reported as failed with exception, nothing is executed | +| A test package with **invalid body** | each `--%test` is reported as failed with exception, nothing is executed | +| A test package with **invalid spec** | package is not considered a valid unit test package and is excluded from execution. When trying to run a test package with invalid spec explicitly, exception is raised. Only valid specifications are parsed for annotations | +| A test package that is raising an exception in `--%beforeall` | each `--%test` is reported as failed with exception, `--%test`, `--%beforeeach`, `--%beforetest`, `--%aftertest` and `--%aftereach` are not executed. `--%afterall` is executed to allow cleanup of whatever was done in `--%beforeall` | +| A test package that is raising an exception in `--%beforeeach` | each `--%test` is reported as failed with exception, `--%test`, `--%beforetest` and `--%aftertest` is not executed. The `--%aftereach` and `--%afterall` blocks are getting executed to allow cleanup of whatever was done in `--%before...` blocks | +| A test package that is raising an exception in `--%beforetest` | the `--%test` is reported as failed with exception, `--%test` is not executed. The `--%aftertest`, `--%aftereach` and `--%afterall` blocks are getting executed to allow cleanup of whatever was done in `--%before...` blocks | +| A test package that is raising an exception in `--%test` | the `--%test` is reported as failed with exception. The execution of other blocks continues normally | +| A test package that is raising an exception in `--%aftertest` | the `--%test` is reported as failed with exception. The execution of other blocks continues normally | +| A test package that is raising an exception in `--%aftereach` | each `--%test` is reported as failed with exception. | +| A test package that is raising an exception in `--%afterall` | all blocks of the package are executed, as the `--%afterall` is the last step of package execution. Exception in `--%afterall` is not affecting test results. A warning with exception stacktrace is displayed in the summary | + + +!!! warning + If an exception is thrown in an `afterall` procedure then **no failure reported by utPLSQL**.
+ Framework will only report a warning on the suite that the `afterall` belongs to. Example of reporting with exception thrown in `%beforetest`: ```` diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 1d98faa6c..8849a7f47 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1,12 +1,13 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) + +## Expectation concepts -# 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. utPLSQL uses expectations and matchers to perform the check on the data. Example of an expectation -```sql +```sql linenums="1" begin ut.expect( 'the tested value' ).to_equal('the expected value'); end; @@ -21,15 +22,16 @@ FAILURE ``` Expectation is a combination of: + - the expected value - optional custom message for the expectation - the matcher used to perform comparison -- them matcher parameters (actual value), depending on the matcher type +- the matcher parameters (actual value), depending on the matcher type Matcher defines the comparison operation to be performed on expected (and actual) value. Pseudo-code: -```sql +```sql linenums="1" ut.expect( a_actual {data-type} [, a_message {varchar2}] ).to_( {matcher} ); ut.expect( a_actual {data-type} [, a_message {varchar2}] ).not_to( {matcher} ); ``` @@ -37,7 +39,7 @@ Pseudo-code: Expectations provide two variants of syntax that you can use. Both variants are functionally-equal but give different usage flexibility. Syntax where matcher is passed as parameter to the expectation: -```sql +```sql linenums="1" ut.expect( a_actual ).to_( {matcher} ); ut.expect( a_actual ).not_to( {matcher} ); -- example @@ -45,18 +47,18 @@ Syntax where matcher is passed as parameter to the expectation: ``` Shortcut syntax, where matcher is directly part of expectation: -```sql +```sql linenums="1" ut.expect( a_actual ).to_{matcher}; ut.expect( a_actual ).not_to_{matcher}; --example - ut.expect( 1 ).to_( be_null() ); + ut.expect( 1 ).to_be_null(); ``` When using shortcut syntax you don't need to surround matcher with brackets. Shortcut syntax is provided for convenience. If you would like to perform more dynamic checks in your code, you could pass the matcher into a procedure like in the below example: -```sql +```sql linenums="1" declare procedure do_check( p_actual varchar2, p_matcher ut_matcher ) is begin @@ -79,26 +81,30 @@ SUCCESS Actual: 'Alibaba' (varchar2) was expected to match: 'ali' , modifiers 'i' ``` -**Note:** -> The examples in the document will be only using shortcut syntax, to keep the document brief. +!!! note + In order to keep the document brief, the examples in the document are only using the standalone expectations syntax. + +## Using expectations -# Using expectations There are two ways to use expectations: - by invoking utPLSQL framework to execute suite(s) of utPLSQL tests - without invoking the utPLSQL framework - running expectations standalone ## Running expectations within utPLSQL framework -When expectations are ran a part of test suite, the framework tracks: + +When expectations are run as a part of a test suite, the framework tracks: + - status of each expectation - outcomes (messages) produced by each expectation - call stack to each expectation In this case: + - expectation results of are not sent directly to `dbms_output` - utPLSQL Reporters used when running suite decide on how the expectation results are formatted and displayed Example of test suite with an expectation: -```sql +```sql linenums="1" create or replace package test_divide as --%suite(Divide two numbers) @@ -156,17 +162,15 @@ Please read about different options for [running test suites](running-unit-tests ## Running expectations outside utPLSQL framework When expectations are invoked outside of utPLSQL framework the outputs from expectations are redirected straight to `dbms_output`. -**Note:** -> The output from expectation contains call stack trace only when expectation fails. -> Source code of the line which called the expectation is only reported when the line is part of in-database code (package) and the user calling expectation has privileges to see that source code. +!!! note + The output from expectation contains call stack trace only when expectation fails.
+ Source code of the line which called the expectation is only reported when the line is part of in-database code (package) and the user calling expectation has privileges to see that source code. -**Important** -> Please do not use expectations as part of your production code. They are not designed to be used as part ot your code. Expectations are meant to be used only as part of your day-to-day testing activities. +!!! warning "**Important**" + Please do not use expectations as part of your production code. They are not designed to be used as part of your code. Expectations are meant to be used only as part of your day-to-day testing activities. -**Note:** -> The examples in the document will be only using standalone expectations, to keep the document brief. -# Matchers +## Matchers utPLSQL provides the following matchers to perform checks on the expected and actual values. - `be_between( a_upper_bound {data-type}, a_lower_bound {data-type} )` @@ -186,11 +190,11 @@ utPLSQL provides the following matchers to perform checks on the expected and ac - `match( a_patter {varchar2} [, a_modifiers {varchar2}] )` ## Providing a custom message -You can provide a custom failure message as second argument for the expectation by passing message as the second parameter to the expectation. +You can provide a custom failure message by passing it as the second parameter to the expectation. `ut.expect( a_actual {data-type}, a_message {varchar2} ).to_{matcher}` Example: -````sql +````sql linenums="1" exec ut.expect( 'supercat', 'checked superhero-animal was not a dog' ).to_equal('superdog'); ```` @@ -205,6 +209,7 @@ If the message is provided, it is being added to the normal failure message retu This is mostly useful when your expectations accept dynamic content, as you can provide additional context to make failing test results more readable. In most cases, there is no need to provide custom message to expectation. This is because utPLSQL identifies: + - The test used to execute the expectation - The line number where the expectation is placed in your test code - The line text of the expectation @@ -212,7 +217,7 @@ In most cases, there is no need to provide custom message to expectation. This i Custom message is useful, if your expectation is placed in a shared procedure to perform a check and your test is using the procedure multiple times. Example: -```sql +```sql linenums="1" create or replace package shared_expectation_test is --%suite @@ -249,19 +254,20 @@ Failures: 1) the_test "Checking table ALL_USERS" Actual: 28 (number) was expected to equal: 0 (number) - at "UT3$USER#.SHARED_EXPECTATION_TEST.TABLE_IS_EMPTY", line 6 ut.expect( l_count, 'Checking table '||p_table_name ).to_equal(0); - at "UT3$USER#.SHARED_EXPECTATION_TEST.THE_TEST", line 11 + at "UT3_USER.SHARED_EXPECTATION_TEST.TABLE_IS_EMPTY", line 6 ut.expect( l_count, 'Checking table '||p_table_name ).to_equal(0); + at "UT3_USER.SHARED_EXPECTATION_TEST.THE_TEST", line 11 "Checking table ALL_TABLES" Actual: 55 (number) was expected to equal: 0 (number) - at "UT3$USER#.SHARED_EXPECTATION_TEST.TABLE_IS_EMPTY", line 6 ut.expect( l_count, 'Checking table '||p_table_name ).to_equal(0); - at "UT3$USER#.SHARED_EXPECTATION_TEST.THE_TEST", line 12 + at "UT3_USER.SHARED_EXPECTATION_TEST.TABLE_IS_EMPTY", line 6 ut.expect( l_count, 'Checking table '||p_table_name ).to_equal(0); + at "UT3_USER.SHARED_EXPECTATION_TEST.THE_TEST", line 12 Finished in .066344 seconds 1 tests, 1 failed, 0 errored, 0 disabled, 0 warning(s) ``` In the tests results window you can see the list of failed expectations for a test as well as: + - the additional message for expectation - the reason why the expectation failed - the line number of the expectation @@ -273,7 +279,7 @@ In the tests results window you can see the list of failed expectations for a te Expectations provide a very convenient way to perform a check on a negated matcher. Syntax to check for matcher evaluating to true: -```sql +```sql linenums="1" begin ut.expect( a_actual {data-type} ).to_{matcher}; ut.expect( a_actual {data-type} ).to_( {matcher} ); @@ -281,7 +287,7 @@ end; ``` Syntax to check for matcher evaluating to false: -```sql +```sql linenums="1" begin ut.expect( a_actual {data-type} ).not_to_{matcher}; ut.expect( a_actual {data-type} ).not_to( {matcher} ); @@ -291,7 +297,7 @@ end; If a matcher evaluated to NULL, then both `to_` and `not_to` will cause the expectation to report failure. Example: -```sql +```sql linenums="1" declare l_actual boolean; begin @@ -312,37 +318,38 @@ FAILURE ``` Since NULL is neither *true* nor *false*, both expectations will report failure. -# Supported data types +## Supported data types The matrix below illustrates the data types supported by different matchers. -| Matcher | blob | boolean | clob | date | number | timestamp | timestamp
with
timezone | timestamp
with
local
timezone | varchar2 | interval
year
to
month | interval
day
to
second | cursor | nested
table
/ varray | object | json | -| :---------------------: | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | :--: | -| **be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **be_false** | | X | | | | | | | | | | | | | | -| **be_true** | | X | | | | | | | | | | | | | | -| **be_greater_than** | | | | X | X | X | X | X | | X | X | | | | | -| **be_greater_or_equal** | | | | X | X | X | X | X | | X | X | | | | | -| **be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | | -| **be_less_than** | | | | X | X | X | X | X | | X | X | | | | | -| **be_between** | | | | X | X | X | X | X | X | X | X | | | | | -| **equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| **contain** | | | | | | | | | | | | X | X | X | | -| **match** | | | X | | | | | | X | | | | | | | -| **be_like** | | | X | | | | | | X | | | | | | | -| **be_empty** | X | | X | | | | | | | | | X | X | | X | -| **have_count** | | | | | | | | | | | | X | X | | X | - - -# Expecting exceptions +| Matcher | blob | boolean | clob | date | number | timestamp | timestamp
with
timezone | timestamp
with
local
timezone | varchar2 | interval
year
to
month | interval
day
to
second | cursor | nested
table
/ varray | object | json | +| :-----------------------: | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | :--: | +| **be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **be_false** | | X | | | | | | | | | | | | | | +| **be_true** | | X | | | | | | | | | | | | | | +| **be_greater_than** | | | | X | X | X | X | X | | X | X | | | | | +| **be_greater_or_equal** | | | | X | X | X | X | X | | X | X | | | | | +| **be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | | +| **be_less_than** | | | | X | X | X | X | X | | X | X | | | | | +| **be_between** | | | | X | X | X | X | X | X | X | X | | | | | +| **equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| **contain** | | | | | | | | | | | | X | X | X | | +| **match** | | | X | | | | | | X | | | | | | | +| **be_like** | | | X | | | | | | X | | | | | | | +| **be_empty** | X | | X | | | | | | | | | X | X | | X | +| **have_count** | | | | | | | | | | | | X | X | | X | +| **be_within().of_()** | | | | X | X | X | X | X | | | | | | | | +| **be_within_pct().of_()** | | | | | X | | | | | | | | | | | + +## Expecting exceptions Testing is not limited to checking for happy-path scenarios. When writing tests, you often want to validate that in specific scenarios, an exception is thrown. Use the `--%throws` annotation, to test for expected exceptions. Example: -```sql +```sql linenums="1" create or replace function divide(x varchar2, y varchar2) return number is begin return x/y; @@ -381,19 +388,19 @@ Finished in .009229 seconds 1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` -For more details see documentation of the [`--%throws` annotation.](annotations.md#throws-annotation) +For more details see documentation of the [`--%throws` annotation.](annotations.md#throws) -# Matchers +## Matchers -You can choose different matchers to validate the your PL/SQL code is working as expected. +You can choose different matchers to validate that your PL/SQL code is working as expected. -## be_between +### be_between Validates that the actual value is between the lower and upper bound. Example: -```sql +```sql linenums="1" declare l_timestamp timestamp := current_timestamp; l_timestamp_tz timestamp with time zone := systimestamp; @@ -445,17 +452,17 @@ SUCCESS Actual: 'Abb' (varchar2) was expected to be between: 'Aba' and 'Abc' ``` -## be_empty +### be_empty Unary matcher that validates if the provided dataset is empty. Can be used with `BLOB`,`CLOB`, `refcursor` or `nested table`/`varray` passed as `ANYDATA` -**Note:** -BLOB/CLOB that is initialized is not NULL but it is actually equal to `empty_blob()`/`empty_clob()`. +!!! note + BLOB/CLOB that is initialized is not NULL but it is actually equal to `empty_blob()`/`empty_clob()`. Example: -```sql +```sql linenums="1" declare l_cursor sys_refcursor; begin @@ -494,11 +501,11 @@ FAILURE at "anonymous block", line 9 ``` -## be_false +### be_false Unary matcher that validates if the provided value is false. Usage: -```sql +```sql linenums="1" begin ut.expect( ( 1 = 0 ) ).to_be_false(); ut.expect( ( 1 = 1 ) ).to_( be_false() ); @@ -522,11 +529,11 @@ SUCCESS Actual: TRUE (boolean) was expected not to be false ``` -## be_greater_or_equal +### be_greater_or_equal Checks if the actual value is greater or equal than the expected. Usage: -```sql +```sql linenums="1" begin ut.expect( sysdate ).to_be_greater_or_equal( sysdate - 1 ); ut.expect( sysdate ).to_( be_greater_or_equal( sysdate + 1 ) ); @@ -550,11 +557,11 @@ SUCCESS Actual: 2019-07-07T22:43:29 (date) was expected not to be greater or equal: 2019-07-08T22:43:29 (date) ``` -## be_greater_than +### be_greater_than Checks if the actual value is greater than the expected. Usage: -```sql +```sql linenums="1" begin ut.expect( 2 ).to_be_greater_than( 1 ); ut.expect( 0 ).to_( be_greater_than( 1 ) ); @@ -578,11 +585,11 @@ SUCCESS Actual: 0 (number) was expected not to be greater than: 1 (number) ``` -## be_less_or_equal +### be_less_or_equal Checks if the actual value is less or equal than the expected. Usage: -```sql +```sql linenums="1" begin ut.expect( 3 ).to_be_less_or_equal( 3 ); ut.expect( 4 ).to_( be_less_or_equal( 3 ) ); @@ -606,11 +613,11 @@ SUCCESS Actual: 4 (number) was expected not to be less or equal: 3 (number) ``` -## be_less_than +### be_less_than Checks if the actual value is less than the expected. Usage: -```sql +```sql linenums="1" begin ut.expect( 3 ).to_be_less_than( 2 ); ut.expect( 0 ).to_( be_less_than( 2 ) ); @@ -634,19 +641,17 @@ FAILURE at "anonymous block", line 5 ``` -## be_like +### be_like Validates that the actual value is like the expected expression. Syntax: `ut.expect( a_actual ).to_be_like( a_mask [, a_escape_char] )` -Parameters `a_mask` and `a_escape_char` represent valid parameters of the [Oracle LIKE condition](https://docs.oracle.com/database/121/SQLRF/conditions007.htm#SQLRF52142). - -If you use Oracle Database version 11.2.0.4, you may run into Oracle Bug 14402514: WRONG RESULTS WITH LIKE ON CLOB USING ESCAPE CHARACTER. In this case we recommend to use `match` instead of `be_like`. +Parameters `a_mask` and `a_escape_char` represent valid parameters of the [Oracle LIKE condition](https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Pattern-matching-Conditions.html). Usage: -```sql +```sql linenums="1" begin ut.expect( 'Lorem_impsum' ).to_be_like( '%rem%'); ut.expect( 'Lorem_impsum' ).to_be_like( '%rem\_i%', '\' ); @@ -672,11 +677,11 @@ SUCCESS Actual: 'Lorem_impsum' (varchar2) was expected not to be like: '%reM%' ``` -## be_not_null +### be_not_null Unary matcher that validates if the actual value is not null. Usage: -```sql +```sql linenums="1" begin ut.expect( to_clob('ABC') ).to_be_not_null(); ut.expect( to_clob('') ).to_( be_not_null() ); @@ -700,11 +705,11 @@ SUCCESS Actual: NULL (varchar2) was expected not to be not null ``` -## be_null +### be_null Unary matcher that validates if the actual value is null. Usage: -```sql +```sql linenums="1" begin ut.expect( '' ).to_be_null(); ut.expect( 0 ).to_( be_null() ); @@ -728,11 +733,11 @@ SUCCESS Actual: 0 (number) was expected not to be null ``` -## be_true +### be_true Unary matcher that validates if the provided value is true. Usage: -```sql +```sql linenums="1" begin ut.expect( ( 1 = 0 ) ).to_be_true(); ut.expect( ( 1 = 1 ) ).to_( be_true() ); @@ -756,13 +761,13 @@ FAILURE at "anonymous block", line 5 ``` -## have_count +### have_count Unary matcher that validates if the provided dataset count is equal to expected value. -Can be used with `refcursor` , `json`or `table type` +Can be used with `refcursor`, `json` or `table type` Usage: -```sql +```sql linenums="1" declare l_cursor sys_refcursor; l_collection ut_varchar2_list; @@ -792,17 +797,17 @@ FAILURE at "anonymous block", line 11 ``` -## match +### match Validates that the actual value is matching the expected regular expression. Syntax: `ut.expect( a_actual ).to_match( a_pattern [, a_modifiers] );` -Parameters `a_pattern` and `a_modifiers` represent a valid regexp pattern accepted by [Oracle REGEXP_LIKE condition](https://docs.oracle.com/database/121/SQLRF/conditions007.htm#SQLRF00501) +Parameters `a_pattern` and `a_modifiers` represent a valid regexp pattern accepted by [Oracle REGEXP_LIKE condition](https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Pattern-matching-Conditions.html#GUID-D2124F3A-C6E4-4CCA-A40E-2FFCABFD8E19) Usage: -```sql +```sql linenums="1" begin ut.expect( '123-456-ABcd' ).to_match( '\d{3}-\d{3}-[a-z]{4}', 'i' ); ut.expect( 'some value' ).to_( match( '^some.*' ) ) ; @@ -826,9 +831,9 @@ FAILURE at "anonymous block", line 5 ``` -## equal -The equal matcher is very restrictive. Test using this matcher succeeds only when the compared data-types are exactly the same. -If you are comparing `varchar2` to a `number` will fail even if the text contains the same numeric value as the number. +### equal +The `equal` matcher is very restrictive. Test using this matcher succeeds only when the compared data-types are exactly the same. +If you are comparing a `varchar2` to a `number`, it will fail even if the text contains the same numeric value as the number. The matcher will also fail when comparing a `timestamp` to a `timestamp with timezone` data-type etc. The matcher enables detection of data-type changes. @@ -840,7 +845,7 @@ Syntax: `ut.expect( a_actual ).to_equal( a_expected [, a_nulls_are_equal])[.advanced_options]` Example usage -```sql +```sql linenums="1" declare l_actual varchar2(20); l_expected varchar2(20); @@ -907,27 +912,27 @@ FAILURE ``` -**Note:** ->**Comparing NULLs gives success by default ** -The `a_nulls_are_equal` parameter controls the behavior of a `null = null` comparison. -To change the behavior of `NULL = NULL` comparison pass the `a_nulls_are_equal => false` to the `equal` matcher. +!!! note + **by default, comparing NULL to NULL gives success**
+ The `a_nulls_are_equal` parameter controls the behavior of a `null = null` comparison.
+ To change the behavior of `NULL = NULL` comparison pass the `a_nulls_are_equal => false` to the `equal` matcher. -## contain +### contain This matcher supports only compound data-types comparison. It check if the actual set contains all values of expected subset. -When comparing data using `contain` matcher, the data-types of columns for compared compound types must be exactly the same. +When comparing data using the `contain` matcher, the data-types of columns for compared compound types must be exactly the same. The matcher supports all advanced comparison options as `equal` like: `include` , `exclude`, `join_by` etc.. The matcher is successful when actual data set contains all of the values from expected results. -The matcher will cause a test to fail if actual data set does not contain any of expected values. +The matcher will cause a test to fail if actual data set does not contain some of expected values. ![included_set](../images/venn21.gif) **Example 1.** -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -959,7 +964,7 @@ FAILURE When duplicate rows are present in expected data set, actual data set must also include the same amount of duplicates. **Example 2.** -```sql +```sql linenums="1" declare l_actual ut_varchar2_list; l_expected ut_varchar2_list; @@ -987,7 +992,7 @@ The negated version of `contain` ( `not_to_contain` ) is successful only when al ![not_overlapping_set](../images/venn22.gif) **Example 3.** -```sql +```sql linenums="1" declare l_actual ut_varchar2_list; l_expected ut_varchar2_list; @@ -1024,7 +1029,7 @@ FAILURE **Example 4.** -```sql +```sql linenums="1" declare l_actual ut_varchar2_list; l_expected ut_varchar2_list; @@ -1057,7 +1062,7 @@ FAILURE **Example 5.** -```sql +```sql linenums="1" declare l_actual ut_varchar2_list; l_expected ut_varchar2_list; @@ -1093,10 +1098,116 @@ SUCCESS DEF ``` +### to_be_within of + +Determines whether expected value is within range (tolerance) from another value. + +The logical formual used for calcuating the matcher is: +``` + result := ( abs( expected - actual ) <= distance ) +``` +The actual formula used for calculation is more complex to handle different data-types of expected/actual values as well as differnet types of distance value. +The matcher will fail if the `expected` and `actual` are more than `distance` apart from each other. +The matcher will fail if the dataypes of `expected` and `actual` are not the same. + +The matcher works with data-types: `number`, `date`, `timestamp`, `timestamp with time zone`, `timestamp with local time zone` +The data-types of compared values must match exactly and if type does not match, the expectation will fail. + +| expected/actual
data-type | distance data-type | +|:------------------------------:|:----------------------:| +| number | number | +| date | interval day to second | +| date | interval year to month | +| timestamp | interval day to second | +| timestamp | interval year to month | +| timestamp with time zone | interval day to second | +| timestamp with time zone | interval year to month | +| timestamp with local time zone | interval day to second | +| timestamp with local time zone | interval year to month | + + +The distance must be expressed as a non-negative number or non-negative interval. + +>Note: +> Interval year-to-moth as a distance is giving sucess if the distance between the given dates/timestamps evaluates to value less or equal of the specified interval +> Keep in mind that a checking for distance of `interval '0-1' year to month` will actuall be successful if the distance is less than a month and 15 days. +> This is due to how oracle evaluates conversion between timestamp difference converted to `year to month interval`. +> The behavior is similar to a call to `months_between()` function with results rounded to full monts ie. round(months_between(date, date)) + +**Example 1.** +```sql linenums="1" +begin + ut.expect(3).to_be_within(1).of_(4); +end; +/ +``` + +**Example 2.** +```sql linenums="1" +begin + ut.expect(3).to_be_within(1).of_(5); +end; +/ +``` + +Returns following output via DBMS_OUTPUT: +``` +Failures: + + 1) wihtin_test + Actual: 3 (number) was expected to be within 1 of 5 (number) + at "UT3_DEVELOP.UT_BE_WITHIN.OF_", line 48 l_result.expectation.to_(l_result ); + at "UT3_DEVELOP.TEST_BETWNSTR.WIHTIN_TEST", line 5 +``` + +**Example 3.** +```sql linenums="1" +begin + ut.expect(sysdate).to_be_within(interval '1' day).of_(sysdate+2); +end; +/ +``` + +Returns following output via DBMS_OUTPUT: +``` +Failures: + + 1) wihtin_test + Actual: 2020-06-07T13:32:58 (date) was expected to be within 1 day of 2020-06-09T13:32:58 (date) + at "UT3_DEVELOP.UT_BE_WITHIN.OF_", line 55 l_result.expectation.to_(l_result ); + at "UT3_DEVELOP.TEST_BETWNSTR.WIHTIN_TEST", line 5 +``` + + +### to_be_within_pct of + +Determines whether actual value is within percentage range of expected value. +The matcher only works with `number` data-type. + +The percentage deviation (distance) must be expressed as a non-negative number. +The formula used for calcuation of expectation is: +``` + result := ( ( distance ) * expected >= abs( expected - actual ) * 100 ) +``` + +**Example 1.** +```sql linenums="1" +begin + ut.expect(9).to_be_within_pct(10).of_(10); +end; +/ +``` + +``` +SUCCESS + Actual: 9 (number) was expected to be within 10 % of 10 (number) +``` + ## Comparing cursors, object types, nested tables and varrays utPLSQL is capable of comparing compound data-types including: + - ref cursors - object types - nested table/varray types @@ -1108,16 +1219,15 @@ utPLSQL is capable of comparing compound data-types including: - Columns in compound data are compared as **ordered list of elements** by default. Use `unordered_columns` option when order of columns in cursor is not relevant - Comparison of compound data is data-type aware. So a column `ID NUMBER` in a cursor is not the same as `ID VARCHAR2(100)`, even if they both hold the same numeric values. - Comparison of cursor columns containing `DATE` will only compare date part **and ignore time** by default. See [Comparing cursor data containing DATE fields](#comparing-cursor-data-containing-date-fields) to check how to enable date-time comparison in cursors. -- Comparison of cursor returning `TIMESTAMP` **columns** against cursor returning `TIMESTAMP` **bind variables** requires variables to be casted to proper precision. This is an Oracle SQL - PLSQL compatibility issue and usage of CAST is the only known workaround for now. See [Comparing cursor data containing TIMESTAMP bind variables](#comparing-cursor-data-containing-timestamp-bind-variables) for examples. +- Comparison of cursor returning `TIMESTAMP` **columns** against cursor returning `TIMESTAMP` **bind variables** requires variables to be cast to proper precision. This is an Oracle SQL - PLSQL compatibility issue and usage of CAST is the only known workaround for now. See [Comparing cursor data containing TIMESTAMP bind variables](#comparing-cursor-data-containing-timestamp-bind-variables) for examples. - To compare nested table/varray type you need to convert it to `anydata` by using `anydata.convertCollection()` - To compare object type you need to convert it to `anydata` by using `anydata.convertObject()` -- 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) +- 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](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 - define columns/attributes to include in comparison - and more ... @@ -1128,6 +1238,7 @@ For details on available options and how to use them, read the [advanced data co When comparing compound data, utPLSQL will determine the difference between the expected and the actual data. The diff includes: + - differences in column names, column positions and column data-type for cursor data - only data in columns/rows that differ @@ -1137,9 +1248,9 @@ Consider the following expected cursor data | ID (NUMBER)| FIRST_NAME (VARCHAR2) | LAST_NAME (VARCHAR2) | SALARY (NUMBER) | |:----------:|:----------------------:|:----------------------:|:---------------:| -| 1 | JACK | SPARROW | 10000 | -| 2 | LUKE | SKYWALKER | 1000 | -| 3 | TONY | STARK | 1000000 | +| 1 | JACK | SPARROW | 10000 | +| 2 | LUKE | SKYWALKER | 1000 | +| 3 | TONY | STARK | 1000000 | And the actual cursor data: @@ -1152,6 +1263,7 @@ And the actual cursor data: The two data-sets above have the following differences: + - column ID is misplaced (should be first column but is last) - column SALARY has data-type VARCHAR2 but should be NUMBER - column GENDER exists in actual but not in the expected (it is an Extra column) @@ -1163,7 +1275,7 @@ The two data-sets above have the following differences: utPLSQL will report all of the above differences in a readable format to help you identify what is not correct in the compared dataset. Below example illustrates, how utPLSQL will report such differences. -```sql +```sql linenums="1" declare l_actual sys_refcursor; l_expected sys_refcursor; @@ -1211,11 +1323,13 @@ FAILURE ``` utPLSQL identifies and reports on columns: + - column misplacement - column data-type mismatch - extra/missing columns When comparing rows utPLSQL: + - reports only mismatched columns when rows match - reports columns existing in both data-sets when whole row is not matching - reports whole extra (not expected) row from actual when actual has extra rows @@ -1225,6 +1339,7 @@ When comparing rows utPLSQL: ### Object and nested table data-type comparison examples When comparing object type / nested table / varray, utPLSQL will check: + - if data-types match - if data in the compared elements is the same. @@ -1234,7 +1349,7 @@ When diffing, utPLSQL will not check name and data-type of individual attribute Below examples demonstrate how to compare object and nested table data-types. Object type comparison. -```sql +```sql linenums="1" create type department as object(name varchar2(30)) / @@ -1262,7 +1377,7 @@ FAILURE ``` Table type comparison. -```sql +```sql linenums="1" create type department as object(name varchar2(30)) / create type departments as table of department @@ -1300,7 +1415,7 @@ FAILURE ``` Some of the possible combinations of anydata and their results: -```sql +```sql linenums="1" clear screen set serverout on set feedback off @@ -1484,18 +1599,18 @@ FAILURE ### Comparing cursor data containing DATE fields -**Important note** +!!! warning "Important" -utPLSQL uses XMLType internally to represent rows of the cursor data. This is by far the most flexible method and allows comparison of cursors containing LONG, CLOB, BLOB, user defined types and even nested cursors. -Due to the way Oracle handles DATE data type when converting from cursor data to XML, utPLSQL has no control over the DATE formatting. -The NLS_DATE_FORMAT setting from the moment the cursor was opened determines the formatting of dates used for cursor data comparison. -By default, Oracle NLS_DATE_FORMAT is timeless, so data of DATE datatype, will be compared ignoring the time component. + utPLSQL uses XMLType internally to represent rows of the cursor data. This is by far the most flexible method and allows comparison of cursors containing LONG, CLOB, BLOB, user defined types and even nested cursors.
+ Due to the way Oracle handles DATE data type when converting from cursor data to XML, utPLSQL has no control over the DATE formatting.
+ The NLS_DATE_FORMAT setting from the moment the cursor was opened determines the formatting of dates used for cursor data comparison.
+ By default, Oracle NLS_DATE_FORMAT is timeless, so data of DATE datatype, will be compared ignoring the time component.
You should surround cursors and expectations with procedures `ut.set_nls`, `ut.reset_nls`. This way, the DATE data in cursors will be properly formatted for comparison using date-time format. The example below makes use of `ut.set_nls`, `ut.reset_nls`, so that the date in `l_expected` and `l_actual` is compared using date-time formatting. -```sql +```sql linenums="1" clear screen alter session set nls_date_format='yyyy-mm-dd'; set serverout on @@ -1540,8 +1655,9 @@ drop table events; ``` In the above example: + - The first expectation is successful, as the `l_expected` cursor contains different date-time then the cursor returned by `get_events` function call -- The second expectation fails, as the column `event_date` will get compared as DATE without TIME (suing default current session NLS date format) +- The second expectation fails, as the column `event_date` will get compared as DATE without TIME (using default current session NLS date format) Output via DBMS_OUTPUT from the above example: ``` @@ -1578,7 +1694,7 @@ This applies to `timestamp`,`timestamp with timezone`, `timestamp with local tim Example below illustrates usage of `cast` operator to assure appropriate precision is applied on timestamp bind-variables in cursor result-set -```sql +```sql linenums="1" clear screen set serverout on set feedback off @@ -1641,9 +1757,13 @@ FAILURE at "anonymous block", line 32 ``` -# Comparing Json objects +## Comparing Json objects + +utPLSQL is capable of comparing json data-types of `json_element_t`, and also `json` **on Oracle 21 and above** + +!!! note + Whenever a database is upgraded to compatible version the utPLSQL needs to be reinstalled to pick up json changes. E.g. upgrade from 19c to 23 AI to enable `json` type compare. -utPLSQL is capable of comparing json data-types **on Oracle 12.2 and above**. ### Notes on comparison of json data @@ -1651,11 +1771,12 @@ utPLSQL is capable of comparing json data-types **on Oracle 12.2 and above**. - During comparison of json objects the order doesn't matter. - During comparison of json arrays the index of element is taken into account - To compare json you have to make sure its type of `json_element_t` or its subtypes +- From version 21 and above a native `json` type is supported. -Compare JSON example: -```sql +Compare JSON example using `json_element_t`: +```sql linenums="1" declare l_expected json_element_t; l_actual json_element_t; @@ -1736,8 +1857,8 @@ FAILURE at "anonymous block", line 59 ``` -Comparing parts of JSON example: -```sql +Comparing parts of JSON example using `json_element_t` subtypes: +```sql linenums="1" declare l_actual json_object_t; l_actual_extract json_array_t; diff --git a/docs/userguide/getting-started.md b/docs/userguide/getting-started.md index db7bff7cd..059ff9223 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.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) # Getting started with TDD and utPLSQL @@ -7,7 +7,7 @@ utPLSQL is designed in a way that allows you to follow Below is an example of building a simple function with TDD. -# Gather requirements +## Gather requirements We have a requirement to build a function that will return a substring of a string that is passed to the function. @@ -17,14 +17,14 @@ The function should accept three parameters: - start_position - end_position -# Create a test +## Create a test We will start from the bare minimum and move step by step, executing tests every time we make minimal progress. This way, we assure we don't jump ahead too much and produce code that is untested or untestable. -## Create test package +### Create test package -```sql +```sql linenums="1" create or replace package test_betwnstr as --%suite(Between string function) @@ -43,9 +43,9 @@ Finished in .451423 seconds 0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` -## Define specification for the test +### Define specification for the test -```sql +```sql linenums="1" create or replace package test_betwnstr as --%suite(Between string function) @@ -76,9 +76,9 @@ Finished in .509673 seconds Well our test is failing as the package specification requires a body. -## Define body of first test +### Define body of first test -```sql +```sql linenums="1" create or replace package body test_betwnstr as procedure basic_usage is @@ -110,11 +110,11 @@ Finished in .415851 seconds Our test is failing as the test suite package body is invalid. Looks like we need to define the function we want to test. -# Implement code to fulfill the requirement +## Implement code to fulfill the requirement -## Define tested function +### Define tested function -```sql +```sql linenums="1" create or replace function betwnstr( a_string varchar2, a_start_pos integer, a_end_pos integer ) return varchar2 is begin @@ -143,11 +143,11 @@ Finished in .375178 seconds So now we see that our test works but the function does not return the expected results. Let us fix this and continue from here. -## Fix the tested function +### Fix the tested function The function returned a string one character short, so we need to add 1 to the substr parameter. -```sql +```sql linenums="1" create or replace function betwnstr( a_string varchar2, a_start_pos integer, a_end_pos integer ) return varchar2 is begin @@ -169,14 +169,14 @@ Finished in .006077 seconds So our test is now passing, great! -# Refactor +## Refactor Once our tests are passing, we can safely refactor (restructure) the code as we have a safety harness in place to ensure that after the restructuring and cleanup of the code, everything is still working. One thing worth mentioning is that refactoring of tests is as important as refactoring of code. Maintainability of both is equally important. -# Further requirements +## Further requirements It seems like our work is done. We have a function that returns a substring from start position to end position. As we move through the process of adding tests, it's very important to think about edge cases. @@ -195,12 +195,12 @@ Here is a list of edge cases for our function: We should define expected behavior for each of these edge cases. Once defined we can start implementing tests for those behaviors and adjust the tested function to meet the requirements specified in the tests. -## Add test for additional requirement +### Add test for additional requirement A new requirement was added: Start position zero - should be treated as start position one -```sql +```sql linenums="1" create or replace package test_betwnstr as --%suite(Between string function) @@ -250,11 +250,11 @@ Finished in .232584 seconds Looks like our function does not work as expected for zero start position. -## Implementing the requirement +### Implementing the requirement Let's fix our function so that the new requirement is met -```sql +```sql linenums="1" create or replace function betwnstr( a_string varchar2, a_start_pos integer, a_end_pos integer ) return varchar2 is begin @@ -281,14 +281,14 @@ Finished in .012718 seconds Great! We have made some visible progress. -## Refactoring +### Refactoring When all tests are passing we can proceed with a safe cleanup of our code. The function works well, but we use the `return` twice, which is not the best coding practice. An alternative implementation could be cleaner. -```sql +```sql linenums="1" create or replace function betwnstr( a_string varchar2, a_start_pos integer, a_end_pos integer ) return varchar2 is begin @@ -308,7 +308,7 @@ Finished in .013739 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` -# Remaining requirements +## Remaining requirements You may continue on with the remaining edge cases from here. diff --git a/docs/userguide/install.md b/docs/userguide/install.md index e8ff255c5..649d7a6ca 100644 --- a/docs/userguide/install.md +++ b/docs/userguide/install.md @@ -1,27 +1,18 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) -# Supported database versions +## Supported database versions -utPLSQL is continuously tested against following versions of Oracle databases -* 11g R2 -* 12c -* 12c R2 -* 18c -* 19c +- Oracle Database 19c or newer (SE/EE/Cloud/Free) -We do our best to assure full compatibility with supported versions of Oracle databases [See](http://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf#page=6) +We do our best to assure full compatibility with **actively supported** versions of Oracle databases. +See [this page](https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf#page=6) for Oracle Database Releases and their Support Lifetime by Oracle. -# Requirements -utPLSQL will run on any Oracle Database version 11g relase 2 or above. +### Licensed features required -## Licensed features required +utPLSQL doesn't require any extra licensed features of Oracle database. It can be installed on any Edition of Oracle Database. -utPLSQL doesn't require any extra licensed features of Oracle database. It can be installed on any Standard Edition Oracle Database. - -In fact, it even supports Oracle 11g XE which is a free Oracle Database version with minimal features and storage limits. - -## Storage requirements +### Storage requirements utPLSQL will use tablespace for the following: - storage of annotation cache @@ -38,9 +29,9 @@ Profiler results may require regular purging to assure low space consumption. utPLSQl does not purge profiler tables as those tables can can be shared with other tools. -# Downloading utPLSQL +## Downloading utPLSQL -## Manual download +### Manual download - Go to GitHub releases page for utPLSQL [`https://github.com/utPLSQL/utPLSQL/releases`](https://github.com/utPLSQL/utPLSQL/releases) - Choose the version to download - latest is always greatest @@ -51,13 +42,13 @@ utPLSQl does not purge profiler tables as those tables can can be shared with ot The files have identical content but use different compression (tar / zip ) so choose whichever you prefer depending on your platform (Win/Mac/Unix/Linux). -## Scripted download of latest utPLSQL version +### Scripted download of latest utPLSQL version The below snippets can be used to download latest version of utPLSQL from github releases. After downloading follow the installation instructions in next sections of this document. -### Unix/Linux +#### Unix/Linux ```bash #!/bin/bash @@ -75,7 +66,7 @@ You may download with a one-liner if that is more convenient. curl -LOk $(curl --silent https://api.github.com/repos/utPLSQL/utPLSQL/releases/latest | awk '/browser_download_url/ { print $2 }' | grep ".zip\"" | sed 's/"//g') ``` -### Windows +#### Windows To run the script on windows you will need [PowerShell 3.0](https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/02/weekend-scripter-install-powershell-3-0-on-windows-7/) or above. You will also need .NET 4.0 Framework or above. @@ -104,7 +95,7 @@ foreach ($i in $urlList) { } ``` -# Headless installation +## Headless installation utPLSQL can be installed with DDL trigger, to enable tracking of DDL changes to your unit test packages. This is the recommended installation approach, when you want to compile and run unit test packages in a schema containing huge amount of database packages (for example Oracle EBS installation schema). @@ -115,28 +106,17 @@ This process can be time-consuming if DB schema is large. The headless scripts accept three optional parameters that define: - username to create as owner of utPLSQL (default `ut3`) - password for owner of utPLSQL (default `XNtxj8eEgA6X6b6f`) -- tablespace to use for storage of profiler data (default `users`) +- tablespace to use for storage of profiler and utPLSQL cache data (default `users`) The scripts need to be executed by `SYSDBA`, in order to grant access to `DBMS_LOCK` and `DBMS_CRYPTO` system packages. -**Note:** -> Grant on `DBMS_LOCK` is required only for installation on Oracle versions below 18c. For versions 18c and above, utPLSQL uses `DBMS_SESSION.SLEEP` so access to `DBMS_LOCK` package is no longer needed. - -**Note:** -> The user performing the installation must have the `ADMINISTER DATABASE TRIGGER` privilege. This is required for installation of trigger that is responsible for parsing annotations at at compile-time of a package. - -**Note:** -> When installing with DDL trigger, utPLSQL will not be registering unit tests for any of oracle-maintained schemas. -For Oracle 11g following users are excluded: -> ANONYMOUS, APPQOSSYS, AUDSYS, DBSFWUSER, DBSNMP, DIP, GGSYS, GSMADMIN_INTERNAL, GSMCATUSER, GSMUSER, ORACLE_OCM, OUTLN, REMOTE_SCHEDULER_AGENT, SYS, SYS$UMF, SYSBACKUP, SYSDG, SYSKM, SYSRAC, SYSTEM, WMSYS, XDB, XS$NULL -> -> For Oracle 12c and above the users returned by below query are excluded by utPLSQL: -> ->```sql -> select username from all_users where oracle_maintained='Y'; ->``` +!!! warning "Important" + - `DBMS_LOCK` is required for session synchronization between main session and session consuming realtime reports.
+ - The user performing the installation must have the `ADMINISTER DATABASE TRIGGER` privilege. This is required for installation of trigger that is responsible for parsing annotations at at compile-time of a package.
+ - When installed with DDL trigger, utPLSQL will not be registering unit tests for any of oracle-maintained schemas.
+ `select username from all_users where oracle_maintained='Y';`
-## Installation without DDL trigger +### Installation without DDL trigger To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless.sql` as SYSDBA. @@ -152,7 +132,7 @@ cd source sqlplus sys/sys_pass@db as sysdba @install_headless.sql utp3 my_verySecret_password utp3_tablespace ``` -## Installation with DDL trigger +### Installation with DDL trigger To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless_with_trigger.sql` as SYSDBA. @@ -168,7 +148,7 @@ cd source sqlplus sys/sys_pass@db as sysdba @install_headless_with_trigger.sql utp3 my_verySecret_password utp3_tablespace ``` -# Recommended Schema +## 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 a shared schema is really not recommended as you loose isolation of framework. @@ -184,16 +164,15 @@ If the installing user and utPLSQL owner is one and the same, the user must have - ALTER SESSION - CREATE TRIGGER -In addition the user must be granted the execute privilege on `DBMS_LOCK` and `DBMS_CRYPTO` packages. +In addition, the user must be granted the execute privilege on `DBMS_LOCK` and `DBMS_CRYPTO` packages. -utPLSQL is using [DBMS_PROFILER tables](https://docs.oracle.com/cd/E18283_01/appdev.112/e16760/d_profil.htm#i999476) for code coverage. The tables required by DBMS_PROFILER will be created in the installation schema unless they already exist. -The uninstall process will **not** drop profiler tables, as they can potentially be shared and reused for profiling PLSQL code. +utPLSQL is using [DBMS_PROFILER tables](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_PROFILER.html) for code coverage. The tables required by DBMS_PROFILER will be created in the installation schema unless they already exist. It is up to DBA to maintain the storage of the profiler tables. -# Manual installation procedure +## Manual installation procedure -## Creating schema for utPLSQL +### Creating schema for utPLSQL To create the utPLSQL schema and grant all the required privileges execute script `create_utplsql_owner.sql` from the `source` directory with parameters: - `user name` - the name of the user that will own of utPLSQL object @@ -206,7 +185,7 @@ cd source sqlplus sys/sys_password@database as sysdba @create_utPLSQL_owner.sql ut3 ut3 users ``` -## Installing utPLSQL +### Installing utPLSQL To install the utPLSQL framework into your database, go to `source` directory, run the `install.sql` providing the `schema_name` for utPLSQL as parameter. Schema must be created prior to calling the `install` script. You may install utPLSQL from any account that has sufficient privileges to create objects in other users schema. @@ -217,7 +196,7 @@ cd source sqlplus admin/admins_password@database @install.sql ut3 ``` -## Installing DDL trigger +### Installing DDL trigger To minimize startup time of utPLSQL framework (especially on a database with large schema) it is recommended to install utPLSQL DDL trigger to enable utPLSQL annotation to be updated at compile-time. It's recommended to install DDL trigger when connected as `SYSDBA` user. Trigger is created in utPLSQL schema. @@ -232,11 +211,11 @@ cd source sqlplus admin/admins_password@database @install_ddl_trigger.sql ut3 ``` -**Note:** ->Trigger can be installed ant any point in time. +!!! note + Trigger can be installed ant any point in time after the utPLSQL installation. The framework will detect the presence of DDL trigger and act accordingly. -## Allowing other users to access the utPLSQL framework +### Allowing other users to access the utPLSQL framework In order to allow other users to access utPLSQL, synonyms must be created and privileges granted. You have two options: @@ -265,29 +244,29 @@ The following tools that support the SQL*Plus commands can be used to run the in - [SQLcl](http://www.oracle.com/technetwork/developer-tools/sqlcl/overview/index.html) - [Oracle SQL Developer](http://www.oracle.com/technetwork/developer-tools/sql-developer/overview/index.html) -# Checking environment and utPLSQL version +## Checking environment and utPLSQL version To check the framework version execute the following query: -```sql +```sql linenums="1" select substr(ut.version(),1,60) as ut_version from dual; ``` Additionally you may retrieve more information about your environment by executing the following query: -```sql +```sql linenums="1" select xmlserialize( content xmltype(ut_run_info()) as clob indent size = 2 ) from dual; ``` -# Additional requirements +## Additional requirements In order to use the Code Coverage functionality of utPLSQL, users executing the tests must have the CREATE privilege on the PLSQL code that the coverage is gathered on. -This is a requirement of [DBMS_PROFILER package](https://docs.oracle.com/cd/E18283_01/appdev.112/e16760/d_profil.htm#i999476). +This is a requirement of [DBMS_PROFILER package](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_PROFILER.html#GUID-DEE5EA97-14AA-4AF2-A5F7-4AB1D004B99F). In practice, user running tests for PLSQL code that he does not own, needs to have CREATE ANY PROCEDURE/CREATE ANY TRIGGER privileges. Running code coverage on objects that the user does not own will **not produce any coverage information** without those privileges. -# Uninstalling utPLSQL +## Uninstalling utPLSQL To uninstall run `uninstall.sql` and provide `schema_name` where utPLSQL is installed. @@ -297,7 +276,7 @@ cd source sqlplus admin/admins_password@database @uninstall.sql ut3 ``` -The uninstall script will remove all the objects installed by the install script. +The uninstall script will remove all the objects installed by the installation script. Additionally, all the public and private synonyms pointing to the objects in the utPLSQL schema will be removed. If you have extended any utPLSQL types such as a custom reporter, these will need to be dropped before the uninstall, otherwise the uninstall script might fail. @@ -305,15 +284,15 @@ If you have extended any utPLSQL types such as a custom reporter, these will nee The uninstall script does not drop the schema. **In order for the uninstall to be successful, you need to use the uninstall script that was provided with the exact utPLSQL version installed on your database.** -i.e. the uninstall script provided with version 3.0.1 will probably not work if you want to remove version 3.0.0 from your database. +i.e. the uninstall script provided with version 3.1.11 will not work correctly if you want to remove version 3.0.0 from your database. Alternatively you can drop the user that owns utPLSQL and re-create it using headless install. -# Version upgrade +## Version upgrade Currently, the only way to upgrade version of utPLSQL v3.0.0 and above is to remove the previous version and install the new version. -# Working with utPLSQL v2 +## Working with utPLSQL v2 If you are using utPLSQL v2, you can still install utPLSQL v3. The only requirement is that utPLSQL v3 needs to be installed in a different schema than utPLSQL v2. diff --git a/docs/userguide/querying_suites.md b/docs/userguide/querying_suites.md index f15eb029a..99ff5f044 100644 --- a/docs/userguide/querying_suites.md +++ b/docs/userguide/querying_suites.md @@ -1,7 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# Qyerying for test suites - +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) ## Obtaining information about suites @@ -25,26 +22,36 @@ Querying the data from function provides the follwing details: - `tags` - tags associated with suites To get list of all test suites in current schema -```sql +```sql linenums="1" select * from table(ut_runner.get_suites_info()) where item_type = 'UT_SUITE'; ``` To get list of all tests for test suite `TEST_STUFF` in current user schema -```sql +```sql linenums="1" select * from table(ut_runner.get_suites_info(USER, 'TEST_STUFF')) where item_type = 'UT_TEST'; ``` To get a full information about suite `TEST_STUFF` including suite description, all contexts and tests in a suite -```sql +```sql linenums="1" select * from table(ut_runner.get_suites_info(USER, 'TEST_STUFF')) where item_type = 'UT_TEST'; ``` +To get a full information about suites that have a path like `ut3:tests.test_package_*` including suite description, all contexts and tests in a suite +```sql linenums="1" +select * from table(ut_runner.get_suites_info('ut3:tests.test_package_*') where item_type = 'UT_TEST'; +``` + +To get a full information about suites that have object name like `test_package_*` including suite description, all contexts and tests in a suite +```sql linenums="1" +select * from table(ut_runner.get_suites_info('test_package_*')); +``` + ## Checking if schema contains tests Function `ut_runner.has_suites(a_owner)` returns boolean value indicating if given schema contains test suites. Example: -```sql +```sql linenums="1" begin if ut_runner.has_suites(USER) then dbms_output.put_line( 'User '||USER||' owns test suites' ); @@ -59,7 +66,7 @@ end; Function `ut_runner.is_suite(a_owner, a_package_name) ` returns boolean value indicating if given package is a test suites. Example: -```sql +```sql linenums="1" begin if ut_runner.is_suite(USER,'TEST_STUFF') then dbms_output.put_line( 'Package '||USER||'.TEST_STUFF is a test suite' ); @@ -74,7 +81,7 @@ end; Function `ut_runner.is_test(a_owner, a_package_name, a_procedure_name) ` returns boolean value indicating if given package is a test suites. Example: -```sql +```sql linenums="1" begin if ut_runner.is_test(USER,'TEST_STUFF','A_TEST_TO_CHECK_STUFF') then dbms_output.put_line( 'Procedure '||USER||'.TEST_STUFF.A_TEST_TO_CHECK_STUFF is a test' ); diff --git a/docs/userguide/reporters.md b/docs/userguide/reporters.md index fc0d8f714..92c62cf72 100644 --- a/docs/userguide/reporters.md +++ b/docs/userguide/reporters.md @@ -1,8 +1,8 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) -utPLSQL provides the following reporting formats. +utPLSQL provides several reporting formats. The sections below describe most of them. -# Documentation reporter +## Documentation reporter The `ut_documentation_reporter` is the default reporting format used by the framework. It provides a human readable test results. @@ -17,7 +17,8 @@ Example outputs from documentation reporter. ![doc_reporter_outputs](../images/documentation_reporter.png) -The documentation report provides the following information. +Documentation reporter provides the following information. + - Test suite name or test package name (nested with suitepath if suitepath is used) - Test description name or test procedure name - Information about test failing `(FAILED - n)` @@ -26,7 +27,7 @@ The documentation report provides the following information. - Summary with total number of tests, number of tests with status and timing for the execution -## Color output from documentation reporter +### Color output from documentation reporter When invoking tests with documentation reporter and your command line supports ANSICONSOLE (default on Unix) [available for Windows](http://adoxa.altervista.org/ansicon/), you can obtain the coloured outputs from the documentation reporter. @@ -41,7 +42,7 @@ Example outputs from documentation reporter. ![doc_reporter_outputs](../images/documentation_reporter_color.png) -# JUnit reporter +## JUnit reporter Most of continuous integration servers (like Jenkins) are capable of consuming unit test execution results in [JUnit](https://en.wikipedia.org/wiki/JUnit) format. The `ut_junit_reporter` in earlier version referred as `ut_xunit_reporter` is producing outcomes as JUnit-compatible XML unit test report, that can be used by CI servers to display their custom reports and provide metrics (like tests execution trends). @@ -63,7 +64,7 @@ Example of failure report details -# Teamcity reporter +## Teamcity reporter [Teamcity](https://www.jetbrains.com/teamcity/) is a CI server by Jetbrains. It supports JUnit reporting and additionally has it's own format of reporting that allows tracking of progress of a CI step/task as it executes. The TeamCity format developed by Jetbrains is supported by utPLSQL with `ut_teamcity_reporter`. @@ -83,9 +84,12 @@ Example of failure report details ![junit_reporter_outputs_errors](../images/teamcity_report_example_errors.png) -# Sonar test reporter -If you are using [SonarQube](https://about.sonarqube.com/) to do static code analysis for you PLSQL projects, your code analysis can benefit from code coverage and test results. +## Sonar test reporter + +If you are using [SonarQube](https://www.sonarqube.org/) or [SonarCloud](https://about.sonarcloud.io/) to do static code analysis for you PLSQL projects, +your code analysis can benefit from code coverage and test results. utPLSQL provides two reporters to for SonarQube: + - `ut_sonar_test_reporter` - provides an XML output of each test executed per each project test file (package) - `ut_coverage_sonar_reporter` - provides XML output of code coverage per each project source file @@ -97,15 +101,15 @@ The paths to files can be relative to the project root directory (recommended) o Providing invalid paths or paths to non-existing files will result in failure when publishing test results/coverage results to sonar server. -For details on how to invoke reporter with paths, see the **Coverage reporters** section. +For details on how to invoke reporter with paths, see the [Code coverage](coverage.md) section. -# TFS / VSTS Reporter -If you are using [TFS](https://www.visualstudio.com/tfs/) or [VSTS](https://www.visualstudio.com/team-services/) to do static code analysis for you PLSQL projects and run builds, your code analysis can benefit from code coverage and test results. TFS reporter is designed specifically to [work with Microsoft Team Fundation Server](https://docs.microsoft.com/en-us/vsts/build-release/tasks/test/publish-test-results?view=vsts) report format which is very old version of [JUnit](https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd). -Main diffrence between standard JUnit is that elements cannot be nested and attribute skipped is not present. +## TFS / VSTS Reporter -utPLSQL provides test reporter to for TFS / VSTS server: -- `ut_tfs_junit_reporter` - provides an XML output of each test executed per each project test file (package) +If you are using [TFS](https://www.visualstudio.com/tfs/) or [VSTS](https://www.visualstudio.com/team-services/) to do static code analysis for you PLSQL projects and run builds, +your code analysis can benefit from code coverage and test results. TFS reporter is designed specifically to [work with Microsoft Team Fundation Server](https://docs.microsoft.com/en-us/vsts/build-release/tasks/test/publish-test-results?view=vsts) report format which is very old version of [JUnit](https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd). +Main difference between standard JUnit is that elements cannot be nested and attribute skipped is not present. +utPLSQL provides a dedicated `ut_tfs_junit_reporter` reporter to for TFS / VSTS servers. The reporter provides an XML output of each test executed per each project test file (package). Example of test report from TFS CI server. Summary: @@ -117,12 +121,26 @@ Details: ![tfs_junit_reporter_outputs](../images/tfs_details.png) -# Coverage reporters +## Coverage reporters + +utPLSQL comes with a set of build-in coverage reporters. +[Code coverage](coverage.md) section describes in details how to use configure and use code coverage. + +## TAP Reporter + +The `ut_tap_reporter` produces output compatible with the [Test Anything Protocol](https://testanything.org) (Version 14). TAP output can be consumed by a TAP consumer that can aggregate results from testsuites across different programming languages while maintaining good readability for humans. -utPLSQL comes with a set of build-in coverage reporters. Have a look into the [coverage documentation](coverage.md) to learn more about them. +![tap_output_no_color](../images/tap_reporter_no_color.png) +If you use a compatible terminal, you can also have a colored result. Only top level `not ok`-results will be colored: -# Debug reporter +![tap_colored_output](../images/tap_reporter_colored.png) + +Suites (and suitepaths) and contexts are included as [commented subtests](https://testanything.org/tap-version-14-specification.html#:~:text=Commented%20Subtests) including a summary of the tests performed for that specific context/suite. This example package has the suitepath "org.utplsql.tests.helpers": + +![tap_include_suitepath](../images/tap_reporter_suitepath.png) + +## Debug reporter The `ut_debug_reporter` provides a highly verbose output containing thorough details about framework and test execution. @@ -131,6 +149,7 @@ Use this reporter only when you need to investigate framework issues or raise a Usage of this reporter might have impact on performance of test-suite execution. Amongst others, reporter provides the following information: + - framework version - database version - database OS @@ -152,14 +171,40 @@ Amongst others, reporter provides the following information: - every test - every expectation and it's result -Some of the information in debug log might be redundant. +Some information in debug log might be redundant. -**Note:** ->Some of the information in debug log may be sensitive. In particular: -> - expectation results and messages (logged even for successful runs) -> - test structure -> - db object names -> - etc. +!!! note + Some information in debug log may be sensitive. In particular:
+ - expectation results and messages (logged even for successful runs)
+ - test structure
+ - db object names
+ - etc. - - \ No newline at end of file +## Custom reporters + +It is possible to add your own reporters by creating an appropriate object type. +In principle, it has to be a subtype of `ut_reporter_base`. However, if the reporter is expected to produce output consumable by a client oustside of the database (e.g. the data has to be reported to the screen or to a file), then you should base it on `ut_output_reporter_base` (which is a subtype of `ut_reporter_base`). In contrast, if you would like to create a reporter that, for example, saves the data to a database table, then it should be based directly on `ut_reporter_base`. (Currently, all reporters in the utPLSQL framework are based on `ut_output_reporter_base`.) Coverage reporters are based on `ut_coverage_reporter_base` (a subtype of `ut_output_reporter_base`). + +If you need to produce a colored text output from the custom reporter, then you can build it basing on `ut_console_reporter_base` (a subtype of `ut_output_reporter_base`). In many cases it may also be more convenient to create the custom reporter type under a more specialized type, like `ut_documentation_reporter` or `ut_junit_reporter`, and override just some of the functionality. + +It is recommended to create the reporter type in the schema where utPLSQL is installed (by default it is the `UT3` schema). Note that before running the utPLSQL uninstall scripts, all custom reporters should be dropped (cf. [the installation documentation](install.md)). In particular, when upgrading to a newer version of utPLSQL, one has to drop the custom reporters and recreate them after the upgrade. + +!!! note + Please make sure that grants have been added and synonyms created for the custom reporter in order for reporter to be accessible the same way as other reporters. + Assuming that reporter with name `customer_reporter` was created in schema `UT3` +```sql + grant execute on ut3.custom_reporter to public; + create or replace public synonym custom_reporter for ut3.custom_reporter; +``` + + + +!!! note + It is possible, but cumbersome, to use another schema for storing the custom reporters. This requires to create a synonym for the base reporter type in the schema that is going to own the custom reporter, and to provide appropriate grants both to the owner of the custom reporter and to the user running the reporter. After upgrading or reinstalling utPLSQL, the extra privileges need to be recreated. This approach is not recommended. + +Assuming that the custom reporter type is created in the `UT3` schema, to run the tests using a custom reporter just call: `exec ut.run(ut3.custom_reporter_name());`, optionally providing parameter values to the `custom_reporter_name` constructor. + +One may get acquainted with the source code of the standard reporters bundled with utPLSQL (including the coverage reporters) by browsing the [`source/reporters/`](https://github.com/utPLSQL/utPLSQL/tree/develop/source/reporters) directory. The base reporter types `ut_reporter_base`, `ut_output_reporter_base` and `ut_console_reporter_base` are defined in [`source/core/types`](https://github.com/utPLSQL/utPLSQL/tree/develop/source/core/types). The base coverage reporter type `ut_coverage_reporter_base` is in [`source/core/coverage`](https://github.com/utPLSQL/utPLSQL/tree/develop/source/core/coverage). There are also two examples of custom reporters in [`examples/custom_reporters/`](https://github.com/utPLSQL/utPLSQL/tree/develop/examples/custom_reporters), both extending the functionality of `ut_documentation_reporter`: + +* `ut_custom_reporter` accepts an integer parameter `a_tab_size`; it alters the behaviour of `ut_documentation_reporter` by changing the size of the indentation according to the parameter value (by default the indentation is increased). +* `ut_expectations_reporter` accepts a `varchar2` parameter `a_report_all_expectations`; if its value is `'Y'` (which is the default), then the reporter shows the results of all expectations that are run. This stays in contrast with `ut_documentation_reporter`, which shows the results of all tests that are run, but only of the expectations that failed (keep in mind that a single test may consist of several expectations). diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md index e8e411613..79671247e 100644 --- a/docs/userguide/running-unit-tests.md +++ b/docs/userguide/running-unit-tests.md @@ -1,6 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) - -# Running tests +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) utPLSQL framework provides two main entry points to run unit tests from within the database: @@ -10,7 +8,7 @@ utPLSQL framework provides two main entry points to run unit tests from within t These two entry points differ in purpose and behavior. Most of the time you will want to use `ut.run` as `ut_runner.run` is designed for API integration and does not display the results to the screen. -# Running from CI servers and command line +## Running from CI servers and command line The best way to run your tests from CI server or command line is to use the [utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli) command line client. @@ -24,7 +22,7 @@ You may download the latest release of the command line client from [here](https ```bash #!/bin/bash # Get the url to latest release "zip" file -DOWNLOAD_URL=$(curl --silent https://api.github.com/repos/utPLSQL/utPLSQL-cli/releases/latest | awk '/zipball_url/ { print $2 }' | sed -r 's/"|,//g') +DOWNLOAD_URL=$(curl --silent https://api.github.com/repos/utPLSQL/utPLSQL-cli/releases/latest | awk '/browser_download_url/ { print $2 }' | grep ".zip\"" | sed 's/"//g') # Download the latest release "zip" file curl -Lk "${DOWNLOAD_URL}" -o utplsql-cli.zip # Extract downloaded "zip" file @@ -32,7 +30,7 @@ unzip -q utplsql-cli.zip ``` -# ut.run +## ut.run The `ut` package contains overloaded `run` procedures and functions. The `run` API is designed to be called directly by a developer when using an IDE/SQL console to execute unit tests. @@ -42,11 +40,14 @@ A single line call is enough to execute a set of tests from one or more schemes. The **procedures** execute the specified tests and produce output to DBMS_OUTPUT using the specified reporter. The **functions** can only be used in SELECT statements. They execute the specified tests and produce outputs as a pipelined data stream to be consumed by a select statement. -## ut.run procedures +### ut.run procedures The examples below illustrate different ways and options to invoke `ut.run` procedures. +You can use a wildcard character `*` to call tests by part of their name or to call tests that are located on paths matched by part of path string. +Wildcard character can be placed anywhere on the path and can occur mutliple times. +Schema name cannot contain a wildcard character whether is in a suitepath call or call by object name. -```sql +```sql linenums="1" alter session set current_schema=hr; set serveroutput on begin @@ -56,7 +57,7 @@ end; Executes all tests in current schema (_HR_). -```sql +```sql linenums="1" set serveroutput on begin ut.run('HR'); @@ -65,7 +66,7 @@ end; Executes all tests in specified schema (_HR_). -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr:com.my_org.my_project'); @@ -75,8 +76,25 @@ end; Executes all tests from all packages that are on the _com.my_org.my_project_ suitepath. Check the [annotations documentation](annotations.md) to find out about suitepaths and how they can be used to organize test packages for your project. +```sql linenums="1" +set serveroutput on +begin + ut.run('hr:com*'); +end; +``` + +Executes all tests in schema `hr` from all packages that are on suitepath starting with `com`. -```sql +```sql linenums="1" +set serveroutput on +begin + ut.run('hr:co*.my_*.my_*'); +end; +``` + +Executes all tests in schema `hr` from all packages that starting with `my_` and all tests starting with `my_*` that are on suitepath starting with `co` . + +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus'); @@ -85,7 +103,7 @@ end; Executes all tests from package _hr.test_apply_bonus_. -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus.bonus_cannot_be_negative'); @@ -94,36 +112,53 @@ end; Executes single test procedure _hr.test_apply_bonus.bonus_cannot_be_negative_. -```sql +```sql linenums="1" set serveroutput on begin ut.run(ut_varchar2_list('hr.test_apply_bonus','cust')); end; ``` -Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_. +Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_ (passing individual items to be executed as elements of the ut_varchar2_list table type). + -```sql +```sql linenums="1" set serveroutput on begin - ut.run(ut_varchar2_list('hr.test_apply_bonus,cust)'); + ut.run(ut_varchar2_list('hr.test_apply_bonus,cust')); end; ``` +Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_ (passing all items as a comma-separated-list of values into a single element of the ut_varchar2_list table type). -Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_. -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus,cust'); end; ``` -Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_. +Executes all tests from package _hr.test_apply_bonus_ and all tests from schema _cust_ (no explicit ut_varchar2_list table type). Using a list of items to execute allows you to execute a fine-grained set of tests. List can be passed as a comma separated list or a list of *ut_varchar2_list objects* or as a list within ut_varchar2_list. +```sql linenums="1" +set serveroutput on +begin + ut.run('hr.test*'); +end; +``` +Executes all tests in schema `hr` located in packages starting with name `test`. + +```sql linenums="1" +set serveroutput on +begin + ut.run('hr.test_apply_bonus.bonus_*'); +end; +``` +Executes test procedures with names starting with `bonus` in package `hr.test_apply_bonus` . + **Note:** @@ -132,7 +167,7 @@ List can be passed as a comma separated list or a list of *ut_varchar2_list obje The `ut.run` procedures and functions accept `a_reporter` attribute that defines the reporter to be used in the run. You can execute any set of tests with any of the predefined reporters. -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus', ut_junit_reporter()); @@ -143,7 +178,7 @@ Executes all tests from package _HR.TEST_APPLY_BONUS_ and provide outputs to DBM For details on build-in reporters look at [reporters documentation](reporters.md). -## ut.run functions +### ut.run functions The `ut.run` functions provide exactly the same functionality as the `ut.run` procedures. You may use the same sets of parameters with both functions and procedures. @@ -155,11 +190,11 @@ Functions provide output as a pipelined stream and therefore need to be executed At the end of the run, the transaction is automatically rolled-back and all uncommitted changes are reverted. Example. -```sql +```sql linenums="1" select * from table(ut.run('hr.test_apply_bonus', ut_junit_reporter())); ``` -# ut_runner.run procedures +## ut_runner.run procedures The `ut_runner` package provides an API for integrating utPLSQL with other products. Maven, Jenkins, SQL Develper, PL/SQL Developer, TOAD and others can leverage this API to call utPLSQL. @@ -188,19 +223,19 @@ Running with multiple reporters. - each reporter for each test-run must have a unique `reporter_id`. The `reporter_id` is used between two sessions to identify the data stream Example: -```sql +```sql linenums="1" --main test run ( session 1 ) declare l_reporter ut_realtime_reporter := ut_realtime_reporter(); begin l_reporter.set_reporter_id( 'd8a79e85915640a6a4e1698fdf90ba74' ); l_reporter.output_buffer.init(); - ut_runner.run (ut_varchar2_list ('ut3_tester','ut3$user#'), ut_reporters( l_reporter ) ); + ut_runner.run (ut_varchar2_list ('ut3_tester','ut3_user'), ut_reporters( l_reporter ) ); end; / ``` -```sql +```sql linenums="1" --report consumer ( session 2 ) set arraysize 1 set pagesize 0 @@ -213,7 +248,7 @@ select * ); ``` -```sql +```sql linenums="1" --alternative version of report consumer ( session 2 ) set arraysize 1 set pagesize 0 @@ -226,16 +261,16 @@ select ``` -# Order of test execution +## Order of test execution -## Default order +### Default order When unit tests are executed without random order, they are ordered by: - schema name - suite path or test package name if `--%suitepath` was not specified for that package - `--%test` line number in package -## Random order +### Random order You can force a test run to execute tests in random order by providing one of options to `ut.run`: - `a_random_test_order` - true/false for procedures and 1/0 for functions @@ -245,14 +280,14 @@ When tests are executed with random order, randomization is applied to single le This is needed to maintain visibility and accessibility of common setup/cleanup `beforeall`/`afterall` in tests. Example: -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus', a_random_test_order => true); end; ``` -```sql +```sql linenums="1" select * from table(ut.run('hr.test_apply_bonus', a_random_test_order => 1)); ``` @@ -267,42 +302,238 @@ Tests were executed with random order seed '302980531'. If you want to re-run tests using previously generated seed, you may do so by running them with parameter `a_random_test_order_seed` Example: -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus', a_random_test_order_seed => 302980531); end; ``` -```sql +```sql linenums="1" select * from table(ut.run('hr.test_apply_bonus', a_random_test_order_seed => 302980531)); ``` **Note** >Random order seed must be a positive number within range of 1 .. 1 000 000 000. -# Run by Tags +## Run by Tags In addition to the path, you can filter the tests to be run by specifying tags. Tags are defined in the test / context / suite with the `--%tags`-annotation ([Read more](annotations.md#tags)). Multiple tags are separated by comma. -The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run. -```sql -begin - ut.run('hr.test_apply_bonus', a_tags => 'test1,test2'); -end; + +### Tag Expressions + +Tag expressions are boolean expressions created by combining tags with the `!`, `&`, `|` operators. Tag expressions can be grouped using `(` and `)` braces. Grouping tag expressions affects operator precedence. + +Two reserved keywords, `any` and `none`, can be used when creating a tag expression to run tests. +- `any` keyword represents tests and suites with any tags +- `none` keyword represents tests and suites without tags + +These keywords may be combined with other expressions just like normal tags. + +!!! note + When specifying `none`, be aware that it will exclude any tests/suites/contexts contained within a tagged suite. + +| Operator | Meaning | +| -------- | --------| +| ! | not | +| & | and | +| \| | or | + +If you are tagging your tests across multiple dimensions, tag expressions help you to select which tests to execute. When tagging by test type (e.g., micro, integration, end-to-end) and feature (e.g., product, catalog, shipping), the following tag expressions can be useful. + + +| Tag Expression | Selection | +| -------- | --------| +| product | all tests for product | +| catalog \| shipping | all tests for catalog plus all tests for shipping | +| catalog & shipping | all tests that are tagged with both `catalog` and `shipping` tags | +| product & !end-to-end | all tests tagged `product`, except the tests tagged `end-to-end` | +| (micro \| integration) & (product \| shipping) | all micro or integration tests for product or shipping | + + +Taking the last expression above `(micro | integration) & (product | shipping)` + +| --%tags |included in run | +| -------- | --------| +| micro | no | +| integration | no | +| micro | no | +| product | no | +| shipping | no | +| micro | no | +| micro, integration | no | +| product, shipping | no | +| micro, product | yes | +| micro, shipping | yes | +| integration, product | yes | +| integration, shipping | yes | +| integration, micro, shipping | yes | +| integration, micro, product | yes | +| integration, shipping ,product | yes | +| micro, shipping ,product | yes | +| integration, micro, shipping ,product | yes | + + +### Sample execution of test with tags. + +Execution of the test with tag expressions is done using the parameter `a_tags`. +Given a test package `ut_sample_test` defined below + +```sql linenums="1" +create or replace package ut_sample_test is + + --%suite(Sample Test Suite) + --%tags(api) + + --%test(Compare Ref Cursors) + --%tags(complex,fast) + procedure ut_refcursors1; + + --%test(Run equality test) + --%tags(simple,fast) + procedure ut_test; + +end ut_sample_test; +/ + +create or replace package body ut_sample_test is + + procedure ut_refcursors1 is + v_actual sys_refcursor; + v_expected sys_refcursor; + begin + open v_expected for select 1 as test from dual; + open v_actual for select 2 as test from dual; + + ut.expect(v_actual).to_equal(v_expected); + end; + + procedure ut_test is + begin + ut.expect(1).to_equal(0); + end; + +end ut_sample_test; +/ ``` -```sql -select * from table(ut.run('hr.test_apply_bonus', a_tags => 'suite1')) + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api')); ``` +The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api` + +```sql linenums="1" +select * from table(ut.run(a_tags => 'fast&complex')); +``` +The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex` and `fast` + +```sql linenums="1" +select * from table(ut.run(a_tags => 'fast')); +``` +The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast` + +### Excluding tests/suites by tags -You can also exclude specific tags by adding a `-` (dash) in front of the tag +It is possible to exclude parts of test suites with tags. +In order to do so, prefix the tag name to exclude with a `!` (exclamation) sign when invoking the test run which is equivalent of `-` (dash) in legacy notation. +Examples (based on above sample test suite) -```sql -select * from table(ut.run('hr.test_apply_bonus', a_tags => '-suite1')) +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!complex')); ``` -# Keeping uncommitted data after test-run +or + +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!complex&!test1')); +``` + +which is equivalent of exclusion on whole expression + +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!(complex|test1)')); +``` + +The above calls will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex` and except those suites/contexts/tests that are marked as `test1`. +Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed. + + +### Sample execution with `any` and `none` + +Given a sample test package: + +```sql linenums="1" +create or replace package ut_sample_test is + + --%suite(Sample Test Suite) + + --%test(Compare Ref Cursors) + --%tags(complex,fast) + procedure ut_refcursors1; + + --%test(Run equality test) + --%tags(simple,fast) + procedure ut_test; + + --%test(Run equality test no tag) + procedure ut_test_no_tag; + +end ut_sample_test; +/ + +create or replace package body ut_sample_test is + + procedure ut_refcursors1 is + v_actual sys_refcursor; + v_expected sys_refcursor; + begin + open v_expected for select 1 as test from dual; + open v_actual for select 2 as test from dual; + + ut.expect(v_actual).to_equal(v_expected); + end; + + procedure ut_test is + begin + ut.expect(1).to_equal(0); + end; + + procedure ut_test_no_tag is + begin + ut.expect(1).to_equal(0); + end; + +end ut_sample_test; +/ +``` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none')); +``` + +The above call will execute tests `ut_test_no_tag` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'any')); +``` + +The above call will execute tests `ut_test` and `ut_refcursors1` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none|simple')); +``` + +The above call will execute tests `ut_test_no_tag` and `ut_test` + +```sql linenums="1" +select * from table(ut.run(a_tags => 'none|!simple')); +``` + +The above call will execute tests `ut_test_no_tag` and `ut_refcursors1` + +## Keeping uncommitted data after test-run utPLSQL by default runs tests in autonomous transaction and performs automatic rollback to assure that tests do not impact one-another and do not have impact on the current session in your IDE. @@ -313,7 +544,7 @@ Setting this flag to true has following side-effects: - automatic rollback is forced to be disabled in test-run even if it was explicitly enabled by using annotation `--%rollback(manual) Example invocation: -```sql +```sql linenums="1" set serveroutput on begin ut.run('hr.test_apply_bonus', a_force_manual_rollback => true); @@ -323,7 +554,7 @@ end; **Note:** >This option is not available when running tests using `ut.run` as a table function. -# Reports character-set encoding +## Reports character-set encoding To get properly encoded reports, when running utPLSQL with HTML/XML reports on data containing national characters you need to provide your client character set when calling `ut.run` functions and procedures. @@ -332,8 +563,8 @@ If you run your tests using `utPLSQL-cli`, this is done automatically and no act To make sure that the reports will display your national characters properly when running from IDE like SQLDeveloper/TOAD/SQLPlus or sqlcl you need to provide the charaterset manualy to `ut.run`. Example call with characterset provided: -```sql +```sql linenums="1" begin ut.run('hr.test_apply_bonus', ut_junit_reporter(), a_client_character_set => 'Windows-1251'); end; -``` \ No newline at end of file +``` diff --git a/docs/userguide/upgrade.md b/docs/userguide/upgrade.md index ad5f2bf8f..db02d6c50 100644 --- a/docs/userguide/upgrade.md +++ b/docs/userguide/upgrade.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.1.11.3475--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.4.4532--develop-blue.svg) # Upgrading from version 2 diff --git a/examples/RunAllExamples.sql b/examples/RunAllExamples.sql index faa3388cf..f5f42b7f3 100644 --- a/examples/RunAllExamples.sql +++ b/examples/RunAllExamples.sql @@ -3,22 +3,8 @@ set echo on set feedback on set linesize 1000 -declare - l_run_number binary_integer; -begin - dbms_profiler.start_profiler( run_number => l_run_number); -end; -/ - -- Examples for users @@RunUserExamples.sql -- Framework developer examples @@RunDeveloperExamples.sql - -declare - l_return_code binary_integer; -begin - l_return_code := dbms_profiler.stop_profiler(); -end; -/ diff --git a/examples/annoation_parser_testing/annotation_parser_test.sql b/examples/annoation_parser_testing/annotation_parser_test.sql new file mode 100644 index 000000000..d1ff43fcd --- /dev/null +++ b/examples/annoation_parser_testing/annotation_parser_test.sql @@ -0,0 +1,298 @@ +-- Annotation Manager Volume Benchmark +-- 1000 generated packages, cold cache per iteration + +set serveroutput on size unlimited +set timing off +set feedback off + +declare + c_packages constant pls_integer := 1000; + c_procs_per_pkg constant pls_integer := 20; + c_contexts_per_pkg constant pls_integer := 4; + c_pkg_prefix constant varchar2(20) := 'TST_GEN_PKG_'; + c_iterations constant pls_integer := 4; + c_perf_threshold constant number := 0.80; + + l_baseline_total_ms number := 0; + l_new_total_ms number := 0; + l_start timestamp; + l_elapsed interval day to second; + + -- Source holders + l_source_lines dbms_preprocessor.source_lines_t; + l_source_clob clob; + l_result ut_annotations; + + -- Correctness tracking + l_missing_from_cache pls_integer := 0; + l_count_mismatches pls_integer := 0; + l_mismatch_detail varchar2(32767); + + -- Cache check + l_cursor sys_refcursor; + l_annotated_obj ut_annotated_object; + l_name varchar2(128); + l_annotations ut_annotations; + l_cache_count pls_integer := 0; + + -- Per-package timing + l_pkg_baseline_ms number; + l_pkg_new_ms number; + l_pkg varchar2(30); + l_ratio number; + + -- Expected annotation count — mirrors generator logic exactly + function expected_annotation_count( + a_pkg_number pls_integer, + a_procs pls_integer, + a_contexts pls_integer + ) return pls_integer is + l_count pls_integer := 0; + begin + l_count := l_count + 2; -- %suite + %suitepath + l_count := l_count + 2; -- %beforeall + %afterall + l_count := l_count + 2; -- %beforeeach + %aftereach + + -- package level conditional + if mod(a_pkg_number, 3) = 0 then l_count := l_count + 1; end if; -- %displayname + if mod(a_pkg_number, 5) = 0 then l_count := l_count + 1; end if; -- %rollback + + -- contexts: each contributes %context + %endcontext + l_count := l_count + (a_contexts * 2); + + -- per procedure + for i in 1 .. a_procs loop + l_count := l_count + 1; -- %test + if mod(i, 3) = 0 then l_count := l_count + 1; end if; -- %tags + if mod(i, 6) = 0 then l_count := l_count + 1; end if; -- %throws + if mod(i, 8) = 0 then l_count := l_count + 1; end if; -- %disabled + if mod(i, 5) = 0 then l_count := l_count + 2; end if; -- %beforetest + %aftertest + end loop; + + return l_count; + end expected_annotation_count; + +begin + dbms_output.put_line('Packages : ' || c_packages); + dbms_output.put_line('Procs/pkg : ' || c_procs_per_pkg); + dbms_output.put_line('Contexts/pkg: ' || c_contexts_per_pkg); + dbms_output.put_line('Iterations : ' || c_iterations); + dbms_output.put_line('Started : ' || to_char(systimestamp,'YYYY-MM-DD HH24:MI:SS.FF3')); + + -- Phase 1: Performance benchmark (cold cache per package per iteration) + dbms_output.put_line(chr(10) || '-- Phase 1: Performance --'); + dbms_output.put_line( + rpad('package', 20) || ' | ' || + lpad('baseline_ms', 11) || ' | ' || + lpad('new_ms', 6) || ' | ' || + lpad('ratio', 5) || ' | ' || + 'status' + ); + dbms_output.put_line(rpad('-', 70, '-')); + + for p in 1 .. c_packages loop + l_pkg := c_pkg_prefix || lpad(p, 4, '0'); + + -- load source once per package + l_source_lines := dbms_preprocessor.get_post_processed_source( + object_type => 'PACKAGE', + schema_name => user, + object_name => l_pkg + ); + + dbms_lob.createtemporary(l_source_clob, true); + for i in 1 .. l_source_lines.count loop + dbms_lob.writeappend(l_source_clob, length(l_source_lines(i)), l_source_lines(i)); + end loop; + + -- baseline: clob version + l_pkg_baseline_ms := 0; + for iter in 1 .. c_iterations loop + ut_annotation_manager.purge_cache(user, 'PACKAGE'); + l_start := systimestamp; + l_result := ut_annotation_parser.parse_object_annotations(l_source_clob); + l_elapsed := systimestamp - l_start; + l_pkg_baseline_ms := l_pkg_baseline_ms + + extract(second from l_elapsed) * 1000; + end loop; + l_pkg_baseline_ms := l_pkg_baseline_ms / c_iterations; + + -- new: source_lines_t version + l_pkg_new_ms := 0; + for iter in 1 .. c_iterations loop + ut_annotation_manager.purge_cache(user, 'PACKAGE'); + l_start := systimestamp; + l_result := ut_annotation_parser.parse_object_annotations(l_source_lines); + l_elapsed := systimestamp - l_start; + l_pkg_new_ms := l_pkg_new_ms + + extract(second from l_elapsed) * 1000; + end loop; + l_pkg_new_ms := l_pkg_new_ms / c_iterations; + + -- accumulate + l_baseline_total_ms := l_baseline_total_ms + l_pkg_baseline_ms; + l_new_total_ms := l_new_total_ms + l_pkg_new_ms; + + l_ratio := round(l_pkg_new_ms / nullif(l_pkg_baseline_ms, 0), 3); + + -- print every 100 packages to avoid flooding output + if mod(p, 100) = 0 or p = 1 then + dbms_output.put_line( + rpad(l_pkg, 20) || ' | ' || + lpad(round(l_pkg_baseline_ms, 3), 11) || ' | ' || + lpad(round(l_pkg_new_ms, 3), 6) || ' | ' || + lpad(l_ratio, 5) || ' | ' || + case when l_ratio <= c_perf_threshold + then 'PASS (' || round((1-l_ratio)*100,1) || '% faster)' + else 'SLOW (ratio=' || l_ratio || ')' + end + ); + end if; + + dbms_lob.freetemporary(l_source_clob); + end loop; + + -- ------------------------------------------------------------------------- + -- Phase 1 summary + -- ------------------------------------------------------------------------- + l_ratio := round(l_new_total_ms / nullif(l_baseline_total_ms, 0), 3); + + dbms_output.put_line(rpad('-', 70, '-')); + dbms_output.put_line('Baseline total ms : ' || round(l_baseline_total_ms, 3)); + dbms_output.put_line('New total ms : ' || round(l_new_total_ms, 3)); + dbms_output.put_line('Overall ratio : ' || l_ratio); + dbms_output.put_line('Improvement : ' || round((1 - l_ratio) * 100, 1) || '%'); + + -- Phase 2: Cache presence check + dbms_output.put_line(chr(10) || '-- Phase 2: Cache presence --'); + + ut_annotation_manager.purge_cache(user, 'PACKAGE'); + ut_annotation_manager.rebuild_annotation_cache(user, 'PACKAGE'); + + -- query cache tables directly to avoid validate_annotation_cache side effects + open l_cursor for + select ut_annotated_object( + i.object_owner, i.object_name, i.object_type, i.parse_time, + cast( + collect( + ut_annotation( + c.annotation_position, c.annotation_name, + c.annotation_text, c.subobject_name + ) order by c.annotation_position + ) as ut_annotations + ) + ) + from ut_annotation_cache_info i + join ut_annotation_cache c on i.cache_id = c.cache_id + where i.object_owner = user + and i.object_type = 'PACKAGE' + and i.object_name like c_pkg_prefix || '%' + group by i.object_owner, i.object_type, i.object_name, i.parse_time; + + loop + fetch l_cursor into l_annotated_obj; + exit when l_cursor%notfound; + l_cache_count := l_cache_count + 1; + end loop; + close l_cursor; + + dbms_output.put_line('Expected in cache : ' || c_packages); + dbms_output.put_line('Found in cache : ' || l_cache_count); + dbms_output.put_line('Cache check : ' || + case when l_cache_count = c_packages + then 'PASS — all ' || c_packages || ' packages found in cache' + else 'FAIL — missing ' || (c_packages - l_cache_count) || ' packages from cache' + end + ); + + -- Phase 3: Annotation count correctness + dbms_output.put_line(chr(10) || '-- Phase 3: Annotation count correctness --'); + + -- reopen same direct cache query for correctness check + open l_cursor for + select ut_annotated_object( + i.object_owner, i.object_name, i.object_type, i.parse_time, + cast( + collect( + ut_annotation( + c.annotation_position, c.annotation_name, + c.annotation_text, c.subobject_name + ) order by c.annotation_position + ) as ut_annotations + ) + ) + from ut_annotation_cache_info i + join ut_annotation_cache c on i.cache_id = c.cache_id + where i.object_owner = user + and i.object_type = 'PACKAGE' + and i.object_name like c_pkg_prefix || '%' + group by i.object_owner, i.object_type, i.object_name, i.parse_time; + + loop + fetch l_cursor into l_annotated_obj; + exit when l_cursor%notfound; + l_name := l_annotated_obj.object_name; + l_annotations := l_annotated_obj.annotations; + + declare + l_pkg_number pls_integer; + l_expected pls_integer; + begin + l_pkg_number := to_number(substr(l_name, length(c_pkg_prefix) + 1)); + l_expected := expected_annotation_count( + l_pkg_number, + c_procs_per_pkg, + c_contexts_per_pkg + ); + + if l_annotations.count != l_expected then + l_count_mismatches := l_count_mismatches + 1; + if l_count_mismatches <= 10 then + l_mismatch_detail := l_mismatch_detail || chr(10) || + ' ' || l_name || + ': expected=' || l_expected || + ' actual=' || l_annotations.count; + end if; + end if; + end; + end loop; + close l_cursor; + + dbms_output.put_line('Packages checked : ' || c_packages); + dbms_output.put_line('Count mismatches : ' || l_count_mismatches); + if l_count_mismatches > 0 then + dbms_output.put_line('Mismatch detail (first 10):' || l_mismatch_detail); + end if; + dbms_output.put_line('Count check : ' || + case when l_count_mismatches = 0 + then 'PASS — all annotation counts match expected' + else 'FAIL — ' || l_count_mismatches || ' packages have wrong annotation count' + end + ); + + dbms_output.put_line(chr(10) || '================================================='); + dbms_output.put_line('FINAL RESULTS'); + dbms_output.put_line('================================================='); + dbms_output.put_line('Performance : ' || + case when l_ratio <= c_perf_threshold then 'PASS' else 'FAIL' end); + dbms_output.put_line('Cache : ' || + case when l_cache_count = c_packages then 'PASS' else 'FAIL' end); + dbms_output.put_line('Counts : ' || + case when l_count_mismatches = 0 then 'PASS' else 'FAIL' end); + dbms_output.put_line('Overall : ' || + case when l_ratio <= c_perf_threshold + and l_cache_count = c_packages + and l_count_mismatches = 0 + then 'PASS' + else 'FAIL' + end + ); + dbms_output.put_line('Finished : ' || + to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS.FF3')); + dbms_output.put_line('================================================='); + + -- cleanup + ut_annotation_manager.purge_cache(user, 'PACKAGE'); + +end; +/ diff --git a/examples/annoation_parser_testing/create_test_packages.sql b/examples/annoation_parser_testing/create_test_packages.sql new file mode 100644 index 000000000..94a7f3de2 --- /dev/null +++ b/examples/annoation_parser_testing/create_test_packages.sql @@ -0,0 +1,185 @@ +declare + c_packages constant pls_integer := 1000; + c_procs_per_pkg constant pls_integer := 20; + + l_spec varchar2(32767); + l_body varchar2(32767); + l_pkg varchar2(30); + l_errors pls_integer := 0; + + procedure append_to_source(a_source in out nocopy varchar2, a_text varchar2) is + begin + a_source := a_source || a_text; + end; + + procedure append_line(a_source in out nocopy varchar2, a_text varchar2 default null) is + begin + a_source := a_source || a_text || chr(10); + end; + +begin + + for r in ( + select object_name + from user_objects + where object_name like 'TST_GEN_PKG_%' + and object_type = 'PACKAGE' + order by object_name + ) loop + begin + execute immediate 'drop package ' || r.object_name; + exception + when others then + dbms_output.put_line('ERROR dropping ' || r.object_name || ': ' || sqlerrm); + end; + end loop; + dbms_output.put_line('Existing packages dropped.'); + + for p in 1 .. c_packages loop + l_pkg := 'TST_GEN_PKG_' || lpad(p, 4, '0'); + l_spec := null; + l_body := null; + + -- Package spec + append_line(l_spec, 'create or replace package ' || l_pkg || ' as'); + append_line(l_spec); + append_line(l_spec, ' --%suite(Generated suite ' || p || ')'); + append_line(l_spec, ' --%suitepath(generated.vol_test)'); + + if mod(p, 3) = 0 then + append_line(l_spec, ' --%displayname(Suite display name for pkg ' || p || ')'); + end if; + if mod(p, 5) = 0 then + append_line(l_spec, ' --%rollback(manual)'); + end if; + + append_line(l_spec); + append_line(l_spec, ' --%beforeall'); + append_line(l_spec, ' procedure setup_suite;'); + append_line(l_spec); + append_line(l_spec, ' --%afterall'); + append_line(l_spec, ' procedure teardown_suite;'); + append_line(l_spec); + append_line(l_spec, ' --%beforeeach'); + append_line(l_spec, ' procedure setup_test;'); + append_line(l_spec); + append_line(l_spec, ' --%aftereach'); + append_line(l_spec, ' procedure teardown_test;'); + append_line(l_spec); + + for i in 1 .. c_procs_per_pkg loop + declare + l_proc varchar2(30) := 'test_proc_' || lpad(i, 3, '0'); + begin + if mod(i - 1, 5) = 0 then + append_line(l_spec, ' --%context(context_' || ceil(i/5) || ')'); + append_line(l_spec); + end if; + + if mod(i, 4) = 0 then + append_line(l_spec, ' /* multi-line comment for ' || l_proc || ' */'); + end if; + + append_line(l_spec, ' --%test(Test ' || i || ' in pkg ' || p || ')'); + + if mod(i, 3) = 0 then + append_line(l_spec, ' --%tags(tag_' || mod(i,5) || ',smoke)'); + end if; + if mod(i, 6) = 0 then + append_line(l_spec, ' --%throws(-20001)'); + end if; + if mod(i, 8) = 0 then + append_line(l_spec, ' --%disabled(generated disabled test ' || i || ')'); + end if; + if mod(i, 5) = 0 then + append_line(l_spec, ' --%beforetest(setup_test)'); + append_line(l_spec, ' --%aftertest(teardown_test)'); + end if; + + append_line(l_spec, ' -- regular comment'); + append_line(l_spec, ' procedure ' || l_proc || ';'); + append_line(l_spec); + + if mod(i, 5) = 0 then + append_line(l_spec, ' --%endcontext'); + append_line(l_spec); + end if; + end; + end loop; + + append_line(l_spec, 'end ' || l_pkg || ';'); + + -- ----------------------------------------------------------------------- + -- Package body + -- ----------------------------------------------------------------------- + append_line(l_body, 'create or replace package body ' || l_pkg || ' as'); + append_line(l_body); + + for stub in ( + select column_value as proc_name + from table(ut_varchar2_list( + 'setup_suite','teardown_suite', + 'setup_test','teardown_test' + )) + ) loop + append_line(l_body, ' procedure ' || stub.proc_name || ' is'); + append_line(l_body, ' begin'); + append_line(l_body, ' null;'); + append_line(l_body, ' end ' || stub.proc_name || ';'); + append_line(l_body); + end loop; + + for i in 1 .. c_procs_per_pkg loop + declare + l_proc varchar2(30) := 'test_proc_' || lpad(i, 3, '0'); + begin + append_line(l_body, ' procedure ' || l_proc || ' is'); + append_line(l_body, ' begin'); + if mod(i, 4) = 0 then + append_line(l_body, ' ut.expect(''/* not a comment */'').to_equal(''/* not a comment */'');'); + elsif mod(i, 4) = 1 then + append_line(l_body, ' ut.expect(' || i || ').to_be_greater_than(0);'); + elsif mod(i, 4) = 2 then + append_line(l_body, ' ut.expect(q''[-- not a comment]'').to_equal(q''[-- not a comment]'');'); + else + append_line(l_body, ' ut.expect(''test_' || i || ''').not_to_be_null();'); + end if; + append_line(l_body, ' end ' || l_proc || ';'); + append_line(l_body); + end; + end loop; + + append_line(l_body, 'end ' || l_pkg || ';'); + + -- Execute DDL — with error capture so one bad package doesn't stop all + begin + execute immediate l_spec; + execute immediate l_body; + exception + when others then + l_errors := l_errors + 1; + dbms_output.put_line('ERROR on ' || l_pkg || ': ' || sqlerrm); + end; + + if mod(p, 100) = 0 then + dbms_output.put_line('Created ' || p || ' packages...'); + end if; + + end loop; + + dbms_output.put_line('----------------------------------------'); + dbms_output.put_line('Done. packages=' || c_packages || + ' errors=' || l_errors); + + -- quick sanity check + declare + l_count pls_integer; + begin + select count(*) into l_count + from user_objects + where object_name like 'TST_GEN_PKG_%' + and object_type = 'PACKAGE'; + dbms_output.put_line('Packages in user_objects: ' || l_count); + end; +end; +/ diff --git a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql index 468d176ed..e713fa02d 100644 --- a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql +++ b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql @@ -20,6 +20,7 @@ begin ut_event_manager.initialize(); ut_event_manager.add_listener(l_doc_reporter); ut_event_manager.add_listener(l_tc_reporter); + ut_event_manager.trigger_event(ut_event_manager.gc_initialize, l_run); l_suite := ut_suite(user, 'ut_exampletest',a_line_no=>1); l_suite.description := 'Test Suite Name'; diff --git a/mkdocs.yml b/mkdocs.yml index f9a2687a8..f18f74ce3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,17 +1,127 @@ # Format documented here # http://www.mkdocs.org/user-guide/configuration/ +# https://squidfunk.github.io/mkdocs-material/getting-started/ +# See this document for list of plugins to disable for offline (local) documentation +# https://squidfunk.github.io/mkdocs-material/setup/building-for-offline-usage/ +edit_uri: "" +site_url: https://www.utplsql.org/ site_name: utPLSQL -site_description: utPLSQL Ultimate Unit Testing Framework for Oracle PL/SQL -copyright: Copyright © 2016 - 2018 utPLSQL Team +site_description: utPLSQL Ultimate Testing Framework for Oracle PL/SQL +copyright: > + Copyright © 2016 - 2026 utPLSQL Team + Change cookie settings repo_url: https://github.com/utPLSQL/utPLSQL -theme: mkdocs +extra_css: + - stylesheets/extra.css +theme: + name: material + custom_dir: docs/overrides + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/lightbulb-outline + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/lightbulb + name: Switch to light mode + logo: assets/icon-transparent.png + favicon: assets/favicon.png + features: + - navigation.instant # disable for offline docs +# - navigation.indexes + - navigation.tabs + - navigation.tracking + - navigation.top + - toc.follow + - toc.integrate + - search.suggest + - search.highlight + - content.code.copy + - content.code.annotate + - content.code.select + +extra: +# homepage: https://www.utPLSQL.org/ + # sets scope of cookies to use a common path (root) + scope: "/" + analytics: + provider: google + property: G-WMFVR98J0Q + feedback: + title: Was this page helpful? + ratings: + - icon: material/emoticon-happy-outline + name: This page was helpful + data: 1 + note: >- + Thanks for your feedback! + - icon: material/emoticon-sad-outline + name: This page could be improved + data: 0 + note: >- + Thanks for your feedback! Help us improve this page by + using our GitHub Discussions. + consent: + title: Cookie consent + description: >- + We use cookies to recognize your repeated visits and preferences, as well + as to measure the effectiveness of our documentation and whether users + find what they're searching for. With your consent, you're helping us to + make our documentation better. + # the cookie: utplsql-consent allows for shred cookie across all documentation versions and the global site + cookie: utplsql-consent + social: + - icon: fontawesome/brands/twitter + link: https://twitter.com/utPLSQL + - icon: fontawesome/regular/comment + link: https://github.com/utPLSQL/utPLSQL/discussions + - icon: fontawesome/brands/github + link: https://github.com/utPLSQL + - icon: fontawesome/solid/envelope + link: mailto:utPLSQL@utPLSQL.org + version: # disable for offline docs + provider: mike # disable for offline docs +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.caret + - pymdownx.mark + - pymdownx.tilde + - toc: + permalink: true use_directory_urls: false strict: true +plugins: + - search + - git-committers: + repository: utPLSQL/utPLSQL + branch: develop + token: !ENV ["API_TOKEN_GITHUB"] + exclude_committers: + - github-actions[bot] + - web-flow + - mike + - git-revision-date-localized: # disable for offline docs + enable_creation_date: true # disable for offline docs + type: datetime # disable for offline docs + nav: - - Home: index.md - User Guide: + - index.md - Installation: userguide/install.md - Getting Started: userguide/getting-started.md - Annotations: userguide/annotations.md @@ -19,14 +129,9 @@ nav: - Advanced data comparison: userguide/advanced_data_comparison.md - Running unit tests: userguide/running-unit-tests.md - Querying for test suites: userguide/querying_suites.md - - Testing best pracitces: userguide/best-practices.md + - Testing best practices: userguide/best-practices.md - Upgrade utPLSQL: userguide/upgrade.md - Reporting: - Using reporters: userguide/reporters.md - - Reporting errors: userguide/exception-reporting.md - Code coverage: userguide/coverage.md - - About: - - Project Details: about/project-details.md - - License: about/license.md - - Support: about/support.md - - Authors: about/authors.md + - Error handling and reporting: userguide/exception-reporting.md diff --git a/mkdocs_offline.yml b/mkdocs_offline.yml new file mode 100644 index 000000000..82304041c --- /dev/null +++ b/mkdocs_offline.yml @@ -0,0 +1,99 @@ +# Format documented here +# http://www.mkdocs.org/user-guide/configuration/ +# https://squidfunk.github.io/mkdocs-material/getting-started/ +# See this document for list of plugins to disable for offline (local) documentation +# https://squidfunk.github.io/mkdocs-material/setup/building-for-offline-usage/ + +edit_uri: "" +site_url: https://www.utplsql.org/ +site_name: utPLSQL-framework +site_description: utPLSQL Ultimate Testing Framework for Oracle PL/SQL & SQL +copyright: Copyright © 2016 - 2026 utPLSQL Team +#repo_url: https://github.com/utPLSQL/utPLSQL # disable for offline docs +extra_css: + - stylesheets/extra.css +theme: + name: material + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/lightbulb-outline + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/lightbulb + name: Switch to light mode + logo: assets/icon-transparent.png + favicon: assets/favicon.png + features: +# - navigation.instant # disable for offline docs +# - navigation.indexes + - navigation.tabs + - navigation.tracking + - toc.follow + - toc.integrate + - search.suggest + - search.highlight +extra: +# homepage: https://www.utPLSQL.org/ + social: + - icon: fontawesome/brands/twitter + link: https://twitter.com/utPLSQL + - icon: fontawesome/regular/comment + link: https://github.com/utPLSQL/utPLSQL/discussions + - icon: fontawesome/brands/github + link: https://github.com/utPLSQL + - icon: fontawesome/solid/envelope + link: mailto:utPLSQL@utPLSQL.org + consent: + title: Cookie consent + description: >- + We use cookies to recognize your repeated visits and preferences, as well + as to measure the effectiveness of our documentation and whether users + find what they're searching for. With your consent, you're helping us to + make our documentation better. +# version: # disable for offline docs +# provider: mike # disable for offline docs +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.caret + - pymdownx.mark + - pymdownx.tilde + - toc: + permalink: true +use_directory_urls: false +strict: true + +plugins: +# - search # disable for offline docs + - mike +# - git-revision-date-localized: # disable for offline docs +# enable_creation_date: true # disable for offline docs +# type: datetime # disable for offline docs + +nav: + - User Guide: + - index.md + - Installation: userguide/install.md + - Getting Started: userguide/getting-started.md + - Annotations: userguide/annotations.md + - Expectations: userguide/expectations.md + - Advanced data comparison: userguide/advanced_data_comparison.md + - Running unit tests: userguide/running-unit-tests.md + - Querying for test suites: userguide/querying_suites.md + - Testing best practices: userguide/best-practices.md + - Upgrade utPLSQL: userguide/upgrade.md + - Reporting: + - Using reporters: userguide/reporters.md + - Code coverage: userguide/coverage.md + - Error handling and reporting: userguide/exception-reporting.md diff --git a/readme.md b/readme.md index 7adecd3e0..ac4dc9815 100644 --- a/readme.md +++ b/readme.md @@ -1,103 +1,47 @@ ![utPLSQL v3 | Testing Framework for PL/SQL](docs/images/utPLSQL-testing-framework-transparent_120.png) ----------- - -[![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) -[![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)](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA) -[![twitter](https://img.shields.io/twitter/follow/utPLSQL.svg?style=social&label=Follow)](https://twitter.com/utPLSQL) - -[![build](https://img.shields.io/travis/utPLSQL/utPLSQL/main.svg?label=main%20branch)](https://travis-ci.org/utPLSQL/utPLSQL) -[![build](https://img.shields.io/travis/utPLSQL/utPLSQL/develop.svg?label=develop%20branch)](https://travis-ci.org/utPLSQL/utPLSQL) -[![sonar](https://sonarcloud.io/api/project_badges/measure?project=utPLSQL&metric=sqale_rating)](https://sonarcloud.io/dashboard/index?id=utPLSQL) -[![Coveralls coverage](https://coveralls.io/repos/github/utPLSQL/utPLSQL/badge.svg?branch=develop)](https://coveralls.io/github/utPLSQL/utPLSQL?branch=develop) - ----------- -utPLSQL version 3 is a complete rewrite of utPLSQL v2 from scratch. -Version 2 still supports older versions of Oracle that are no longer available. -The community that had developed on GitHub decided that a new internal architecture was needed, from that version 3 was born. - -# Introduction -utPLSQL is a Unit Testing framework for Oracle PL/SQL and SQL. -The framework follows industry standards and best patterns of modern Unit Testing frameworks like [JUnit](http://junit.org/junit4/) and [RSpec](http://rspec.info/) - - -# Key features - -- multiple ways to compare data with [matchers](docs/userguide/expectations.md) -- native comparison of complex types (objects/collections/cursors) -- in-depth and consistent reporting of failures and errors for tests -- tests identified and configured by [annotations](docs/userguide/annotations.md) -- hierarchies of test suites configured with annotations -- automatic (configurable) transaction control -- Build-in [coverage](docs/userguide/coverage.md) reporting -- Integration with SonarQube, Coveralls, Jenkins and Teamcity with [reporters](docs/userguide/reporters.md) -- plugin architecture for reporters and matchers -- flexible and simple test invocation -- multi-reporting from test-run from [command line](https://github.com/utPLSQL/utPLSQL-cli) - -Requirements: - - Version of Oracle under [extended support](http://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf) (Currently 11.2 and above) - -# Download - -Published releases are available for download on the [utPLSQL GitHub Releases Page.](https://github.com/utPLSQL/utPLSQL/releases) - -# Documentation - -Full documentation of the project is automatically published on [utPLSQL github pages](https://utplsql.github.io/utPLSQL/) - -[Cheat-sheets](https://www.cheatography.com/jgebal/lists/utplsql-v3-cheat-sheets/) - -# Installation - -To install the utPLSQL into a new database schema and grant it to public, execute the script `install_headless.sql`. -This will create a new user `UT3`, grant all required privileges to that user and create PUBLIC synonyms needed. - -For detailed instructions on other install options see the [Install Guide](docs/userguide/install.md) - +[![license](https://img.shields.io/github/license/utPLSQL/utPLSQL.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![GitHub Discussions](https://img.shields.io/github/discussions/utPLSQL/utPLSQL)](https://github.com/utPLSQL/utPLSQL/discussions) +[![X](https://img.shields.io/twitter/follow/utPLSQL.svg?style=social&label=Follow)](https://twitter.com/utPLSQL) +[![build](https://github.com/utPLSQL/utPLSQL/actions/workflows/build.yml/badge.svg)](https://github.com/utPLSQL/utPLSQL/actions/workflows/build.yml) +[![QualityGate](https://sonarcloud.io/api/project_badges/measure?project=utPLSQL_utPLSQL&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=utPLSQL_utPLSQL) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=utPLSQL_utPLSQL&metric=coverage)](https://sonarcloud.io/summary/new_code?id=utPLSQL_utPLSQL) -# Running tests +utPLSQL is a unit testing framework for Oracle PL/SQL and SQL, following industry standards and best practices of modern testing frameworks like [JUnit](http://junit.org/junit4/) and [RSpec](http://rspec.info/). -To execute using development IDE (TOAD/SQLDeveloper/PLSQLDeveloper/other) use one of following commands. -```sql -begin - ut.run(); -end; -/ -``` -```sql -exec ut.run(); -``` -```sql -select * from table(ut.run()); -``` +## Key Features -The above commands will run all the suites in the current schema and provide report to dbms_output or as a select statement. +- Multiple ways to compare data with [matchers](docs/userguide/expectations.md) +- Native comparison of complex types (objects, collections, cursors) +- In-depth and consistent reporting of failures and errors +- Tests identified and configured by [annotations](docs/userguide/annotations.md) +- Hierarchies of test suites configured with annotations +- Automatic (configurable) transaction control +- Built-in [coverage](docs/userguide/coverage.md) reporting +- Integration with SonarQube, Jenkins and TeamCity via [reporters](docs/userguide/reporters.md) +- Plugin architecture for reporters and matchers +- Flexible and simple test invocation +- Multi-format reporting from the [command line client](https://github.com/utPLSQL/utPLSQL-cli) -# Command line client +## Requirements -You can use the utPLSQL command line client [utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli) to run tests without the need for Oracle Client or any IDE like SQLDeveloper/TOAD etc. +- Oracle Database 19c or newer -Amongst many benefits it provides ability to: -* see the progress of test execution for long-running tests - real-time reporting -* use many reporting formats simultaneously and save reports to files (publish) -* map your project source files and test files into database objects +## Installation -Download the [latest client](https://github.com/utPLSQL/utPLSQL-cli/releases/latest) and are good to go. -See [project readme](https://github.com/utPLSQL/utPLSQL-cli/blob/develop/README.md) for details. +Published releases are available for download on the [utPLSQL GitHub Releases page](https://github.com/utPLSQL/utPLSQL/releases). +To install utPLSQL into a new database schema and grant it to public, execute the script `install_headless.sql`. +This creates a new user `UT3`, grants all required privileges, and creates PUBLIC synonyms. -# Example unit test packages +For all install options see the [Install Guide](docs/userguide/install.md). -**For examples of using Continuous Integration Server & SonarCloud with utPLSQL see the [utPLSQL demo project](https://github.com/utPLSQL/utPLSQL-demo-project/).** +## Quick Start +**1. Write a test package** -The below test package is a fully-functional Unit Test package for testing a [`betwnstr` function](examples/between_string/betwnstr.sql). -The package specification is [annotated](docs/userguide/annotations.md) with special comments. -The annotations define that a package is a unit test suite, they also allow defining a description for the suite as well as the test itself. -The package body consists of procedures containing unit test code. To validate [an expectation](docs/userguide/expectations.md) in test, use `ut.expect( actual_data ).to_( ... )` syntax. +Annotate a package specification to define a test suite and its tests, then implement each test procedure in the package body: ```sql create or replace package test_between_string as @@ -107,12 +51,6 @@ create or replace package test_between_string as -- %test(Returns substring from start position to end position) procedure normal_case; - -- %test(Returns substring when start position is zero) - procedure zero_start_position; - - -- %test(Returns string until end if end position is greater than string length) - procedure big_end_position; - -- %test(Returns null for null input string value) procedure null_string; end; @@ -125,16 +63,6 @@ create or replace package body test_between_string as ut.expect( betwnstr( '1234567', 2, 5 ) ).to_( equal('2345') ); end; - procedure zero_start_position is - begin - ut.expect( betwnstr( '1234567', 0, 5 ) ).to_( equal('12345') ); - end; - - procedure big_end_position is - begin - ut.expect( betwnstr( '1234567', 0, 500 ) ).to_( equal('1234567') ); - end; - procedure null_string is begin ut.expect( betwnstr( null, 2, 5 ) ).to_( be_null ); @@ -144,62 +72,69 @@ end; / ``` -Outputs from running the above tests +**2. Run your tests** + +```sql +exec ut.run(); +``` + +**3. See the results** + ``` Between string function Returns substring from start position to end position - Returns substring when start position is zero - Returns string until end if end position is greater than string length Returns null for null input string value Finished in .036027 seconds -4 tests, 0 failures +2 tests, 0 failures ``` +For complete working examples see [examples/](examples/). + +## Running Tests -# Contributing to the project +From any Oracle-compatible IDE (SQL Developer, TOAD, PL/SQL Developer): + +```sql +begin + ut.run(); +end; +/ +``` +```sql +exec ut.run(); +``` +```sql +select * from table(ut.run()); +``` -We welcome new developers to join our community and contribute to the utPLSQL project. -If you are interested in helping please read our [guide to contributing](CONTRIBUTING.md) -The best place to start is to read the documentation and get familiar with the existing code base. -A [slack chat](https://utplsql.slack.com/) is the place to go if you want to talk with team members. -To sign up to the chat use [this link](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA). +These commands run all suites in the current schema and report test results to `DBMS_OUTPUT` or as a result set. +## Command Line Client ----------- -[__Authors__](docs/about/authors.md) +[utPLSQL-cli](https://github.com/utPLSQL/utPLSQL-cli) lets you run tests without an Oracle Client or IDE. It provides: +- Real-time test reporting +- Simultaneous output of multiple report formats into different files +- Source files and test files mapping for coverage reports ----------- -__Project Directories__ +Download the [latest release](https://github.com/utPLSQL/utPLSQL-cli/releases/latest) and see the [CLI readme](https://github.com/utPLSQL/utPLSQL-cli/blob/develop/README.md) for details. -* .travis - contains files needed for travis-ci integration -* client_source - Sources to be used on the client-side. Developer workstation or CI platform to run the tests. -* development - Set of useful scripts and utilities for development and debugging of utPLSQL -* docs - Documentation of the project -* examples - Example source code and unit tests -* source - The installation code for utPLSQL -* tests - Tests for utPLSQL framework +## Documentation ----------- +Full documentation is published at [https://www.utplsql.org/utPLSQL/](https://www.utplsql.org/utPLSQL/). -If you have a great feature in mind, that you would like to see in utPLSQL v3 please create an [issue on GitHub](https://github.com/utPLSQL/utPLSQL/issues) or discuss it with us in the [slack chat rooms](https://utplsql.slack.com/). Use [invite link](https://join.slack.com/t/utplsql/shared_invite/zt-d6zor80g-WWqAhbLWioJZUtLYeXetzA) to join the chat. +[Cheat-sheets](https://www.cheatography.com/jgebal/lists/utplsql-v3-cheat-sheets/) are available for quick reference. +See the [Changelog](CHANGELOG.md) for the full version history. -# Version 2 to Version 3 Comparison +Migrating from legacy utPLSQL v2? See the [version 2 to version 3 comparison](docs/compare_version2_to_3.md). -[Version 2 to Version 3 Comparison](docs/compare_version2_to_3.md) +## Contributing -# Supporters +We welcome contributions of all kinds. Please read the [contributing guide](CONTRIBUTING.md) and the [code of conduct](https://github.com/utPLSQL/utPLSQL?tab=coc-ov-file) before getting started. -The utPLSQL project is community-driven and is not commercially motivated. Nonetheless, donations and other contributions are always welcome, and are detailed below. +[GitHub Discussions](https://github.com/utPLSQL/utPLSQL/discussions) is the place to ask questions and connect with the team. - - - - - - - -
supported_by_redgateutPLSQL has been supported by Redgate in the form of sponsored stickers and t-shirts. Thank you for helping us spreading the word!
+The list of **authors** and **significant contributors** is available on the [About](https://www.utplsql.org/about/) page. For a full list of contributors, see [contributors](https://github.com/utPLSQL/utPLSQL/graphs/contributors?all=1) on GitHub. diff --git a/sonar-project.properties b/sonar-project.properties index ddd794a91..0a835a2f3 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,8 +1,9 @@ # must be unique in a given SonarQube instance -sonar.projectKey=utPLSQL +sonar.organization=utplsql +sonar.projectKey=utPLSQL_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.11-develop +sonar.projectVersion=v3.2.4-develop # 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. @@ -14,7 +15,7 @@ sonar.coverage.exclusions=**/*.sql,**/*.pks sonar.tests=./test sonar.testExecutionReportPaths=./test_results.xml sonar.links.issue=https://github.com/utPLSQL/utPLSQL/issues -sonar.links.ci=https://travis-ci.org/utPLSQL/utPLSQL +sonar.links.ci=https://github.com/utPLSQL/utPLSQL/actions sonar.links.homepage=https://github.com/utPLSQL/utPLSQL sonar.projectDescription=PL/SQL Unit Testing Framework sonar.plsql.file.suffixes=sql,tab,pkb,tpb @@ -28,9 +29,9 @@ sonar.pullrequest.github.endpoint=https://api.github.com/ sonar.pullrequest.github.repository=utPLSQL/utPLSQL sonar.plsql.jdbc.driver.class=oracle.jdbc.OracleDriver -sonar.plsql.jdbc.user=UT3 +sonar.plsql.jdbc.user=UT3_DEVELOP sonar.plsql.jdbc.password=ut3 -sonar.plsql.defaultSchema=UT3 +sonar.plsql.defaultSchema=UT3_DEVELOP # Encoding of the source code. Default is default system encoding diff --git a/source/api/be_between.syn b/source/api/be_between.syn index c0972595d..233dd65bf 100644 --- a/source/api/be_between.syn +++ b/source/api/be_between.syn @@ -1 +1 @@ -create synonym be_between for ut_be_between; +create noneditionable synonym be_between for ut_be_between; diff --git a/source/api/be_empty.syn b/source/api/be_empty.syn index a8e28de6e..f63366fa3 100644 --- a/source/api/be_empty.syn +++ b/source/api/be_empty.syn @@ -1 +1 @@ -create synonym be_empty for ut_be_empty; +create noneditionable synonym be_empty for ut_be_empty; diff --git a/source/api/be_false.syn b/source/api/be_false.syn index c2665bbcb..cdf497235 100644 --- a/source/api/be_false.syn +++ b/source/api/be_false.syn @@ -1 +1 @@ -create synonym be_false for ut_be_false; +create noneditionable synonym be_false for ut_be_false; diff --git a/source/api/be_greater_or_equal.syn b/source/api/be_greater_or_equal.syn index 386fe7f32..67616dc1d 100644 --- a/source/api/be_greater_or_equal.syn +++ b/source/api/be_greater_or_equal.syn @@ -1 +1 @@ -create synonym be_greater_or_equal for ut_be_greater_or_equal; +create noneditionable synonym be_greater_or_equal for ut_be_greater_or_equal; diff --git a/source/api/be_greater_than.syn b/source/api/be_greater_than.syn index 6f97b7d22..c180f3d43 100644 --- a/source/api/be_greater_than.syn +++ b/source/api/be_greater_than.syn @@ -1 +1 @@ -create synonym be_greater_than for ut_be_greater_than; +create noneditionable synonym be_greater_than for ut_be_greater_than; diff --git a/source/api/be_less_or_equal.syn b/source/api/be_less_or_equal.syn index b2f836144..0069f52ba 100644 --- a/source/api/be_less_or_equal.syn +++ b/source/api/be_less_or_equal.syn @@ -1 +1 @@ -create synonym be_less_or_equal for ut_be_less_or_equal; +create noneditionable synonym be_less_or_equal for ut_be_less_or_equal; diff --git a/source/api/be_less_than.syn b/source/api/be_less_than.syn index fbef3b4c3..5d2766d05 100644 --- a/source/api/be_less_than.syn +++ b/source/api/be_less_than.syn @@ -1 +1 @@ -create synonym be_less_than for ut_be_less_than; +create noneditionable synonym be_less_than for ut_be_less_than; diff --git a/source/api/be_like.syn b/source/api/be_like.syn index a8be1de71..6f62f21a0 100644 --- a/source/api/be_like.syn +++ b/source/api/be_like.syn @@ -1 +1 @@ -create synonym be_like for ut_be_like; +create noneditionable synonym be_like for ut_be_like; diff --git a/source/api/be_not_null.syn b/source/api/be_not_null.syn index d6458683a..a9e02b94d 100644 --- a/source/api/be_not_null.syn +++ b/source/api/be_not_null.syn @@ -1 +1 @@ -create synonym be_not_null for ut_be_not_null; +create noneditionable synonym be_not_null for ut_be_not_null; diff --git a/source/api/be_null.syn b/source/api/be_null.syn index 42f6516c3..4fe3fb9dd 100644 --- a/source/api/be_null.syn +++ b/source/api/be_null.syn @@ -1 +1 @@ -create synonym be_null for ut_be_null; +create noneditionable synonym be_null for ut_be_null; diff --git a/source/api/be_true.syn b/source/api/be_true.syn index bf7392a62..1aa348c7e 100644 --- a/source/api/be_true.syn +++ b/source/api/be_true.syn @@ -1 +1 @@ -create synonym be_true for ut_be_true; +create noneditionable synonym be_true for ut_be_true; diff --git a/source/api/be_within.syn b/source/api/be_within.syn new file mode 100644 index 000000000..4f4f77606 --- /dev/null +++ b/source/api/be_within.syn @@ -0,0 +1 @@ +create noneditionable synonym be_within for ut_be_within; diff --git a/source/api/be_within_pct.syn b/source/api/be_within_pct.syn new file mode 100644 index 000000000..b9fc1cd53 --- /dev/null +++ b/source/api/be_within_pct.syn @@ -0,0 +1 @@ +create noneditionable synonym be_within_pct for ut_be_within_pct; diff --git a/source/api/contain.syn b/source/api/contain.syn index 5bce7d1d2..5b4158635 100644 --- a/source/api/contain.syn +++ b/source/api/contain.syn @@ -1 +1 @@ -create synonym contain for ut_contain; +create noneditionable synonym contain for ut_contain; diff --git a/source/api/equal.syn b/source/api/equal.syn index 71ba58b53..0d81fcf6f 100644 --- a/source/api/equal.syn +++ b/source/api/equal.syn @@ -1 +1 @@ -create synonym equal for ut_equal; +create noneditionable synonym equal for ut_equal; diff --git a/source/api/have_count.syn b/source/api/have_count.syn index a406d47ec..9cbf8ebf2 100644 --- a/source/api/have_count.syn +++ b/source/api/have_count.syn @@ -1 +1 @@ -create synonym have_count for ut_have_count; +create noneditionable synonym have_count for ut_have_count; diff --git a/source/api/match.syn b/source/api/match.syn index ffbda35b1..13e8cc97d 100644 --- a/source/api/match.syn +++ b/source/api/match.syn @@ -1 +1 @@ -create synonym match for ut_match; +create noneditionable synonym match for ut_match; diff --git a/source/api/ut.pkb b/source/api/ut.pkb index 3b94a2bf7..0a847cfaa 100644 --- a/source/api/ut.pkb +++ b/source/api/ut.pkb @@ -1,8 +1,8 @@ -create or replace package body ut is +create or replace noneditionable package body ut is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -98,6 +98,11 @@ create or replace package body ut is return ut_expectation_json(ut_data_value_json(a_actual), a_message); end; + function expect(a_actual in json , a_message varchar2 := null) return ut_expectation_json is + begin + return ut_expectation_json(ut_data_value_json(a_actual), a_message); + end; + procedure fail(a_message in varchar2) is begin ut_expectation_processor.report_failure(a_message); @@ -126,7 +131,11 @@ create or replace package body ut is a_client_character_set varchar2, a_random_test_order integer, a_random_test_order_seed positive, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is pragma autonomous_transaction; begin @@ -145,7 +154,11 @@ create or replace package body ut is false, ut_utils.int_to_boolean(a_random_test_order), a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); rollback; end; @@ -162,10 +175,15 @@ create or replace package body ut is a_client_character_set varchar2, a_random_test_order integer, a_random_test_order_seed positive, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is pragma autonomous_transaction; begin + a_reporter := coalesce(a_reporter,ut_documentation_reporter()); ut_runner.run( a_paths, @@ -181,7 +199,11 @@ create or replace package body ut is false, ut_utils.int_to_boolean(a_random_test_order), a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); rollback; end; @@ -200,10 +222,15 @@ create or replace package body ut is raise_if_packages_invalidated(); raise no_data_found; end if; - g_result_lines := ut_utils.clob_to_table(l_clob, ut_utils.gc_max_storage_varchar2_len); - g_result_line_no := g_result_lines.first; + if l_clob is not null and l_clob != empty_clob() then + if length(l_clob) > ut_utils.gc_max_storage_varchar2_len then + g_result_lines := ut_utils.clob_to_table(l_clob, ut_utils.gc_max_storage_varchar2_len); + else + g_result_lines := ut_varchar2_list(l_clob); + end if; + g_result_line_no := g_result_lines.first; + end if; end if; - if g_result_line_no is not null then l_result := g_result_lines(g_result_line_no); g_result_line_no := g_result_lines.next(g_result_line_no); @@ -222,7 +249,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -239,10 +270,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -261,7 +297,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -278,10 +318,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -301,7 +346,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -318,10 +367,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -341,7 +395,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -358,10 +416,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -381,7 +444,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -398,10 +465,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -421,7 +493,11 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -438,10 +514,15 @@ create or replace package body ut is a_client_character_set, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); + g_result_lines := ut_varchar2_list(); loop pipe row( get_report_outputs( l_results ) ); end loop; @@ -462,7 +543,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is l_reporter ut_reporter_base := a_reporter; begin @@ -482,7 +567,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); else run_autonomous( @@ -497,7 +586,11 @@ create or replace package body ut is a_client_character_set, ut_utils.boolean_to_int(a_random_test_order), a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end if; if l_reporter is of (ut_output_reporter_base) then @@ -519,7 +612,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is l_reporter ut_reporter_base := a_reporter; begin @@ -536,7 +633,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end; @@ -552,7 +653,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is begin ut.run( @@ -568,7 +673,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end; @@ -584,7 +693,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is begin ut.run( @@ -600,7 +713,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end; @@ -617,7 +734,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is begin ut.run( @@ -633,7 +754,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end; @@ -650,7 +775,11 @@ create or replace package body ut is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is begin ut.run( @@ -666,7 +795,11 @@ create or replace package body ut is a_force_manual_rollback, a_random_test_order, a_random_test_order_seed, - a_tags + a_tags, + a_include_schema_expr, + a_include_object_expr, + a_exclude_schema_expr, + a_exclude_object_expr ); end; @@ -674,7 +807,7 @@ create or replace package body ut is procedure set_nls is begin if g_nls_date_format is null then - select nsp.value + select /*+ no_parallel */ nsp.value into g_nls_date_format from nls_session_parameters nsp where parameter = 'NLS_DATE_FORMAT'; diff --git a/source/api/ut.pks b/source/api/ut.pks index ff75179b0..fdba6b019 100644 --- a/source/api/ut.pks +++ b/source/api/ut.pks @@ -1,8 +1,8 @@ -create or replace package ut authid current_user as +create or replace noneditionable package ut authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -47,6 +47,8 @@ create or replace package ut authid current_user as function expect(a_actual in json_element_t , a_message varchar2 := null) return ut_expectation_json; + function expect(a_actual in json , a_message varchar2 := null) return ut_expectation_json; + procedure fail(a_message in varchar2); function run( @@ -60,7 +62,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -74,7 +80,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -89,7 +99,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -104,7 +118,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -119,7 +137,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -134,7 +156,11 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_random_test_order integer := 0, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) return ut_varchar2_rows pipelined; procedure run( @@ -149,7 +175,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); procedure run( @@ -164,7 +194,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); procedure run( @@ -180,7 +214,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); procedure run( @@ -196,7 +234,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); procedure run( @@ -212,7 +254,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); procedure run( @@ -228,7 +274,11 @@ create or replace package ut authid current_user as a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); /** diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index bb7d3e458..0db1d6e96 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -1,8 +1,8 @@ -create or replace package body ut_runner is +create or replace noneditionable package body ut_runner is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -69,13 +69,16 @@ create or replace package body ut_runner is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ) is l_run ut_run; l_coverage_schema_names ut_varchar2_rows; l_paths ut_varchar2_list; - l_random_test_order_seed positive; - l_tags ut_varchar2_rows := ut_varchar2_rows(); + l_random_test_order_seed positive; begin ut_event_manager.initialize(); if a_reporters is not empty then @@ -114,35 +117,31 @@ create or replace package body ut_runner is l_coverage_schema_names := ut_suite_manager.get_schema_names(l_paths); end if; - - if a_tags is not null then - l_tags := l_tags multiset union distinct ut_utils.convert_collection( - ut_utils.trim_list_elements(ut_utils.filter_list(ut_utils.string_to_table(a_tags,','),ut_utils.gc_word_no_space)) - ); - end if; - l_run := ut_run( a_run_paths => l_paths, a_coverage_options => ut_coverage_options( - coverage_run_id => sys_guid(), + coverage_run_id => ut_coverage.get_coverage_run_id(), schema_names => l_coverage_schema_names, exclude_objects => ut_utils.convert_collection(a_exclude_objects), include_objects => ut_utils.convert_collection(a_include_objects), - file_mappings => set(a_source_file_mappings) + file_mappings => set(a_source_file_mappings), + include_schema_expr => a_include_schema_expr, + include_object_expr => a_include_object_expr, + exclude_schema_expr => a_exclude_schema_expr, + exclude_object_expr => a_exclude_object_expr ), a_test_file_mappings => set(a_test_file_mappings), a_client_character_set => a_client_character_set, a_random_test_order_seed => l_random_test_order_seed, - a_run_tags => l_tags + a_run_tags => a_tags ); - ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, l_tags); + ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, a_tags); if a_force_manual_rollback then l_run.set_rollback_type( a_rollback_type => ut_utils.gc_rollback_manual, a_force => true ); end if; l_run.do_execute(); - finish_run(l_run, a_force_manual_rollback); exception when others then @@ -166,12 +165,14 @@ create or replace package body ut_runner is ut_annotation_manager.purge_cache(a_object_owner, a_object_type); end; - function get_suites_info(a_owner varchar2 := null, a_package_name varchar2 := null) return ut_suite_items_info pipelined is + function get_suites_info(a_owner varchar2, a_package_name varchar2) return ut_suite_items_info pipelined is l_cursor sys_refcursor; l_results ut_suite_items_info; c_bulk_limit constant integer := 100; + l_path varchar2(4000) := nvl(a_owner,sys_context('userenv', 'current_schema'))||'.'||nvl(a_package_name,'*'); begin - l_cursor := ut_suite_manager.get_suites_info( nvl(a_owner,sys_context('userenv', 'current_schema')), a_package_name ); + + l_cursor := ut_suite_manager.get_suites_info(ut_varchar2_list(l_path)); loop fetch l_cursor bulk collect into l_results limit c_bulk_limit; for i in 1 .. l_results.count loop @@ -183,6 +184,26 @@ create or replace package body ut_runner is return; end; + function get_suites_info(a_path varchar2 := null) return ut_suite_items_info pipelined is + l_cursor sys_refcursor; + l_results ut_suite_items_info; + c_bulk_limit constant integer := 100; + i pls_integer; + begin + l_cursor := ut_suite_manager.get_suites_info(ut_varchar2_list(nvl(a_path,sys_context('userenv', 'current_schema')))); + loop + fetch l_cursor bulk collect into l_results limit c_bulk_limit; + i := l_results.first; + while (i is not null) loop + pipe row (l_results(i)); + i := l_results.next(i); + end loop; + exit when l_cursor%notfound; + end loop; + close l_cursor; + return; + end; + function is_test(a_owner varchar2, a_package_name varchar2, a_procedure_name varchar2) return boolean is l_result boolean := false; begin @@ -237,37 +258,6 @@ create or replace package body ut_runner is end loop; end; - function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2 is - l_start_pos pls_integer := 1; - l_end_pos pls_integer := 1; - l_result varchar2(4000); - l_item varchar2(4000); - l_at_end boolean := false; - begin - if a_random_seed is null then - l_result := a_path; - end if; - if a_path is not null then - loop - l_end_pos := instr(a_path,'.',l_start_pos); - if l_end_pos = 0 then - l_end_pos := length(a_path)+1; - l_at_end := true; - end if; - l_item := substr(a_path,l_start_pos,l_end_pos-l_start_pos); - if l_item is not null then - l_result := - l_result || - ut_utils.get_hash( to_char( dbms_utility.get_hash_value( l_item, 1, a_random_seed ) ) ); - end if; - exit when l_at_end; - l_result := l_result || chr(0); - l_start_pos := l_end_pos + 1; - end loop; - end if; - return l_result; - end; - procedure coverage_start(a_coverage_run_id raw) is begin ut_coverage.coverage_start(a_coverage_run_id); diff --git a/source/api/ut_runner.pks b/source/api/ut_runner.pks index 61578a4e0..17bd0fa7a 100644 --- a/source/api/ut_runner.pks +++ b/source/api/ut_runner.pks @@ -1,8 +1,8 @@ -create or replace package ut_runner authid current_user is +create or replace noneditionable package ut_runner authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -71,7 +71,11 @@ create or replace package ut_runner authid current_user is a_force_manual_rollback boolean := false, a_random_test_order boolean := false, a_random_test_order_seed positive := null, - a_tags varchar2 := null + a_tags varchar2 := null, + a_include_schema_expr varchar2 := null, + a_include_object_expr varchar2 := null, + a_exclude_schema_expr varchar2 := null, + a_exclude_object_expr varchar2 := null ); /** @@ -100,7 +104,14 @@ create or replace package ut_runner authid current_user is * @param a_package_name name of unit test package to retrieve (optional), if NULL all unit test packages are returned * @return ut_suite_items_info table of objects */ - function get_suites_info(a_owner varchar2 := null, a_package_name varchar2 := null) return ut_suite_items_info pipelined; + function get_suites_info(a_owner varchar2, a_package_name varchar2) return ut_suite_items_info pipelined; + + /** + * Returns a pipelined collection containing information about unit test suites and the tests contained in them + * + * @param a_path a path from which we lookg for object or suite + */ + function get_suites_info(a_path varchar2 := null) return ut_suite_items_info pipelined; /** @@ -140,11 +151,6 @@ create or replace package ut_runner authid current_user is */ function get_reporters_list return tt_reporters_info pipelined; - /* - * Returns a hash value of suitepath based on input path and random seed - */ - function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2; - procedure coverage_start(a_coverage_run_id raw); procedure coverage_stop; diff --git a/source/api/ut_suite_item_info.tpb b/source/api/ut_suite_item_info.tpb index 9b142acb0..d18f078ee 100644 --- a/source/api/ut_suite_item_info.tpb +++ b/source/api/ut_suite_item_info.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_suite_item_info is +create or replace noneditionable type body ut_suite_item_info is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ create or replace type body ut_suite_item_info is */ constructor function ut_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2, a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer, - a_tags ut_varchar2_rows) return self as result is + a_disabled_reason varchar2, a_tags ut_varchar2_rows) return self as result is begin self.object_owner := a_object_owner; self.object_name := a_object_name; @@ -27,6 +27,10 @@ create or replace type body ut_suite_item_info is self.item_line_no := a_item_line_no; self.path := a_path; self.disabled_flag := a_disabled_flag; + self.disabled_reason := case when + a_disabled_flag = 1 then a_disabled_reason + else null + end; self.tags := case when a_tags is null then null when a_tags.count = 0 then null diff --git a/source/api/ut_suite_item_info.tps b/source/api/ut_suite_item_info.tps index 807635d1b..4b4f9d52c 100644 --- a/source/api/ut_suite_item_info.tps +++ b/source/api/ut_suite_item_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite_item_info as object ( +create or replace noneditionable type ut_suite_item_info as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -23,9 +23,10 @@ create or replace type ut_suite_item_info as object ( item_line_no integer, -- line_number where annotation identifying the item exists path varchar2( 4000 ),-- suitepath of the item disabled_flag integer, -- 0 (zero) if item is not disabled, 1 if item is disabled by --%disabled annotation + disabled_reason varchar2(4000), -- if disable flag is set then you can pass reason tags varchar2(4000), constructor function ut_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2, a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer, - a_tags ut_varchar2_rows) return self as result + a_disabled_reason varchar2, a_tags ut_varchar2_rows) return self as result ) / diff --git a/source/api/ut_suite_items_info.tps b/source/api/ut_suite_items_info.tps index 7c9ac33c3..5a04c9a74 100644 --- a/source/api/ut_suite_items_info.tps +++ b/source/api/ut_suite_items_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite_items_info as +create or replace noneditionable type ut_suite_items_info as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/check_object_grants.sql b/source/check_object_grants.sql index d499ced9c..0e7a9b4c9 100644 --- a/source/check_object_grants.sql +++ b/source/check_object_grants.sql @@ -1,3 +1,20 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + declare $if dbms_db_version.version >= 18 $then c_expected_grants constant dbmsoutput_linesarray := dbmsoutput_linesarray('DBMS_CRYPTO'); @@ -24,7 +41,7 @@ begin l_target_table := get_view('dba_tab_privs'); l_owner_column := case when l_target_table like 'dba%' then 'owner' else 'table_schema' end; execute immediate q'[ - select listagg(' - '||object_name,CHR(10)) within group(order by object_name) + select /*+ no_parallel */ listagg(' - '||object_name,CHR(10)) within group(order by object_name) from ( select column_value as object_name from table(:l_expected_grants) diff --git a/source/check_sys_grants.sql b/source/check_sys_grants.sql index 79b657328..f81157183 100644 --- a/source/check_sys_grants.sql +++ b/source/check_sys_grants.sql @@ -1,3 +1,20 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + define expected_grants = "&1" declare c_expected_grants constant dbmsoutput_linesarray := dbmsoutput_linesarray( &expected_grants ); diff --git a/source/core/annotations/ut_annotated_object.tps b/source/core/annotations/ut_annotated_object.tps index 06a0f4a36..ca327d7fc 100644 --- a/source/core/annotations/ut_annotated_object.tps +++ b/source/core/annotations/ut_annotated_object.tps @@ -1,7 +1,7 @@ -create type ut_annotated_object as object( +create or replace noneditionable type ut_annotated_object as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotated_objects.tps b/source/core/annotations/ut_annotated_objects.tps index c7b142a32..2b338e7d9 100644 --- a/source/core/annotations/ut_annotated_objects.tps +++ b/source/core/annotations/ut_annotated_objects.tps @@ -1,7 +1,7 @@ -create type ut_annotated_objects as +create or replace noneditionable type ut_annotated_objects as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation.tps b/source/core/annotations/ut_annotation.tps index 982c0348c..cc1f90e86 100644 --- a/source/core/annotations/ut_annotation.tps +++ b/source/core/annotations/ut_annotation.tps @@ -1,7 +1,7 @@ -create type ut_annotation as object( +create or replace noneditionable type ut_annotation as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation_cache.sql b/source/core/annotations/ut_annotation_cache.sql index 8baa6e75c..7d69b1db6 100644 --- a/source/core/annotations/ut_annotation_cache.sql +++ b/source/core/annotations/ut_annotation_cache.sql @@ -1,7 +1,7 @@ create table ut_annotation_cache ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/annotations/ut_annotation_cache_info.sql b/source/core/annotations/ut_annotation_cache_info.sql index 7bbf99e00..6248f4fbc 100644 --- a/source/core/annotations/ut_annotation_cache_info.sql +++ b/source/core/annotations/ut_annotation_cache_info.sql @@ -1,7 +1,7 @@ create table ut_annotation_cache_info ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb index 4882108fc..afc473404 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pkb +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_annotation_cache_manager as +create or replace noneditionable package body ut_annotation_cache_manager as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,20 +21,20 @@ create or replace package body ut_annotation_cache_manager as l_timestamp timestamp := systimestamp; pragma autonomous_transaction; begin - update ut_annotation_cache_schema s + update /*+ no_parallel */ 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 + insert /*+ no_parallel */ 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 + if a_object.annotations is not null and a_object.annotations.count > 0 then - update ut_annotation_cache_info i + update /*+ no_parallel */ ut_annotation_cache_info i 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)) @@ -42,21 +42,24 @@ create or replace package body ut_annotation_cache_manager as if sql%rowcount = 0 then - insert into ut_annotation_cache_info + insert /*+ no_parallel */ 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, l_timestamp) returning cache_id into l_cache_id; end if; + + delete /*+ no_parallel */ from ut_annotation_cache c where cache_id = l_cache_id; - 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 - insert into ut_annotation_cache + insert /*+ no_parallel */ into ut_annotation_cache (cache_id, annotation_position, annotation_name, annotation_text, subobject_name) - select l_cache_id, a.position, a.name, a.text, a.subobject_name + select /*+ no_parallel */ l_cache_id, a.position, a.name, a.text, a.subobject_name from table(a_object.annotations) a; + elsif a_object.annotations is null or a_object.annotations.count = 0 then + ut_annotation_cache_manager.remove_from_cache( + ut_annotation_objs_cache_info( + ut_annotation_obj_cache_info(a_object.object_owner, a_object.object_name, a_object.object_type, 'Y', null) + ) + ); end if; commit; end; @@ -67,9 +70,9 @@ create or replace package body ut_annotation_cache_manager as pragma autonomous_transaction; begin - delete from ut_annotation_cache c + delete /*+ no_parallel */ from ut_annotation_cache c where c.cache_id - in (select i.cache_id + in (select /*+ no_parallel */ i.cache_id from ut_annotation_cache_info i join table (a_objects) o on o.object_name = i.object_name @@ -78,25 +81,26 @@ create or replace package body ut_annotation_cache_manager as and o.needs_refresh = 'Y' ); - update ut_annotation_cache_schema s + update /*+ no_parallel */ 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 + select /*+ no_parallel */ 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 + insert /*+ no_parallel */ into ut_annotation_cache_schema s (object_owner, object_type, max_parse_time) - select distinct o.object_owner, o.object_type, l_timestamp + select /*+ no_parallel */ 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 + merge /*+ no_parallel */ + into ut_annotation_cache_info i + using (select /*+ no_parallel */ o.object_name, o.object_type, o.object_owner from table(a_objects) o where o.needs_refresh = 'Y' ) o @@ -116,7 +120,7 @@ create or replace package body ut_annotation_cache_manager as 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( + select /*+ no_parallel */ ut_annotation_obj_cache_info( object_owner => i.object_owner, object_name => i.object_name, object_type => i.object_type, @@ -135,7 +139,7 @@ create or replace package body ut_annotation_cache_manager as l_result t_cache_schema_info; begin begin - select * + select /*+ no_parallel */ * into l_result from ut_annotation_cache_schema s where s.object_type = a_object_type and s.object_owner = a_object_owner; @@ -149,7 +153,7 @@ create or replace package body ut_annotation_cache_manager as procedure set_fully_refreshed(a_object_owner varchar2, a_object_type varchar2) is pragma autonomous_transaction; begin - update ut_annotation_cache_schema s + update /*+ no_parallel */ 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; @@ -160,9 +164,9 @@ create or replace package body ut_annotation_cache_manager as pragma autonomous_transaction; begin - delete from ut_annotation_cache_info i + delete /*+ no_parallel */ from ut_annotation_cache_info i where exists ( - select 1 from table (a_objects) o + select /*+ no_parallel */ 1 from table (a_objects) o where o.object_name = i.object_name and o.object_type = i.object_type and o.object_owner = i.object_owner @@ -175,7 +179,7 @@ create or replace package body ut_annotation_cache_manager as l_results sys_refcursor; begin open l_results for - select ut_annotated_object( + select /*+ no_parallel */ ut_annotated_object( i.object_owner, i.object_name, i.object_type, i.parse_time, cast( collect( @@ -204,15 +208,15 @@ create or replace package body ut_annotation_cache_manager as else 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 || ' )'; + l_cache_filter := ' c.cache_id in (select /*+ no_parallel */ 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 /*+ no_parallel */ 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 /*+ no_parallel */ 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 /*+ no_parallel */ from ut_annotation_cache_schema s where ' || l_filter using a_object_owner, a_object_type; commit; diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks index e4ba6dd7a..1aa4cb501 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pks +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -1,7 +1,7 @@ -create or replace package ut_annotation_cache_manager authid definer as +create or replace noneditionable package ut_annotation_cache_manager authid definer as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation_cache_schema.sql b/source/core/annotations/ut_annotation_cache_schema.sql index b8f908e84..b8ebaa40b 100644 --- a/source/core/annotations/ut_annotation_cache_schema.sql +++ b/source/core/annotations/ut_annotation_cache_schema.sql @@ -1,7 +1,7 @@ create table ut_annotation_cache_schema ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/annotations/ut_annotation_cache_seq.sql b/source/core/annotations/ut_annotation_cache_seq.sql index 0a898e98e..f0f1f17d7 100644 --- a/source/core/annotations/ut_annotation_cache_seq.sql +++ b/source/core/annotations/ut_annotation_cache_seq.sql @@ -1,7 +1,7 @@ create sequence ut_annotation_cache_seq /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index c9de1c7e4..b35f40075 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_annotation_manager as +create or replace noneditionable package body ut_annotation_manager as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ create or replace package body ut_annotation_manager as 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))||') */ + execute immediate 'select /*+ no_parallel cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */ value(i) from table( :l_data ) i where @@ -61,17 +61,17 @@ create or replace package body ut_annotation_manager as 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; + l_result ut_annotation_objs_cache_info := ut_annotation_objs_cache_info(); begin - ut_event_manager.trigger_event( 'get_objects_to_refresh - start' ); + ut_event_manager.trigger_event( 'get_objects_to_refresh - start', ut_key_anyvalues().put('ut_trigger_check.is_alive()',ut_trigger_check.is_alive()) ); 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 + l_cached_objects := ut_annotation_cache_manager.get_cached_objects_list( a_object_owner, a_object_type, a_modified_after ); execute immediate - 'select /*+ cardinality(i '||ut_utils.scale_cardinality(cardinality(l_cached_objects))||') */ + 'select /*+ no_parallel 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, @@ -94,8 +94,6 @@ create or replace package body ut_annotation_manager as 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_objects_to_refresh - end (count='||l_result.count||')'); return l_result; @@ -108,7 +106,7 @@ 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 x.name, x.text + q'[select /*+ no_parallel */ x.name, x.text from (select /*+ cardinality( r ]'||l_card||q'[ )*/ s.name, s.text, s.line, max(case when s.text like '%--%\%%' escape '\' @@ -123,6 +121,7 @@ create or replace package body ut_annotation_manager as 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'[' + and s.origin_con_id = sys_context('userenv', 'con_id') ) x where x.is_annotated = 'Y' order by x.name, x.line]' @@ -145,34 +144,37 @@ create or replace package body ut_annotation_manager as l_parse_time date := sysdate; pragma autonomous_transaction; begin - loop - fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; - for i in 1 .. l_names.count loop - if l_names(i) != l_name then - l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type); - ut_annotation_cache_manager.update_cache( - ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations) - ); - l_object_lines.delete; - end if; - - l_name := l_names(i); - l_object_lines(l_object_lines.count+1) := l_lines(i); - end loop; - exit when a_sources_cursor%notfound; + begin + loop + fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; + for i in 1 .. l_names.count loop + if l_names(i) != l_name then + l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type); + ut_annotation_cache_manager.update_cache( + ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations) + ); + l_object_lines.delete; + end if; + + l_name := l_names(i); + l_object_lines(l_object_lines.count+1) := l_lines(i); + end loop; + exit when a_sources_cursor%notfound; - end loop; - if a_sources_cursor%rowcount > 0 then - l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type); - ut_annotation_cache_manager.update_cache( - ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations) - ); - l_object_lines.delete; + end loop; + if a_sources_cursor%rowcount > 0 then + l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type); + ut_annotation_cache_manager.update_cache( + ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations) + ); + l_object_lines.delete; + end if; + end; + if a_sources_cursor%isopen then + close a_sources_cursor; end if; - close a_sources_cursor; end; - procedure validate_annotation_cache( a_object_owner varchar2, a_object_type varchar2, @@ -233,26 +235,27 @@ create or replace package body ut_annotation_manager as function get_source_from_sql_text(a_object_name varchar2, a_sql_text ora_name_list_t, a_parts binary_integer) return sys_refcursor is l_sql_clob clob; - l_sql_lines ut_varchar2_rows := ut_varchar2_rows(); + l_sql_lines dbms_preprocessor.source_lines_t := dbms_preprocessor.source_lines_t(); + l_sql_lines_clob ut_varchar2_list := ut_varchar2_list(); l_result sys_refcursor; begin if a_parts > 0 then for i in 1..a_parts loop ut_utils.append_to_clob(l_sql_clob, a_sql_text(i)); end loop; - l_sql_clob := ut_utils.replace_multiline_comments(l_sql_clob); - -- replace comment lines that contain "-- create or replace" - l_sql_clob := regexp_replace(l_sql_clob, '^.*[-]{2,}\s*create(\s+or\s+replace).*$', modifier => 'mi'); - -- remove the "create [or replace] [[non]editionable] " so that we have only "type|package" for parsing - -- needed for dbms_preprocessor - l_sql_clob := regexp_replace(l_sql_clob, '^(.*?\s*create(\s+or\s+replace)?(\s+(editionable|noneditionable))?\s+?)((package|type).*)', '\5', 1, 1, 'ni'); - -- remove "OWNER." from create or replace statement. - -- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088 - l_sql_clob := regexp_replace(l_sql_clob, '^(package|type)\s+("?[a-zA-Z][a-zA-Z0-9#_$]*"?\.)(.*)', '\1 \3', 1, 1, 'ni'); - l_sql_lines := ut_utils.convert_collection( ut_utils.clob_to_table(l_sql_clob) ); + + l_sql_lines_clob := ut_utils.clob_to_table(l_sql_clob); + for i in 1..l_sql_lines_clob.count loop + l_sql_lines(i) := l_sql_lines_clob(i); + end loop; + + -- replace multiline comments that contain "-- create or replace" with single line comment to avoid parsing issues with dbms_preprocessor + l_sql_lines := ut_utils.replace_multiline_comments(l_sql_lines); + -- strip CREATE header (possibly split across lines) while preserving line numbers + l_sql_lines := ut_utils.strip_create_header_lines(l_sql_lines); end if; open l_result for - select a_object_name as name, column_value||chr(10) as text from table(l_sql_lines); + select /*+ no_parallel */ a_object_name as name, column_value||chr(10) as text from table(l_sql_lines); return l_result; end; @@ -261,11 +264,12 @@ create or replace package body ut_annotation_manager as l_sources_view varchar2(200) := ut_metadata.get_source_view_name(); begin open l_result for - q'[select :a_object_name, s.text + q'[select /*+ no_parallel */ :a_object_name, s.text from ]'||l_sources_view||q'[ s where s.type = :a_object_type and s.owner = :a_object_owner and s.name = :a_object_name + and s.origin_con_id = sys_context('userenv', 'con_id') order by s.line]' using a_object_name, a_object_type, a_object_owner, a_object_name; return l_result; @@ -279,7 +283,7 @@ create or replace package body ut_annotation_manager as 'GSMCATUSER','GSMUSER','ORACLE_OCM','OUTLN','REMOTE_SCHEDULER_AGENT','SYS','SYS$UMF', 'SYSBACKUP','SYSDG','SYSKM','SYSRAC','SYSTEM','WMSYS','XDB','XS$NULL'); $else - select username bulk collect into l_restricted_users + select /*+ no_parallel */ username bulk collect into l_restricted_users from all_users where oracle_maintained = 'Y'; $end if ora_dict_obj_owner member of l_restricted_users then diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index 70257afac..eedef002a 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -1,7 +1,7 @@ -create or replace package ut_annotation_manager authid current_user as +create or replace noneditionable package ut_annotation_manager authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation_obj_cache_info.tps b/source/core/annotations/ut_annotation_obj_cache_info.tps index 4b8178a9d..5436a11ed 100644 --- a/source/core/annotations/ut_annotation_obj_cache_info.tps +++ b/source/core/annotations/ut_annotation_obj_cache_info.tps @@ -1,7 +1,7 @@ -create type ut_annotation_obj_cache_info as object( +create or replace noneditionable type ut_annotation_obj_cache_info as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation_objs_cache_info.tps b/source/core/annotations/ut_annotation_objs_cache_info.tps index bab33a9d3..c29b7d208 100644 --- a/source/core/annotations/ut_annotation_objs_cache_info.tps +++ b/source/core/annotations/ut_annotation_objs_cache_info.tps @@ -1,7 +1,7 @@ -create type ut_annotation_objs_cache_info as +create or replace noneditionable type ut_annotation_objs_cache_info as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 6248f36b9..5680d3cd2 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_annotation_parser as +create or replace noneditionable package body ut_annotation_parser as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,12 +22,10 @@ create or replace package body ut_annotation_parser as type tt_comment_list is table of varchar2(32767) index by binary_integer; gc_annotation_qualifier constant varchar2(1) := '%'; - gc_annot_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*-- *('||gc_annotation_qualifier||'.*?)$'; -- chr(09) is a tab character gc_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}'; gc_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}'; - gc_regexp_identifier constant varchar2(50) := '[a-zA-Z][a-zA-Z0-9#_$]*'; - gc_annotation_block_pattern constant varchar2(200) := '(({COMMENT#.+}'||chr(10)||')+)( |'||chr(09)||')*(procedure|function)\s+(' || - gc_regexp_identifier || ')'; + gc_regexp_identifier constant varchar2(50) := '[[:alpha:]][[:alnum:]$#_]*'; + gc_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || gc_regexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?'; @@ -72,133 +70,144 @@ create or replace package body ut_annotation_parser as a_subobject_name varchar2 := null ) is l_loop_index binary_integer := 1; - l_annotation_index binary_integer; + l_annotation_index binary_integer := 1; begin - -- loop while there are unprocessed comment blocks - while 0 != nvl(regexp_instr(srcstr => a_source - ,pattern => gc_comment_replacer_regex_ptrn - ,occurrence => l_loop_index - ,subexpression => 1) - ,0) loop + -- loop while there are unprocessed comment blocks + while l_annotation_index is not null loop -- define index of the comment block and get it's content from cache l_annotation_index := regexp_substr( a_source ,gc_comment_replacer_regex_ptrn ,1 ,l_loop_index ,subexpression => 1); - add_annotation( a_annotations, l_annotation_index, a_comments( l_annotation_index ), a_subobject_name ); - l_loop_index := l_loop_index + 1; + if l_annotation_index is not null then + add_annotation( a_annotations, l_annotation_index, a_comments( l_annotation_index ), a_subobject_name ); + l_loop_index := l_loop_index + 1; + end if; end loop; end add_annotations; - procedure add_procedure_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments in out nocopy tt_comment_list) is - l_proc_comments varchar2(32767); - l_proc_name varchar2(250); - l_annot_proc_ind number; - l_annot_proc_block varchar2(32767); + procedure add_procedure_annotations( + a_annotations in out nocopy ut_annotations, + a_source in dbms_preprocessor.source_lines_t, + a_comments in out nocopy tt_comment_list + ) is + l_proc_comments varchar2(32767); + l_proc_name varchar2(250); + l_line varchar2(32767); + l_in_comment_block boolean := false; + l_i binary_integer; begin - -- loop through procedures and functions of the package and get all the comment blocks just before it's declaration - l_annot_proc_ind := 1; - loop - --find annotated procedure index - l_annot_proc_ind := regexp_instr(srcstr => a_source - ,pattern => gc_annotation_block_pattern - ,occurrence => 1 - ,modifier => 'i' - ,position => l_annot_proc_ind); - exit when l_annot_proc_ind = 0; - - --get the annotations with procedure name - l_annot_proc_block := regexp_substr(srcstr => a_source - ,pattern => gc_annotation_block_pattern - ,position => l_annot_proc_ind - ,occurrence => 1 - ,modifier => 'i'); - - --extract the annotations - l_proc_comments := trim(regexp_substr(srcstr => l_annot_proc_block - ,pattern => gc_annotation_block_pattern - ,modifier => 'i' - ,subexpression => 1)); - --extract the procedure name - l_proc_name := trim(regexp_substr(srcstr => l_annot_proc_block - ,pattern => gc_annotation_block_pattern - ,modifier => 'i' - ,subexpression => 5)); - - -- parse the comment block for the syntactically correct annotations and store them as an array - add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); - - l_annot_proc_ind := instr(a_source, ';', l_annot_proc_ind + length(l_annot_proc_block) ); + l_i := 1; + while l_i <= a_source.count loop + l_line := a_source(l_i); + -- Comment placeholder line: start/continue accumulating a block + if instr(l_line, chr(123) ||'COMMENT#') > 0 then + if l_in_comment_block then + l_proc_comments := l_proc_comments || l_line || chr(10); + else + l_in_comment_block := true; + l_proc_comments := l_line || chr(10); + end if; + l_i := l_i + 1; + -- Whitespace-only line: allowed between comment block and proc decl + elsif l_in_comment_block and trim(replace(l_line, chr(9))) is null then + l_i := l_i + 1; + -- procedure/function declaration following a comment block + elsif l_in_comment_block + and regexp_like(l_line, '^\s*(procedure|function)\s+', 'i') + then + -- extract just the identifier name (subexpression 2) + l_proc_name := trim(regexp_substr(srcstr =>l_line + ,pattern => '^\s*(procedure|function)\s+('||gc_regexp_identifier||')' + ,modifier => 'i' + ,subexpression => 2)); + + -- pass accumulated comment placeholders + proc name to add_annotations + add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); + + -- reset comment block state + l_in_comment_block := false; + l_proc_comments := null; + + -- advance past proc header to the terminating ';' + -- the header may span multiple lines e.g. with multi-line parameter lists + while l_i <= a_source.count loop + exit when instr(a_source(l_i), ';') > 0; + l_i := l_i + 1; + end loop; + l_i := l_i + 1; -- step past the ';' line itself + -- Any other line: reset comment block accumulator + else + l_in_comment_block := false; + l_proc_comments := null; + l_i := l_i + 1; + end if; end loop; end add_procedure_annotations; - function extract_and_replace_comments(a_source in out nocopy clob) return tt_comment_list is - l_comments tt_comment_list; - l_comment_pos binary_integer; - l_comment_line binary_integer; + function extract_and_replace_comments( + a_source in out nocopy dbms_preprocessor.source_lines_t + ) return tt_comment_list is + l_comments tt_comment_list; + l_line varchar2(32767); + l_comment_pos binary_integer; l_comment_replacer varchar2(50); - l_source clob := a_source; begin - l_comment_pos := 1; - loop - - l_comment_pos := regexp_instr(srcstr => a_source - ,pattern => gc_annot_comment_pattern - ,occurrence => 1 - ,modifier => 'm' - ,position => l_comment_pos); - - exit when l_comment_pos = 0; - - -- position index is shifted by 1 because gc_annot_comment_pattern contains ^ as first sign - -- but after instr index already points to the char on that line - l_comment_pos := l_comment_pos-1; - l_comment_line := length(substr(a_source,1,l_comment_pos))-length(replace(substr(a_source,1,l_comment_pos),chr(10)))+1; - l_comments(l_comment_line) := trim(regexp_substr(srcstr => a_source - ,pattern => gc_annot_comment_pattern - ,occurrence => 1 - ,position => l_comment_pos - ,modifier => 'm' - ,subexpression => 2)); - - l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comment_line); - - l_source := regexp_replace(srcstr => a_source - ,pattern => gc_annot_comment_pattern - ,replacestr => l_comment_replacer - ,position => l_comment_pos - ,occurrence => 1 - ,modifier => 'm'); - dbms_lob.freetemporary(a_source); - a_source := l_source; - dbms_lob.freetemporary(l_source); - l_comment_pos := l_comment_pos + length(l_comment_replacer); + for i in 1 .. a_source.count loop + l_line := a_source(i); + + -- fast path: skip lines that can't possibly match + -- must contain '--' and '%' to be an annotation comment + if instr(l_line, '--') = 0 or instr(l_line, gc_annotation_qualifier) = 0 then + continue; + end if; + + -- find '--' on the line + l_comment_pos := instr(l_line, '--'); + + -- verify everything before '--' is only spaces/tabs (matches ^ *( |\t)*--) + if trim(replace(substr(l_line, 1, l_comment_pos - 1), chr(9))) is not null then + continue; + end if; + + -- skip '--' and any spaces after it, then check for annotation qualifier '%' + l_comment_pos := l_comment_pos + 2; + -- skip optional spaces between -- and % + while l_comment_pos <= length(l_line) + and substr(l_line, l_comment_pos, 1) = ' ' + loop + l_comment_pos := l_comment_pos + 1; + end loop; + + -- must start with annotation qualifier at this position + if substr(l_line, l_comment_pos, 1) != gc_annotation_qualifier then + continue; + end if; + + -- extract annotation text (from % to end of line, trimmed) + l_comments(i) := trim(substr(l_line, l_comment_pos)); + + -- replace line with placeholder, preserving line number in token + l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', i); + a_source(i) := l_comment_replacer; end loop; - ut_utils.debug_log(a_source); return l_comments; end extract_and_replace_comments; ------------------------------------------------------------ --public definitions ------------------------------------------------------------ - - function parse_object_annotations(a_source clob) return ut_annotations is - l_source clob := a_source; + function parse_object_annotations(a_source dbms_preprocessor.source_lines_t) return ut_annotations is + l_source dbms_preprocessor.source_lines_t := a_source; l_comments tt_comment_list; l_annotations ut_annotations := ut_annotations(); l_result ut_annotations; l_comment_index positive; begin - - l_source := ut_utils.replace_multiline_comments(l_source); - - -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing - -- this call modifies l_source + l_source := ut_utils.replace_multiline_comments(l_source); l_comments := extract_and_replace_comments(l_source); - add_procedure_annotations(l_annotations, l_source, l_comments); - delete_processed_comments(l_comments, l_annotations); --at this point, only the comments not related to procedures are left, so we process them all as top-level @@ -208,16 +217,15 @@ create or replace package body ut_annotation_parser as l_comment_index := l_comments.next(l_comment_index); end loop; - dbms_lob.freetemporary(l_source); - - select value(x) bulk collect into l_result from table(l_annotations) x order by x.position; + select /*+ no_parallel */ value(x) bulk collect into l_result from table(l_annotations) x order by x.position asc; return l_result; + end parse_object_annotations; function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t, a_object_type varchar2) return ut_annotations is l_processed_lines dbms_preprocessor.source_lines_t; - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_annotations ut_annotations := ut_annotations(); ex_package_is_wrapped exception; pragma exception_init(ex_package_is_wrapped, -24241); @@ -233,13 +241,11 @@ create or replace package body ut_annotation_parser as else l_processed_lines := sys.dbms_preprocessor.get_post_processed_source(a_source_lines); end if; - --convert to clob for i in 1..l_processed_lines.count loop - ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10))); + l_source(i) := replace(l_processed_lines(i), chr(13)||chr(10), chr(10)); end loop; --parse annotations l_annotations := parse_object_annotations(l_source); - dbms_lob.freetemporary(l_source); exception when ex_package_is_wrapped or source_text_is_empty then null; diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index 0368811db..3e0eaf84c 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -1,7 +1,7 @@ -create or replace package ut_annotation_parser authid current_user as +create or replace noneditionable package ut_annotation_parser authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -29,16 +29,5 @@ create or replace package ut_annotation_parser authid current_user as */ function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t, a_object_type varchar2) return ut_annotations; - - /** - * - * @private - * Parses source code and converts it to annotations - * - * @param a_source_lines ordered lines of source code to be parsed - * @return array containing annotations - */ - function parse_object_annotations(a_source clob) return ut_annotations; - end; / diff --git a/source/core/annotations/ut_annotations.tps b/source/core/annotations/ut_annotations.tps index 85be05e72..9e4adc5ae 100644 --- a/source/core/annotations/ut_annotations.tps +++ b/source/core/annotations/ut_annotations.tps @@ -1,7 +1,7 @@ -create type ut_annotations +create or replace noneditionable type ut_annotations /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/annotations/ut_trigger_annotation_parsing.trg b/source/core/annotations/ut_trigger_annotation_parsing.trg index 437b7742d..75caea5e8 100644 --- a/source/core/annotations/ut_trigger_annotation_parsing.trg +++ b/source/core/annotations/ut_trigger_annotation_parsing.trg @@ -2,6 +2,22 @@ create or replace trigger ut_trigger_annotation_parsing after create or alter or drop on database begin + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ if (ora_dict_obj_owner = UPPER('&&UT3_OWNER') and ora_dict_obj_name = 'UT3_TRIGGER_ALIVE' and ora_dict_obj_type = 'SYNONYM') diff --git a/source/core/annotations/ut_trigger_check.pkb b/source/core/annotations/ut_trigger_check.pkb index 76d3aa11c..463243c5e 100644 --- a/source/core/annotations/ut_trigger_check.pkb +++ b/source/core/annotations/ut_trigger_check.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_trigger_check is +create or replace noneditionable package body ut_trigger_check is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ create or replace package body ut_trigger_check is function is_alive return boolean is pragma autonomous_transaction; begin - execute immediate 'create or replace synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object'; + execute immediate 'create or replace noneditionable synonym '||ut_utils.ut_owner||'.'||gc_check_object_name||' for no_object'; return g_is_trigger_live; end; diff --git a/source/core/annotations/ut_trigger_check.pks b/source/core/annotations/ut_trigger_check.pks index c4c27c596..dae59f3aa 100644 --- a/source/core/annotations/ut_trigger_check.pks +++ b/source/core/annotations/ut_trigger_check.pks @@ -1,7 +1,7 @@ -create or replace package ut_trigger_check authid definer is +create or replace noneditionable package ut_trigger_check authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb index 81d17704a..be9b4ad5d 100644 --- a/source/core/coverage/ut_coverage.pkb +++ b/source/core/coverage/ut_coverage.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage is +create or replace noneditionable package body ut_coverage is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ create or replace package body ut_coverage is g_develop_mode boolean not null := false; g_is_started boolean not null := false; + g_coverage_run_id raw(32); procedure set_develop_mode(a_develop_mode in boolean) is begin @@ -35,27 +36,33 @@ create or replace package body ut_coverage is l_join_mappings varchar2(32767); l_filters varchar2(32767); l_mappings_cardinality integer := 0; + l_regex_exc_filters varchar2(32767); begin l_result := q'[ with - trigger_source_offsets as ( - select min(s.line) - 1 offset, s.owner, s.name, s.type - from {sources_view} s - where s.type = 'TRIGGER' - {filters} - and (lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%') - group by s.owner, s.name, s.type - ), sources as ( select /*+ cardinality(f {mappings_cardinality}) */ {l_full_name} as full_name, s.owner, s.name, s.type, - s.line - case when s.type = 'TRIGGER' then o.offset else 0 end as line, + s.line + - case + when s.type = 'TRIGGER' + then + /* calculate offset of line number for trigger source in coverage reporting */ + min(case when lower(s.text) like '%begin%' or lower(s.text) like '%declare%' or lower(s.text) like '%compound%' then s.line-1 end) + over (partition by s.owner, s.type, s.name) + else 0 + end as line, s.text from {sources_view} s {join_file_mappings} - left join trigger_source_offsets o - on (s.owner = o.owner and s.name = o.name and s.type = o.type) where s.type in ('PACKAGE BODY', 'TYPE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER') + and s.origin_con_id = sys_context('userenv', 'con_id') {filters} + {regex_exc_filters} + and not exists ( + select /*+ cardinality(el {excluded_objects_cardinality})*/ 1 + from table(:l_excluded_objects) el + where s.owner = el.owner and s.name = el.name + ) ), coverage_sources as ( select full_name, owner, name, type, line, text, @@ -77,13 +84,17 @@ create or replace package body ut_coverage is end as to_be_skipped from sources s ) - select full_name, owner, name, type, line, to_be_skipped, text + select /*+ no_parallel */ full_name, owner, name, type, line, to_be_skipped, text from coverage_sources s -- Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter - where (s.owner, s.name) not in ( select /*+ cardinality(el {skipped_objects_cardinality})*/el.owner, el.name from table(:l_skipped_objects) el ) + where not exists ( + select /*+ cardinality(el {skipped_objects_cardinality})*/ 1 + from table(:l_skipped_objects) el + where s.owner = el.owner and s.name = el.name + ) and line > 0 ]'; - + if a_coverage_options.file_mappings is not empty then l_mappings_cardinality := ut_utils.scale_cardinality(cardinality(a_coverage_options.file_mappings)); l_full_name := 'f.file_name'; @@ -92,6 +103,18 @@ create or replace package body ut_coverage is on s.name = f.object_name and s.type = f.object_type and s.owner = f.object_owner'; + elsif coalesce(a_coverage_options.include_schema_expr,a_coverage_options.include_object_expr) is not null then + l_full_name := q'[lower(s.type||' '||s.owner||'.'||s.name)]'; + if a_coverage_options.include_schema_expr is not null then + l_filters := q'[and regexp_like(s.owner,:a_include_schema_expr,'i')]'; + else + l_filters := 'and :a_include_schema_expr is null'; + end if; + if a_coverage_options.include_object_expr is not null then + l_filters := l_filters|| q'[ and regexp_like(s.name,:a_include_object_expr,'i')]'; + else + l_filters := l_filters|| 'and :a_include_object_expr is null'; + end if; else l_full_name := q'[lower(s.type||' '||s.owner||'.'||s.name)]'; l_filters := case @@ -109,37 +132,67 @@ create or replace package body ut_coverage is end; end if; + + if a_coverage_options.exclude_schema_expr is not null then + l_regex_exc_filters := q'[ and not regexp_like(s.owner,:a_exclude_schema_expr,'i')]'; + else + l_regex_exc_filters := ' and :a_exclude_schema_expr is null '; + end if; + + if a_coverage_options.exclude_object_expr is not null then + l_regex_exc_filters := l_regex_exc_filters||q'[ and not regexp_like(s.name,:a_exclude_obj_expr,'i')]'; + else + l_regex_exc_filters := l_regex_exc_filters||'and :a_exclude_obj_expr is null '; + end if; + + + l_result := replace(l_result, '{sources_view}', ut_metadata.get_source_view_name()); l_result := replace(l_result, '{l_full_name}', l_full_name); l_result := replace(l_result, '{join_file_mappings}', l_join_mappings); l_result := replace(l_result, '{filters}', l_filters); l_result := replace(l_result, '{mappings_cardinality}', l_mappings_cardinality); l_result := replace(l_result, '{skipped_objects_cardinality}', ut_utils.scale_cardinality(cardinality(a_skip_objects))); + l_result := replace(l_result, '{excluded_objects_cardinality}', ut_utils.scale_cardinality(cardinality(coalesce(a_coverage_options.exclude_objects, ut_object_names())))); + l_result := replace(l_result, '{regex_exc_filters}', l_regex_exc_filters); return l_result; end; function get_cov_sources_cursor(a_coverage_options in ut_coverage_options) return sys_refcursor is - l_cursor sys_refcursor; - l_skip_objects ut_object_names; - l_sql varchar2(32767); + l_cursor sys_refcursor; + l_skip_objects ut_object_names; + l_excluded_objects ut_object_names; + l_sql varchar2(32767); begin if not is_develop_mode() then --skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage. - l_skip_objects := ut_utils.get_utplsql_objects_list() multiset union all coalesce(a_coverage_options.exclude_objects, ut_object_names()); + l_skip_objects := coalesce( ut_utils.get_utplsql_objects_list() multiset union all ut_suite_manager.get_schema_ut_packages(a_coverage_options.schema_names, a_coverage_options.include_schema_expr) , ut_object_names() ); end if; + --Regex exclusion override the standard exclusion objects. + if a_coverage_options.exclude_schema_expr is null and a_coverage_options.exclude_object_expr is null then + l_excluded_objects := coalesce(a_coverage_options.exclude_objects, ut_object_names()); + end if; + l_sql := get_cov_sources_sql(a_coverage_options, l_skip_objects); ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) ); if a_coverage_options.file_mappings is not empty then - open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.file_mappings, a_coverage_options.exclude_schema_expr, + a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects; + elsif coalesce(a_coverage_options.include_schema_expr,a_coverage_options.include_object_expr) is not null then + open l_cursor for l_sql using a_coverage_options.include_schema_expr, a_coverage_options.include_object_expr, + a_coverage_options.exclude_schema_expr, a_coverage_options.exclude_object_expr, + l_excluded_objects, l_skip_objects; elsif a_coverage_options.include_objects is not empty then - open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.include_objects, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.include_objects, a_coverage_options.exclude_schema_expr, + a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects; else - open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.schema_names, l_skip_objects; + open l_cursor for l_sql using a_coverage_options.schema_names, a_coverage_options.exclude_schema_expr, + a_coverage_options.exclude_object_expr, l_excluded_objects, l_skip_objects; end if; return l_cursor; end; @@ -177,6 +230,7 @@ create or replace package body ut_coverage is l_block_coverage_id integer; begin if not is_develop_mode() and not g_is_started then + g_coverage_run_id := a_coverage_run_id; l_line_coverage_id := ut_coverage_helper_profiler.coverage_start( l_run_comment ); l_block_coverage_id := ut_coverage_helper_block.coverage_start( l_run_comment ); g_is_started := true; @@ -200,9 +254,9 @@ create or replace package body ut_coverage is begin if not is_develop_mode() then g_is_started := false; + g_coverage_run_id := null; ut_coverage_helper_block.coverage_stop(); ut_coverage_helper_profiler.coverage_stop(); - g_is_started := false; end if; end; @@ -260,7 +314,15 @@ create or replace package body ut_coverage is $end return l_result_profiler_enrich; - end get_coverage_data; + end get_coverage_data; + + function get_coverage_run_id return raw is + begin + if g_coverage_run_id is null then + g_coverage_run_id := sys_guid(); + end if; + return g_coverage_run_id; + end; end; / diff --git a/source/core/coverage/ut_coverage.pks b/source/core/coverage/ut_coverage.pks index ba5263dd2..b8bc4e169 100644 --- a/source/core/coverage/ut_coverage.pks +++ b/source/core/coverage/ut_coverage.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage authid current_user is +create or replace noneditionable package ut_coverage authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -74,5 +74,7 @@ create or replace package ut_coverage authid current_user is function get_coverage_data(a_coverage_options ut_coverage_options) return t_coverage; + function get_coverage_run_id return raw; + end; / diff --git a/source/core/coverage/ut_coverage_block.pkb b/source/core/coverage/ut_coverage_block.pkb index 79fd42699..bfdf13274 100644 --- a/source/core/coverage/ut_coverage_block.pkb +++ b/source/core/coverage/ut_coverage_block.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_block is +create or replace noneditionable package body ut_coverage_block is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_block.pks b/source/core/coverage/ut_coverage_block.pks index 08de58e69..9fccb3272 100644 --- a/source/core/coverage/ut_coverage_block.pks +++ b/source/core/coverage/ut_coverage_block.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_block authid current_user is +create or replace noneditionable package ut_coverage_block authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_helper.pkb b/source/core/coverage/ut_coverage_helper.pkb index 4c41aa2f6..ed1bea2ce 100644 --- a/source/core/coverage/ut_coverage_helper.pkb +++ b/source/core/coverage/ut_coverage_helper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_helper is +create or replace noneditionable package body ut_coverage_helper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ create or replace package body ut_coverage_helper is procedure insert_into_tmp_table(a_data t_coverage_sources_tmp_rows) is begin forall i in 1 .. a_data.count - insert into ut_coverage_sources_tmp + insert /*+ no_parallel */ into ut_coverage_sources_tmp (full_name,owner,name,type,line,text,to_be_skipped) values(a_data(i).full_name,a_data(i).owner,a_data(i).name,a_data(i).type,a_data(i).line,a_data(i).text,a_data(i).to_be_skipped); end; @@ -49,7 +49,7 @@ create or replace package body ut_coverage_helper is function is_tmp_table_populated return boolean is l_result integer; begin - select 1 into l_result from ut_coverage_sources_tmp where rownum = 1; + select /*+ no_parallel */ 1 into l_result from ut_coverage_sources_tmp where rownum = 1; return (l_result = 1); exception when no_data_found then @@ -60,7 +60,7 @@ create or replace package body ut_coverage_helper is l_result t_tmp_table_objects_crsr; begin open l_result for - select o.owner, o.name, o.type, o.full_name, max(o.line) as lines_count, + select /*+ no_parallel */ o.owner, o.name, o.type, o.full_name, max(o.line) as lines_count, cast( collect(decode(to_be_skipped, 'Y', to_char(line))) as ut_varchar2_list ) as to_be_skipped_list @@ -73,7 +73,7 @@ create or replace package body ut_coverage_helper is function get_tmp_table_object_lines(a_owner varchar2, a_object_name varchar2) return ut_varchar2_list is l_result ut_varchar2_list; begin - select rtrim(s.text,chr(10)) as text + select /*+ no_parallel */ rtrim(s.text,chr(10)) as text bulk collect into l_result from ut_coverage_sources_tmp s where s.owner = a_owner @@ -86,7 +86,7 @@ create or replace package body ut_coverage_helper is procedure set_coverage_run_ids( a_coverage_run_id raw, a_line_coverage_id integer, a_block_coverage_id integer ) is pragma autonomous_transaction; begin - insert into ut_coverage_runs + insert /*+ no_parallel */ into ut_coverage_runs ( coverage_run_id, line_coverage_id, block_coverage_id ) values ( a_coverage_run_id, a_line_coverage_id, a_block_coverage_id ); diff --git a/source/core/coverage/ut_coverage_helper.pks b/source/core/coverage/ut_coverage_helper.pks index 632b18a0d..11988d935 100644 --- a/source/core/coverage/ut_coverage_helper.pks +++ b/source/core/coverage/ut_coverage_helper.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_helper authid definer is +create or replace noneditionable package ut_coverage_helper authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_helper_block.pkb b/source/core/coverage/ut_coverage_helper_block.pkb index e030f2c37..3abbd54fc 100644 --- a/source/core/coverage/ut_coverage_helper_block.pkb +++ b/source/core/coverage/ut_coverage_helper_block.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_helper_block is +create or replace noneditionable package body ut_coverage_helper_block is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -39,6 +39,14 @@ create or replace package body ut_coverage_helper_block is $else null; $end + exception + when others then + if sqlcode = -08402 then + dbms_output.put_line('Unable to finish gathering coverage with DBMS_PLSQL_CODE_COVERAGE. '); + dbms_output.put_line('Encountered exception `ORA-08402` when calling procedure `DBMS_PLSQL_CODE_COVERAGE.STOP_COVERAGE()`.'); + dbms_output.put_line('Coverage report will only include line-level code-coverage (without branches).'); + dbms_output.put_line('Please reference following issue for details on possible causes: https://github.com/utPLSQL/utPLSQL/issues/1097 '); + end if; end; function block_results(a_object ut_coverage_helper.t_tmp_table_object, a_coverage_run_id raw) return t_block_rows is @@ -46,7 +54,8 @@ create or replace package body ut_coverage_helper_block is l_ut_owner varchar2(250) := ut_utils.ut_owner; begin execute immediate q'[ - select line as line, + select /*+ no_parallel */ + line as line, count(block) as blocks, sum(covered) as covered_blocks from (select line, diff --git a/source/core/coverage/ut_coverage_helper_block.pks b/source/core/coverage/ut_coverage_helper_block.pks index 18adf4fbd..dd8c451d7 100644 --- a/source/core/coverage/ut_coverage_helper_block.pks +++ b/source/core/coverage/ut_coverage_helper_block.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_helper_block authid current_user is +create or replace noneditionable package ut_coverage_helper_block authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_helper_profiler.pkb b/source/core/coverage/ut_coverage_helper_profiler.pkb index dacfcbaa3..f98631593 100644 --- a/source/core/coverage/ut_coverage_helper_profiler.pkb +++ b/source/core/coverage/ut_coverage_helper_profiler.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_helper_profiler is +create or replace noneditionable package body ut_coverage_helper_profiler is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ create or replace package body ut_coverage_helper_profiler is function proftab_results(a_object ut_coverage_helper.t_tmp_table_object, a_coverage_run_id raw) return t_proftab_rows is l_coverage_rows t_proftab_rows; begin - select + select /*+ no_parallel */ d.line#, case when sum(d.total_occur) = 0 and sum(d.total_time) > 0 then 1 else sum(d.total_occur) end total_occur bulk collect into l_coverage_rows diff --git a/source/core/coverage/ut_coverage_helper_profiler.pks b/source/core/coverage/ut_coverage_helper_profiler.pks index f105d3c9c..ca2a44a64 100644 --- a/source/core/coverage/ut_coverage_helper_profiler.pks +++ b/source/core/coverage/ut_coverage_helper_profiler.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_helper_profiler authid definer is +create or replace noneditionable package ut_coverage_helper_profiler authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_profiler.pkb b/source/core/coverage/ut_coverage_profiler.pkb index 6d9f894d5..4e874dabf 100644 --- a/source/core/coverage/ut_coverage_profiler.pkb +++ b/source/core/coverage/ut_coverage_profiler.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_profiler is +create or replace noneditionable package body ut_coverage_profiler is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_profiler.pks b/source/core/coverage/ut_coverage_profiler.pks index 59256ae65..788005e15 100644 --- a/source/core/coverage/ut_coverage_profiler.pks +++ b/source/core/coverage/ut_coverage_profiler.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_profiler authid current_user is +create or replace noneditionable package ut_coverage_profiler authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_reporter_base.tpb b/source/core/coverage/ut_coverage_reporter_base.tpb index 96c4b5066..e86af3795 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tpb +++ b/source/core/coverage/ut_coverage_reporter_base.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_coverage_reporter_base is +create or replace noneditionable type body ut_coverage_reporter_base is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -92,8 +92,7 @@ create or replace type body ut_coverage_reporter_base is ut_coverage_helper.cleanup_tmp_table(); (l_reporter as ut_output_reporter_base).before_calling_run(null); l_reporter.after_calling_run( ut_run( a_coverage_options => a_coverage_options, a_client_character_set => a_client_character_set ) ); - l_reporter.on_finalize(null); - for i in (select x.text from table(l_reporter.get_lines(1, 1)) x ) loop + for i in (select /*+ no_parallel */ x.text from table(l_reporter.get_lines(1, 1)) x ) loop pipe row (i.text); end loop; return; @@ -106,8 +105,7 @@ create or replace type body ut_coverage_reporter_base is ut_coverage_helper.cleanup_tmp_table(); (l_reporter as ut_output_reporter_base).before_calling_run(null); l_reporter.after_calling_run( ut_run( a_coverage_options => a_coverage_options, a_client_character_set => a_client_character_set ) ); - l_reporter.on_finalize(null); - open l_result for select x.text from table(l_reporter.get_lines(1, 1)) x; + open l_result for select /*+ no_parallel */ x.text from table(l_reporter.get_lines(1, 1)) x; return l_result; end; diff --git a/source/core/coverage/ut_coverage_reporter_base.tps b/source/core/coverage/ut_coverage_reporter_base.tps index cae165258..c4afe389b 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tps +++ b/source/core/coverage/ut_coverage_reporter_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_coverage_reporter_base under ut_output_reporter_base( +create or replace noneditionable type ut_coverage_reporter_base under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/coverage/ut_coverage_sources_tmp.sql b/source/core/coverage/ut_coverage_sources_tmp.sql index abaae5353..35eb05510 100644 --- a/source/core/coverage/ut_coverage_sources_tmp.sql +++ b/source/core/coverage/ut_coverage_sources_tmp.sql @@ -1,7 +1,7 @@ create global temporary table ut_coverage_sources_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/events/ut_event_item.tps b/source/core/events/ut_event_item.tps index efec940f5..d9e1b84fc 100644 --- a/source/core/events/ut_event_item.tps +++ b/source/core/events/ut_event_item.tps @@ -1,7 +1,7 @@ -create or replace type ut_event_item authid current_user as object ( +create or replace noneditionable type ut_event_item authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/events/ut_event_listener.tps b/source/core/events/ut_event_listener.tps index 71ee16a0f..7236fd512 100644 --- a/source/core/events/ut_event_listener.tps +++ b/source/core/events/ut_event_listener.tps @@ -1,7 +1,7 @@ -create or replace type ut_event_listener authid current_user as object ( +create or replace noneditionable type ut_event_listener authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/events/ut_event_manager.pkb b/source/core/events/ut_event_manager.pkb index 039eb43a0..50c500d13 100644 --- a/source/core/events/ut_event_manager.pkb +++ b/source/core/events/ut_event_manager.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_event_manager as +create or replace noneditionable package body ut_event_manager as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/events/ut_event_manager.pks b/source/core/events/ut_event_manager.pks index f67bd0f4d..08db7f5da 100644 --- a/source/core/events/ut_event_manager.pks +++ b/source/core/events/ut_event_manager.pks @@ -1,7 +1,7 @@ -create or replace package ut_event_manager authid current_user as +create or replace noneditionable package ut_event_manager authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/output_buffers/ut_output_buffer_base.tpb b/source/core/output_buffers/ut_output_buffer_base.tpb index 38f008e75..8b9c236ef 100644 --- a/source/core/output_buffers/ut_output_buffer_base.tpb +++ b/source/core/output_buffers/ut_output_buffer_base.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_output_buffer_base is +create or replace noneditionable type body ut_output_buffer_base is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,33 +18,110 @@ create or replace type body ut_output_buffer_base is member procedure init(self in out nocopy ut_output_buffer_base, a_output_id raw := null, a_self_type varchar2 := null) is pragma autonomous_transaction; - l_exists int; begin cleanup_buffer(); self.self_type := coalesce(a_self_type,self.self_type); self.output_id := coalesce(a_output_id, self.output_id, sys_guid()); self.start_date := coalesce(self.start_date, sysdate); - self.last_message_id := 0; - select count(*) into l_exists from ut_output_buffer_info_tmp where output_id = self.output_id; - if ( l_exists > 0 ) then - update ut_output_buffer_info_tmp set start_date = self.start_date where output_id = self.output_id; - else - insert into ut_output_buffer_info_tmp(output_id, start_date) values (self.output_id, self.start_date); - end if; + merge /*+ no_parallel */ into ut_output_buffer_info_tmp dst + using (select 1 from dual) src + on (dst.output_id = self.output_id) + when matched then + update + set dst.start_date = self.start_date + when not matched then + insert (output_id, start_date) + values (self.output_id, self.start_date); commit; + dbms_lock.allocate_unique( self.output_id, self.lock_handle); self.is_closed := 0; end; - member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor is + member procedure lock_buffer(a_timeout_sec number := null) is + l_status integer; + begin + l_status := dbms_lock.request( self.lock_handle, dbms_lock.x_mode, 5, false ); + if l_status != 0 then + raise_application_error(-20000, 'Cannot allocate lock for output buffer of reporter. lock request status = '||l_status||', lock handle = '||self.lock_handle||', self.output_id ='||self.output_id); + end if; + end; + + member procedure close(self in out nocopy ut_output_buffer_base) is + l_status integer; + begin + l_status := dbms_lock.release( self.lock_handle ); + if l_status != 0 then + raise_application_error(-20000, 'Cannot release lock for output buffer of reporter. Lock_handle = '||self.lock_handle||' status = '||l_status); + end if; + self.is_closed := 1; + end; + + + member procedure remove_buffer_info(self in ut_output_buffer_base) is + pragma autonomous_transaction; + begin + delete from ut_output_buffer_info_tmp a + where a.output_id = self.output_id; + commit; + end; + + member function timeout_producer_not_started( a_producer_started boolean, a_already_waited_sec number, a_init_wait_sec number ) return boolean + is + l_result boolean := false; + begin + if not a_producer_started and a_already_waited_sec >= a_init_wait_sec then + if a_init_wait_sec > 0 then + self.remove_buffer_info(); + raise_application_error( + ut_utils.gc_out_buffer_timeout, + 'Timeout occurred while waiting for report data producer to start. Waited for: '||ut_utils.to_string( a_already_waited_sec )||' seconds.' + ); + else + l_result := true; + end if; + end if; + return l_result; + end; + + member function timeout_producer_not_finished(a_producer_finished boolean, a_already_waited_sec number, a_timeout_sec number) return boolean + is + l_result boolean := false; + begin + if not a_producer_finished and a_timeout_sec is not null and a_already_waited_sec >= a_timeout_sec then + if a_timeout_sec > 0 then + self.remove_buffer_info(); + raise_application_error( + ut_utils.gc_out_buffer_timeout, + 'Timeout occurred while waiting for more data from producer. Waited for: '||ut_utils.to_string( a_already_waited_sec )||' seconds.' + ); + else + l_result := true; + end if; + end if; + return l_result; + end; + + member function get_lock_status return integer is + l_result integer; + l_release_status integer; + begin + l_result := dbms_lock.request( self.lock_handle, dbms_lock.s_mode, 0, false ); + if l_result = 0 then + l_release_status := dbms_lock.release( self.lock_handle ); + end if; + return l_result; + end; + + member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor is l_lines sys_refcursor; begin open l_lines for - select text, item_type + select /*+ no_parallel */ text, item_type from table(self.get_lines(a_initial_timeout, a_timeout_sec)); return l_lines; end; - member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout natural := null, a_timeout_sec natural := null) is + member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout number := null, a_timeout_sec number := null) is l_data sys_refcursor; l_clob clob; l_item_type varchar2(32767); @@ -54,16 +131,20 @@ create or replace type body ut_output_buffer_base is loop fetch l_data into l_clob, l_item_type; exit when l_data%notfound; - l_lines := ut_utils.clob_to_table(l_clob); - for i in 1 .. l_lines.count loop - dbms_output.put_line(l_lines(i)); - end loop; + if dbms_lob.getlength(l_clob) > 32767 then + l_lines := ut_utils.clob_to_table(l_clob); + for i in 1 .. l_lines.count loop + dbms_output.put_line(l_lines(i)); + end loop; + else + dbms_output.put_line(l_clob); + end if; end loop; close l_data; end; member procedure cleanup_buffer(self in ut_output_buffer_base, a_retention_time_sec natural := null) is - gc_buffer_retention_sec constant naturaln := coalesce(a_retention_time_sec, 60 * 60 * 24); -- 24 hours + gc_buffer_retention_sec constant naturaln := coalesce(a_retention_time_sec, 60 * 60 * 24 * 5); -- 5 days l_retention_days number := gc_buffer_retention_sec / (60 * 60 * 24); l_max_retention_date date := sysdate - l_retention_days; pragma autonomous_transaction; diff --git a/source/core/output_buffers/ut_output_buffer_base.tps b/source/core/output_buffers/ut_output_buffer_base.tps index 745692f38..da2bc9143 100644 --- a/source/core/output_buffers/ut_output_buffer_base.tps +++ b/source/core/output_buffers/ut_output_buffer_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_buffer_base force authid definer as object( +create or replace noneditionable type ut_output_buffer_base force authid definer as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -19,16 +19,21 @@ create or replace type ut_output_buffer_base force authid definer as object( output_id raw(32), is_closed number(1,0), start_date date, - last_message_id number(38,0), + lock_handle varchar2(30 byte), self_type varchar2(250 byte), member procedure init(self in out nocopy ut_output_buffer_base, a_output_id raw := null, a_self_type varchar2 := null), - member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor, - member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout natural := null, a_timeout_sec natural := null), + member procedure lock_buffer(a_timeout_sec number := null), + member function timeout_producer_not_started( a_producer_started boolean, a_already_waited_sec number, a_init_wait_sec number ) return boolean, + member function timeout_producer_not_finished(a_producer_finished boolean, a_already_waited_sec number, a_timeout_sec number) return boolean, + member function get_lock_status return integer, + member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor, + member procedure lines_to_dbms_output(self in ut_output_buffer_base, a_initial_timeout number := null, a_timeout_sec number := null), member procedure cleanup_buffer(self in ut_output_buffer_base, a_retention_time_sec natural := null), - not instantiable member procedure close(self in out nocopy ut_output_buffer_base), + member procedure remove_buffer_info(self in ut_output_buffer_base), + member procedure close(self in out nocopy ut_output_buffer_base), not instantiable member procedure send_line(self in out nocopy ut_output_buffer_base, a_text varchar2, a_item_type varchar2 := null), not instantiable member procedure send_lines(self in out nocopy ut_output_buffer_base, a_text_list ut_varchar2_rows, a_item_type varchar2 := null), not instantiable member procedure send_clob(self in out nocopy ut_output_buffer_base, a_text clob, a_item_type varchar2 := null), - not instantiable member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined + not instantiable member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined ) not final not instantiable / diff --git a/source/core/output_buffers/ut_output_buffer_info_tmp.sql b/source/core/output_buffers/ut_output_buffer_info_tmp.sql index ec09619d2..2aba5a55c 100644 --- a/source/core/output_buffers/ut_output_buffer_info_tmp.sql +++ b/source/core/output_buffers/ut_output_buffer_info_tmp.sql @@ -1,7 +1,7 @@ create table ut_output_buffer_info_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/output_buffers/ut_output_buffer_tmp.sql b/source/core/output_buffers/ut_output_buffer_tmp.sql index 49fefa24d..68b738d3f 100644 --- a/source/core/output_buffers/ut_output_buffer_tmp.sql +++ b/source/core/output_buffers/ut_output_buffer_tmp.sql @@ -1,7 +1,7 @@ create table ut_output_buffer_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -30,3 +30,4 @@ create table ut_output_buffer_tmp( ) organization index nologging initrans 100 overflow nologging initrans 100; +create sequence ut_output_buffer_tmp_seq cache 20; diff --git a/source/core/output_buffers/ut_output_bulk_buffer.tpb b/source/core/output_buffers/ut_output_bulk_buffer.tpb new file mode 100644 index 000000000..4f6698eaf --- /dev/null +++ b/source/core/output_buffers/ut_output_bulk_buffer.tpb @@ -0,0 +1,160 @@ +create or replace noneditionable type body ut_output_bulk_buffer is + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_output_bulk_buffer(self in out nocopy ut_output_bulk_buffer, a_output_id raw := null) return self as result is + begin + self.init(a_output_id, $$plsql_unit); + return; + end; + + overriding member procedure send_line(self in out nocopy ut_output_bulk_buffer, a_text varchar2, a_item_type varchar2 := null) is + pragma autonomous_transaction; + begin + if a_text is not null or a_item_type is not null then + if length(a_text) > ut_utils.gc_max_storage_varchar2_len then + self.send_lines( + ut_utils.convert_collection( + ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len) + ), + a_item_type + ); + else + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type); + end if; + commit; + end if; + end; + + overriding member procedure send_lines(self in out nocopy ut_output_bulk_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is + pragma autonomous_transaction; + begin + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + select /*+ no_parallel */ self.output_id, ut_output_buffer_tmp_seq.nextval, t.column_value, a_item_type + from table(a_text_list) t + where t.column_value is not null or a_item_type is not null; + commit; + end; + + overriding member procedure send_clob(self in out nocopy ut_output_bulk_buffer, a_text clob, a_item_type varchar2 := null) is + pragma autonomous_transaction; + begin + if a_text is not null and a_text != empty_clob() or a_item_type is not null then + if length(a_text) > ut_utils.gc_max_storage_varchar2_len then + self.send_lines( + ut_utils.convert_collection( + ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len) + ), + a_item_type + ); + else + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type); + end if; + commit; + end if; + end; + + overriding member procedure lines_to_dbms_output(self in ut_output_bulk_buffer, a_initial_timeout number := null, a_timeout_sec number := null) is + l_data sys_refcursor; + l_text ut_varchar2_rows; + l_item_type ut_varchar2_rows; + begin + l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec); + loop + fetch l_data bulk collect into l_text, l_item_type limit 10000; + for idx in 1 .. l_text.count loop + dbms_output.put_line(l_text(idx)); + end loop; + exit when l_data%notfound; + end loop; + close l_data; + self.remove_buffer_info(); + end; + + overriding member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor is + lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 ); + l_already_waited_sec number(10,2) := 0; + l_sleep_time number(2,1); + l_exists integer; + l_finished boolean := false; + l_data_produced boolean := false; + l_producer_active boolean := false; + l_producer_started boolean := false; + l_producer_finished boolean := false; + l_results sys_refcursor; + begin + + while not l_finished loop + + if not l_data_produced then + select /*+ no_parallel */ count(1) into l_exists + from ut_output_buffer_tmp o + where o.output_id = self.output_id and rownum = 1; + l_data_produced := (l_exists = 1); + end if; + + l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end; + l_producer_active := (self.get_lock_status() <> 0); + l_producer_started := (l_producer_active or l_data_produced ) or l_producer_started; + l_producer_finished := (l_producer_started and not l_producer_active) or l_producer_finished; + l_finished := + self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec) + or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec) + or l_producer_finished; + + dbms_lock.sleep(l_sleep_time); + l_already_waited_sec := l_already_waited_sec + l_sleep_time; + end loop; + + open l_results for + select /*+ no_parallel */ o.text, o.item_type + from ut_output_buffer_tmp o + where o.output_id = self.output_id + and o.text is not null + order by o.output_id, o.message_id; + + return l_results; + + end; + + /* Important note. + This function code is almost duplicated between two types for performance reasons. + The pipe row clause is much faster on VARCHAR2 then it is on clob. + That is the key reason for two implementations. + */ + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is + l_data sys_refcursor; + l_text ut_varchar2_rows; + l_item_type ut_varchar2_rows; + begin + l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec); + loop + fetch l_data bulk collect into l_text, l_item_type limit 10000; + for idx in 1 .. l_text.count loop + pipe row( ut_output_data_row(l_text(idx), l_item_type(idx)) ); + end loop; + exit when l_data%notfound; + end loop; + close l_data; + self.remove_buffer_info(); + return; + end; + +end; +/ diff --git a/source/core/output_buffers/ut_output_bulk_buffer.tps b/source/core/output_buffers/ut_output_bulk_buffer.tps new file mode 100644 index 000000000..538bc12a5 --- /dev/null +++ b/source/core/output_buffers/ut_output_bulk_buffer.tps @@ -0,0 +1,27 @@ +create or replace noneditionable type ut_output_bulk_buffer under ut_output_buffer_base ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_output_bulk_buffer(self in out nocopy ut_output_bulk_buffer, a_output_id raw := null) return self as result, + overriding member procedure send_line(self in out nocopy ut_output_bulk_buffer, a_text varchar2, a_item_type varchar2 := null), + overriding member procedure send_lines(self in out nocopy ut_output_bulk_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null), + overriding member procedure send_clob(self in out nocopy ut_output_bulk_buffer, a_text clob, a_item_type varchar2 := null), + overriding member procedure lines_to_dbms_output(self in ut_output_bulk_buffer, a_initial_timeout number := null, a_timeout_sec number := null), + overriding member function get_lines_cursor(a_initial_timeout number := null, a_timeout_sec number := null) return sys_refcursor, + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined +) not final +/ diff --git a/source/core/output_buffers/ut_output_clob_buffer_tmp.sql b/source/core/output_buffers/ut_output_clob_buffer_tmp.sql index 48cfe6fde..fa61191ce 100644 --- a/source/core/output_buffers/ut_output_clob_buffer_tmp.sql +++ b/source/core/output_buffers/ut_output_clob_buffer_tmp.sql @@ -6,7 +6,7 @@ begin v_table_sql := 'create table ut_output_clob_buffer_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -45,3 +45,5 @@ begin end; end; / + +create sequence ut_output_clob_buffer_tmp_seq cache 20; diff --git a/source/core/output_buffers/ut_output_clob_table_buffer.tpb b/source/core/output_buffers/ut_output_clob_table_buffer.tpb index 8c2825151..f6d7c9a66 100644 --- a/source/core/output_buffers/ut_output_clob_table_buffer.tpb +++ b/source/core/output_buffers/ut_output_clob_table_buffer.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_output_clob_table_buffer is +create or replace noneditionable type body ut_output_clob_table_buffer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,23 +22,12 @@ create or replace type body ut_output_clob_table_buffer is return; end; - overriding member procedure close(self in out nocopy ut_output_clob_table_buffer) is - pragma autonomous_transaction; - begin - self.last_message_id := self.last_message_id + 1; - insert into ut_output_clob_buffer_tmp(output_id, message_id, is_finished) - values (self.output_id, self.last_message_id, 1); - commit; - self.is_closed := 1; - end; - overriding member procedure send_line(self in out nocopy ut_output_clob_table_buffer, a_text varchar2, a_item_type varchar2 := null) is pragma autonomous_transaction; begin if a_text is not null or a_item_type is not null then - self.last_message_id := self.last_message_id + 1; - insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) - values (self.output_id, self.last_message_id, a_text, a_item_type); + insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_clob_buffer_tmp_seq.nextval, a_text, a_item_type); end if; commit; end; @@ -46,11 +35,10 @@ create or replace type body ut_output_clob_table_buffer is overriding member procedure send_lines(self in out nocopy ut_output_clob_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is pragma autonomous_transaction; begin - insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) - select self.output_id, self.last_message_id + rownum, t.column_value, a_item_type + insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) + select /*+ no_parallel */ self.output_id, ut_output_clob_buffer_tmp_seq.nextval, t.column_value, a_item_type from table(a_text_list) t where t.column_value is not null or a_item_type is not null; - self.last_message_id := self.last_message_id + SQL%rowcount; commit; end; @@ -58,98 +46,87 @@ create or replace type body ut_output_clob_table_buffer is pragma autonomous_transaction; begin if a_text is not null and a_text != empty_clob() or a_item_type is not null then - self.last_message_id := self.last_message_id + 1; - insert into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) - values (self.output_id, self.last_message_id, a_text, a_item_type); + insert /*+ no_parallel */ into ut_output_clob_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_clob_buffer_tmp_seq.nextval, a_text, a_item_type); end if; commit; end; - overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined is - type t_rowid_tab is table of urowid; - l_message_rowids t_rowid_tab; - l_buffer_data ut_output_data_rows; - l_finished_flags ut_integer_list; - l_already_waited_for number(10,2) := 0; - l_finished boolean := false; - lc_init_wait_sec constant naturaln := coalesce(a_initial_timeout, 60 ); -- 1 minute - lc_max_wait_sec constant naturaln := coalesce(a_timeout_sec, 60 * 60 * 4); -- 4 hours - l_wait_for integer := lc_init_wait_sec; - lc_short_sleep_time constant number(1,1) := 0.1; --sleep for 100 ms between checks - lc_long_sleep_time constant number(1) := 1; --sleep for 1 s when waiting long - lc_long_wait_time constant number(1) := 1; --waiting more than 1 sec - l_sleep_time number(2,1) := lc_short_sleep_time; - lc_bulk_limit constant integer := 5000; - l_max_message_id integer := lc_bulk_limit; - - procedure remove_read_data(a_message_rowids t_rowid_tab) is - pragma autonomous_transaction; + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is + lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 ); + l_buffer_rowids ut_varchar2_rows; + l_buffer_data ut_output_data_rows; + l_finished_flags ut_integer_list; + l_already_waited_sec number(10,2) := 0; + l_finished boolean := false; + l_sleep_time number(2,1); + l_lock_status integer; + l_producer_started boolean := false; + l_producer_finished boolean := false; + procedure get_data_from_buffer_table( + a_buffer_data out nocopy ut_output_data_rows, + a_buffer_rowids out nocopy ut_varchar2_rows, + a_finished_flags out nocopy ut_integer_list + ) is + lc_bulk_limit constant integer := 5000; begin - forall i in 1 .. a_message_rowids.count - delete from ut_output_clob_buffer_tmp a - where rowid = a_message_rowids(i); - commit; + with ordered_buffer as ( + select /*+ no_parallel index(a) */ ut_output_data_row(a.text, a.item_type), rowidtochar(a.rowid), is_finished + from ut_output_clob_buffer_tmp a + where a.output_id = self.output_id + and a.message_id <= (select min(message_id) from ut_output_clob_buffer_tmp o where o.output_id = self.output_id) + lc_bulk_limit + order by a.message_id + ) + select /*+ no_parallel */ b.* + bulk collect into a_buffer_data, a_buffer_rowids, a_finished_flags + from ordered_buffer b; end; - procedure remove_buffer_info is - pragma autonomous_transaction; + procedure remove_read_data(a_buffer_rowids ut_varchar2_rows) is + pragma autonomous_transaction; begin - delete from ut_output_buffer_info_tmp a - where a.output_id = self.output_id; + forall i in 1 .. a_buffer_rowids.count + delete from ut_output_clob_buffer_tmp a + where rowid = chartorowid(a_buffer_rowids(i)); commit; end; - begin + begin while not l_finished loop - with ordered_buffer as ( - select /*+ index(a) */ a.rowid, ut_output_data_row(a.text, a.item_type), is_finished - from ut_output_clob_buffer_tmp a - where a.output_id = self.output_id - and a.message_id <= l_max_message_id - order by a.message_id - ) - select b.* - bulk collect into l_message_rowids, l_buffer_data, l_finished_flags - from ordered_buffer b; - --nothing fetched from output, wait and try again - if l_buffer_data.count = 0 then - $if dbms_db_version.version >= 18 $then - dbms_session.sleep(l_sleep_time); - $else - dbms_lock.sleep(l_sleep_time); - $end - l_already_waited_for := l_already_waited_for + l_sleep_time; - if l_already_waited_for > lc_long_wait_time then - l_sleep_time := lc_long_sleep_time; - end if; - else - --reset wait time - -- we wait lc_max_wait_sec for new message - l_wait_for := lc_max_wait_sec; - l_already_waited_for := 0; - l_sleep_time := lc_short_sleep_time; + l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end; + l_lock_status := self.get_lock_status(); + get_data_from_buffer_table( l_buffer_data, l_buffer_rowids, l_finished_flags ); + + if l_buffer_data.count > 0 then + l_already_waited_sec := 0; for i in 1 .. l_buffer_data.count loop if l_buffer_data(i).text is not null then - pipe row(l_buffer_data(i)); + pipe row( l_buffer_data(i) ); elsif l_finished_flags(i) = 1 then l_finished := true; exit; end if; end loop; - remove_read_data(l_message_rowids); - l_max_message_id := l_max_message_id + lc_bulk_limit; - end if; - if l_finished or l_already_waited_for >= l_wait_for then - remove_buffer_info(); - if l_already_waited_for > 0 and l_already_waited_for >= l_wait_for then - raise_application_error( - ut_utils.gc_out_buffer_timeout, - 'Timeout occurred while waiting for output data. Waited for: '||l_already_waited_for||' seconds.' - ); - end if; + remove_read_data(l_buffer_rowids); + else + --nothing fetched from output, wait. + dbms_lock.sleep(l_sleep_time); + l_already_waited_sec := l_already_waited_sec + l_sleep_time; end if; + + l_producer_started := (l_lock_status <> 0 or l_buffer_data.count > 0) or l_producer_started; + l_producer_finished := (l_producer_started and l_lock_status = 0 and l_buffer_data.count = 0) or l_producer_finished; + + l_finished := + self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec) + or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec) + or l_producer_finished + or l_finished; + end loop; + + self.remove_buffer_info(); return; end; diff --git a/source/core/output_buffers/ut_output_clob_table_buffer.tps b/source/core/output_buffers/ut_output_clob_table_buffer.tps index 2eb3640b0..b254c1f47 100644 --- a/source/core/output_buffers/ut_output_clob_table_buffer.tps +++ b/source/core/output_buffers/ut_output_clob_table_buffer.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_clob_table_buffer under ut_output_buffer_base ( +create or replace noneditionable type ut_output_clob_table_buffer under ut_output_buffer_base ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ create or replace type ut_output_clob_table_buffer under ut_output_buffer_base ( overriding member procedure send_line(self in out nocopy ut_output_clob_table_buffer, a_text varchar2, a_item_type varchar2 := null), overriding member procedure send_lines(self in out nocopy ut_output_clob_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null), overriding member procedure send_clob(self in out nocopy ut_output_clob_table_buffer, a_text clob, a_item_type varchar2 := null), - overriding member procedure close(self in out nocopy ut_output_clob_table_buffer), - overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined ) not final / diff --git a/source/core/output_buffers/ut_output_data_row.tps b/source/core/output_buffers/ut_output_data_row.tps index b0f5efe91..2eed2ee1f 100644 --- a/source/core/output_buffers/ut_output_data_row.tps +++ b/source/core/output_buffers/ut_output_data_row.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_data_row as object ( +create or replace noneditionable type ut_output_data_row as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/output_buffers/ut_output_data_rows.tps b/source/core/output_buffers/ut_output_data_rows.tps index 1918f0c81..a10440617 100644 --- a/source/core/output_buffers/ut_output_data_rows.tps +++ b/source/core/output_buffers/ut_output_data_rows.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_data_rows as +create or replace noneditionable type ut_output_data_rows as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/output_buffers/ut_output_table_buffer.tpb b/source/core/output_buffers/ut_output_table_buffer.tpb index e3f869b22..d87362572 100644 --- a/source/core/output_buffers/ut_output_table_buffer.tpb +++ b/source/core/output_buffers/ut_output_table_buffer.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_output_table_buffer is +create or replace noneditionable type body ut_output_table_buffer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,21 +22,11 @@ create or replace type body ut_output_table_buffer is return; end; - overriding member procedure close(self in out nocopy ut_output_table_buffer) is - pragma autonomous_transaction; - begin - self.last_message_id := self.last_message_id + 1; - insert into ut_output_buffer_tmp(output_id, message_id, is_finished) - values (self.output_id, self.last_message_id, 1); - commit; - self.is_closed := 1; - end; - overriding member procedure send_line(self in out nocopy ut_output_table_buffer, a_text varchar2, a_item_type varchar2 := null) is pragma autonomous_transaction; begin if a_text is not null or a_item_type is not null then - if length(a_text) > ut_utils.gc_max_storage_varchar2_len then + if lengthb(a_text) > ut_utils.gc_max_storage_varchar2_len then self.send_lines( ut_utils.convert_collection( ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len) @@ -44,9 +34,8 @@ create or replace type body ut_output_table_buffer is a_item_type ); else - self.last_message_id := self.last_message_id + 1; - insert into ut_output_buffer_tmp(output_id, message_id, text, item_type) - values (self.output_id, self.last_message_id, a_text, a_item_type); + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type); end if; commit; end if; @@ -55,11 +44,10 @@ create or replace type body ut_output_table_buffer is overriding member procedure send_lines(self in out nocopy ut_output_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null) is pragma autonomous_transaction; begin - insert into ut_output_buffer_tmp(output_id, message_id, text, item_type) - select self.output_id, self.last_message_id + rownum, t.column_value, a_item_type + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + select /*+ no_parallel */ self.output_id, ut_output_buffer_tmp_seq.nextval, t.column_value, a_item_type from table(a_text_list) t where t.column_value is not null or a_item_type is not null; - self.last_message_id := self.last_message_id + SQL%rowcount; commit; end; @@ -67,7 +55,7 @@ create or replace type body ut_output_table_buffer is pragma autonomous_transaction; begin if a_text is not null and a_text != empty_clob() or a_item_type is not null then - if length(a_text) > ut_utils.gc_max_storage_varchar2_len then + if ut_utils.lengthb_clob(a_text) > ut_utils.gc_max_storage_varchar2_len then self.send_lines( ut_utils.convert_collection( ut_utils.clob_to_table(a_text, ut_utils.gc_max_storage_varchar2_len) @@ -75,99 +63,100 @@ create or replace type body ut_output_table_buffer is a_item_type ); else - self.last_message_id := self.last_message_id + 1; - insert into ut_output_buffer_tmp(output_id, message_id, text, item_type) - values (self.output_id, self.last_message_id, a_text, a_item_type); + insert /*+ no_parallel */ into ut_output_buffer_tmp(output_id, message_id, text, item_type) + values (self.output_id, ut_output_buffer_tmp_seq.nextval, a_text, a_item_type); end if; commit; end if; end; - overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined is - l_buffer_data ut_varchar2_rows; - l_item_types ut_varchar2_rows; - l_finished_flags ut_integer_list; - l_already_waited_for number(10,2) := 0; - l_finished boolean := false; - lc_init_wait_sec constant naturaln := coalesce(a_initial_timeout, 60 ); -- 1 minute - lc_max_wait_sec constant naturaln := coalesce(a_timeout_sec, 60 * 60 * 4); -- 4 hours - l_wait_for integer := lc_init_wait_sec; - lc_short_sleep_time constant number(1,1) := 0.1; --sleep for 100 ms between checks - lc_long_sleep_time constant number(1) := 1; --sleep for 1 s when waiting long - lc_long_wait_time constant number(1) := 1; --waiting more than 1 sec - l_sleep_time number(2,1) := lc_short_sleep_time; - lc_bulk_limit constant integer := 5000; - l_max_message_id integer := lc_bulk_limit; - - procedure get_data_from_buffer( - a_max_message_id integer, - a_buffer_data out nocopy ut_varchar2_rows, - a_item_types out nocopy ut_varchar2_rows, - a_finished_flags out nocopy ut_integer_list - ) is - pragma autonomous_transaction; - begin - delete from ( - select * - from ut_output_buffer_tmp o - where o.output_id = self.output_id - and o.message_id <= a_max_message_id - order by o.message_id - ) d - returning d.text, d.item_type, d.is_finished - bulk collect into a_buffer_data, a_item_types, a_finished_flags; - commit; - - end; - - procedure remove_buffer_info is - pragma autonomous_transaction; - begin - delete from ut_output_buffer_info_tmp a - where a.output_id = self.output_id; - commit; - end; + overriding member procedure lines_to_dbms_output(self in ut_output_table_buffer, a_initial_timeout number := null, a_timeout_sec number := null) is + l_data sys_refcursor; + l_text ut_varchar2_rows; + l_item_type ut_varchar2_rows; + begin + l_data := self.get_lines_cursor(a_initial_timeout, a_timeout_sec); + loop + fetch l_data bulk collect into l_text, l_item_type limit 10000; + for idx in 1 .. l_text.count loop + dbms_output.put_line(l_text(idx)); + end loop; + exit when l_data%notfound; + end loop; + close l_data; + end; - begin + /* Important note. + This function code is almost duplicated between two types for performance reasons. + The pipe row clause is much faster on VARCHAR2 then it is on clob. + That is the key reason for two implementations. + */ + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined is + lc_init_wait_sec constant number := coalesce(a_initial_timeout, 10 ); + l_buffer_texts ut_varchar2_rows; + l_buffer_item_types ut_varchar2_rows; + l_finished_flags ut_integer_list; + l_already_waited_sec number(10,2) := 0; + l_finished boolean := false; + l_sleep_time number(2,1); + l_lock_status integer; + l_producer_started boolean := false; + l_producer_finished boolean := false; + + procedure get_data_from_buffer_table( + a_buffer_texts out nocopy ut_varchar2_rows, + a_buffer_item_types out nocopy ut_varchar2_rows, + a_finished_flags out nocopy ut_integer_list + ) is + lc_bulk_limit constant integer := 20000; + pragma autonomous_transaction; + begin + delete /*+ no_parallel */ from ( + select /*+ no_parallel */ * + from ut_output_buffer_tmp a + where a.output_id = self.output_id + and a.message_id <= (select min(message_id) from ut_output_buffer_tmp o where o.output_id = self.output_id) + lc_bulk_limit + order by a.message_id + ) d + returning d.text, d.item_type, d.is_finished + bulk collect into a_buffer_texts, a_buffer_item_types, a_finished_flags; + commit; + end; + begin while not l_finished loop - get_data_from_buffer( l_max_message_id, l_buffer_data, l_item_types, l_finished_flags); - --nothing fetched from output, wait and try again - if l_buffer_data.count = 0 then - $if dbms_db_version.version >= 18 $then - dbms_session.sleep(l_sleep_time); - $else - dbms_lock.sleep(l_sleep_time); - $end - l_already_waited_for := l_already_waited_for + l_sleep_time; - if l_already_waited_for > lc_long_wait_time then - l_sleep_time := lc_long_sleep_time; - end if; - else - --reset wait time - -- we wait lc_max_wait_sec for new message - l_wait_for := lc_max_wait_sec; - l_already_waited_for := 0; - l_sleep_time := lc_short_sleep_time; - for i in 1 .. l_buffer_data.count loop - if l_buffer_data(i) is not null then - pipe row(ut_output_data_row(l_buffer_data(i),l_item_types(i))); + + l_sleep_time := case when l_already_waited_sec >= 1 then 0.5 else 0.1 end; + l_lock_status := self.get_lock_status(); + get_data_from_buffer_table( l_buffer_texts, l_buffer_item_types, l_finished_flags ); + + if l_buffer_texts.count > 0 then + l_already_waited_sec := 0; + for i in 1 .. l_buffer_texts.count loop + if l_buffer_texts(i) is not null then + pipe row( ut_output_data_row(l_buffer_texts(i), l_buffer_item_types(i)) ); elsif l_finished_flags(i) = 1 then l_finished := true; exit; end if; end loop; - l_max_message_id := l_max_message_id + lc_bulk_limit; - end if; - if l_finished or l_already_waited_for >= l_wait_for then - remove_buffer_info(); - if l_already_waited_for > 0 and l_already_waited_for >= l_wait_for then - raise_application_error( - ut_utils.gc_out_buffer_timeout, - 'Timeout occurred while waiting for output data. Waited for: '||l_already_waited_for||' seconds.' - ); - end if; + else + --nothing fetched from output, wait. + dbms_lock.sleep(l_sleep_time); + l_already_waited_sec := l_already_waited_sec + l_sleep_time; end if; + + l_producer_started := (l_lock_status <> 0 or l_buffer_texts.count > 0) or l_producer_started; + l_producer_finished := (l_producer_started and l_lock_status = 0 and l_buffer_texts.count = 0) or l_producer_finished; + + l_finished := + self.timeout_producer_not_finished(l_producer_finished, l_already_waited_sec, a_timeout_sec) + or self.timeout_producer_not_started(l_producer_started, l_already_waited_sec, lc_init_wait_sec) + or l_producer_finished + or l_finished; + end loop; + + self.remove_buffer_info(); return; end; diff --git a/source/core/output_buffers/ut_output_table_buffer.tps b/source/core/output_buffers/ut_output_table_buffer.tps index 3e395304a..3f13e736d 100644 --- a/source/core/output_buffers/ut_output_table_buffer.tps +++ b/source/core/output_buffers/ut_output_table_buffer.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_table_buffer under ut_output_buffer_base ( +create or replace noneditionable type ut_output_table_buffer under ut_output_buffer_base ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ create or replace type ut_output_table_buffer under ut_output_buffer_base ( overriding member procedure send_line(self in out nocopy ut_output_table_buffer, a_text varchar2, a_item_type varchar2 := null), overriding member procedure send_lines(self in out nocopy ut_output_table_buffer, a_text_list ut_varchar2_rows, a_item_type varchar2 := null), overriding member procedure send_clob(self in out nocopy ut_output_table_buffer, a_text clob, a_item_type varchar2 := null), - overriding member procedure close(self in out nocopy ut_output_table_buffer), - overriding member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_output_data_rows pipelined + overriding member procedure lines_to_dbms_output(self in ut_output_table_buffer, a_initial_timeout number := null, a_timeout_sec number := null), + overriding member function get_lines(a_initial_timeout number := null, a_timeout_sec number := null) return ut_output_data_rows pipelined ) not final / diff --git a/source/core/session_context/ut_session_context.pkb b/source/core/session_context/ut_session_context.pkb index 2364d8513..12d104eed 100644 --- a/source/core/session_context/ut_session_context.pkb +++ b/source/core/session_context/ut_session_context.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_session_context as +create or replace noneditionable package body ut_session_context as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/session_context/ut_session_context.pks b/source/core/session_context/ut_session_context.pks index bbe256db4..a5e4038a0 100644 --- a/source/core/session_context/ut_session_context.pks +++ b/source/core/session_context/ut_session_context.pks @@ -1,7 +1,7 @@ -create or replace package ut_session_context as +create or replace noneditionable package ut_session_context as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/session_context/ut_session_info.tpb b/source/core/session_context/ut_session_info.tpb index 2d3fdbed9..14f52b003 100644 --- a/source/core/session_context/ut_session_info.tpb +++ b/source/core/session_context/ut_session_info.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_session_info as +create or replace noneditionable type body ut_session_info as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -28,12 +28,14 @@ create or replace type body ut_session_info as member procedure before_calling_run(self in out nocopy ut_session_info, a_run in ut_run) is begin ut_session_context.set_context( 'run_paths', ut_utils.to_string( ut_utils.table_to_clob( a_run.run_paths,',' ), null ) ); + ut_session_context.set_context( 'coverage_run_id', rawtohex( a_run.coverage_options.coverage_run_id ) ); dbms_application_info.set_module( 'utPLSQL', null ); end; member procedure after_calling_run(self in out nocopy ut_session_info, a_run in ut_run) is begin ut_session_context.clear_context( 'run_paths' ); + ut_session_context.clear_context( 'coverage_run_id' ); dbms_application_info.set_module( module, action ); dbms_application_info.set_client_info( client_info ); end; diff --git a/source/core/session_context/ut_session_info.tps b/source/core/session_context/ut_session_info.tps index 863b0dc3e..d682a6bad 100644 --- a/source/core/session_context/ut_session_info.tps +++ b/source/core/session_context/ut_session_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_session_info under ut_event_listener ( +create or replace noneditionable type ut_session_info under ut_event_listener ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_console_reporter_base.tpb b/source/core/types/ut_console_reporter_base.tpb index df256e832..0825b8ba2 100644 --- a/source/core/types/ut_console_reporter_base.tpb +++ b/source/core/types/ut_console_reporter_base.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_console_reporter_base is +create or replace noneditionable type body ut_console_reporter_base is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_console_reporter_base.tps b/source/core/types/ut_console_reporter_base.tps index 6c0a08502..46b344e6e 100644 --- a/source/core/types/ut_console_reporter_base.tps +++ b/source/core/types/ut_console_reporter_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_console_reporter_base under ut_output_reporter_base( +create or replace noneditionable type ut_console_reporter_base under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_coverage_options.tpb b/source/core/types/ut_coverage_options.tpb index 6eb4921a7..2accd960a 100644 --- a/source/core/types/ut_coverage_options.tpb +++ b/source/core/types/ut_coverage_options.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_coverage_options as +create or replace noneditionable type body ut_coverage_options as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,7 +22,11 @@ create or replace type body ut_coverage_options as schema_names ut_varchar2_rows := null, exclude_objects ut_varchar2_rows := null, include_objects ut_varchar2_rows := null, - file_mappings ut_file_mappings := null + file_mappings ut_file_mappings := null, + include_schema_expr varchar2 := null, + include_object_expr varchar2 := null, + exclude_schema_expr varchar2 := null, + exclude_object_expr varchar2 := null ) return self as result is function to_ut_object_list(a_names ut_varchar2_rows, a_schema_names ut_varchar2_rows) return ut_object_names is l_result ut_object_names; @@ -54,10 +58,15 @@ create or replace type body ut_coverage_options as if exclude_objects is not empty then self.exclude_objects := to_ut_object_list(exclude_objects, self.schema_names); end if; --- self.exclude_objects := self.exclude_objects multiset union all ut_suite_manager.get_schema_ut_packages(schema_names); self.include_objects := to_ut_object_list(include_objects, self.schema_names); + self.include_schema_expr := include_schema_expr; + self.include_object_expr := include_object_expr; + self.exclude_schema_expr := exclude_schema_expr; + self.exclude_object_expr := exclude_object_expr; + + return; end; end; diff --git a/source/core/types/ut_coverage_options.tps b/source/core/types/ut_coverage_options.tps index d6e86b653..f2db1dafe 100644 --- a/source/core/types/ut_coverage_options.tps +++ b/source/core/types/ut_coverage_options.tps @@ -1,7 +1,7 @@ -create or replace type ut_coverage_options force as object ( +create or replace noneditionable type ut_coverage_options force as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,13 +21,21 @@ create or replace type ut_coverage_options force as object ( exclude_objects ut_object_names, include_objects ut_object_names, file_mappings ut_file_mappings, + include_schema_expr varchar2(4000), + include_object_expr varchar2(4000), + exclude_schema_expr varchar2(4000), + exclude_object_expr varchar2(4000), constructor function ut_coverage_options( self in out nocopy ut_coverage_options, coverage_run_id raw, schema_names ut_varchar2_rows := null, exclude_objects ut_varchar2_rows := null, include_objects ut_varchar2_rows := null, - file_mappings ut_file_mappings := null + file_mappings ut_file_mappings := null, + include_schema_expr varchar2 := null, + include_object_expr varchar2 := null, + exclude_schema_expr varchar2 := null, + exclude_object_expr varchar2 := null ) return self as result ) / diff --git a/source/core/types/ut_executable.tpb b/source/core/types/ut_executable.tpb index 826a5aaa1..2fa3c1b5d 100644 --- a/source/core/types/ut_executable.tpb +++ b/source/core/types/ut_executable.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_executable is +create or replace noneditionable type body ut_executable is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -38,20 +38,30 @@ create or replace type body ut_executable is return ut_metadata.form_name(l_owner_name, object_name, procedure_name); end; - member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) is + member procedure do_execute( + self in out nocopy ut_executable, + a_item in out nocopy ut_suite_item, + a_ignored_exception_names in ut_varchar2_rows := null, + a_ignored_exception_numbers in ut_varchar2_rows :=null) + is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_item); + l_completed_without_errors := self.do_execute(a_item, a_ignored_exception_names, a_ignored_exception_numbers); end do_execute; - member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean is - l_statement varchar2(4000); - l_status number; - l_cursor_number number; - l_completed_without_errors boolean := true; - l_failed_with_invalid_pck boolean := true; - l_start_transaction_id varchar2(250); - l_end_transaction_id varchar2(250); + member function do_execute( + self in out nocopy ut_executable, + a_item in out nocopy ut_suite_item, + a_ignored_exception_names in ut_varchar2_rows := null, + a_ignored_exception_numbers in ut_varchar2_rows :=null) + return boolean is + l_statement varchar2(4000); + l_status number; + l_cursor_number number; + l_completed_without_errors boolean := true; + l_failed_with_invalid_pck boolean := true; + l_start_transaction_id varchar2(250); + l_end_transaction_id varchar2(250); function is_defined return boolean is l_result boolean := false; @@ -111,19 +121,33 @@ create or replace type body ut_executable is if l_completed_without_errors then l_statement := 'declare' || chr(10) || - ' l_error_stack varchar2(32767);' || chr(10) || - ' l_error_backtrace varchar2(32767);' || chr(10) || + ' l_error_stack varchar2(32767);' || chr(10) || + ' l_error_backtrace varchar2(32767);' || chr(10) || + ' l_ignored_exception_detected integer := 0;' || chr(10) || 'begin' || chr(10) || ' begin' || chr(10) || ' ' || self.form_name( a_skip_current_user_schema => true ) || ';' || chr(10) || ' exception' || chr(10) || + case when a_ignored_exception_names is not empty then + ' when ' || ut_utils.table_to_clob( a_ignored_exception_names, ' or ' ) || ' then ' || chr(10) || + ' l_ignored_exception_detected := 1;' + end || ' when others then ' || chr(10) || + case when a_ignored_exception_numbers is not empty then + ' if sqlcode in (' || ut_utils.table_to_clob( a_ignored_exception_numbers, ', ' ) || ') then ' || chr(10) || + ' l_ignored_exception_detected := 1; ' || chr(10) || + ' else ' || chr(10) || + ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || + ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || + ' end if;' + else ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || - ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || - ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) || + ' l_error_backtrace := dbms_utility.format_error_backtrace;' + end || chr(10) || ' end;' || chr(10) || ' :a_error_stack := l_error_stack;' || chr(10) || ' :a_error_backtrace := l_error_backtrace;' || chr(10) || + ' :a_ignored_exception_detected := l_ignored_exception_detected;' || chr(10) || 'end;'; ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement); @@ -139,9 +163,11 @@ create or replace type body ut_executable is dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native); dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767); dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767); + dbms_sql.bind_variable(l_cursor_number, 'a_ignored_exception_detected', to_number(null)); l_status := dbms_sql.execute(l_cursor_number); dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack); dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace); + dbms_sql.variable_value(l_cursor_number, 'a_ignored_exception_detected', self.ignored_exception_detected); dbms_sql.close_cursor(l_cursor_number); exception when ut_utils.ex_invalid_package then diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps index 786d926ad..0945a8996 100644 --- a/source/core/types/ut_executable.tps +++ b/source/core/types/ut_executable.tps @@ -1,7 +1,7 @@ -create or replace type ut_executable under ut_event_item( +create or replace noneditionable type ut_executable under ut_event_item( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,26 +18,37 @@ create or replace type ut_executable under ut_event_item( /** * The name of the event to be executed before and after the executable is invoked */ - executable_type varchar2(250 char), - owner_name varchar2(250 char), - object_name varchar2(250 char), - procedure_name varchar2(250 char), - error_backtrace varchar2(4000), - error_stack varchar2(4000), - serveroutput clob, + executable_type varchar2(250 char), + owner_name varchar2(250 char), + object_name varchar2(250 char), + procedure_name varchar2(250 char), + error_backtrace varchar2(4000), + error_stack varchar2(4000), + ignored_exception_detected integer, + serveroutput clob, /** * Used for ordering of executables, as Oracle doesn not guarantee ordering of items in a nested table. */ seq_no integer, constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_executable_type varchar2) return self as result, member function form_name(a_skip_current_user_schema boolean := false) return varchar2, - member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item), + member procedure do_execute( + self in out nocopy ut_executable, + a_item in out nocopy ut_suite_item, + a_ignored_exception_names in ut_varchar2_rows := null, + a_ignored_exception_numbers in ut_varchar2_rows := null + ), /** * executes the defines executable * returns true if executed without exceptions * returns false if exceptions were raised */ - member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean, + member function do_execute( + self in out nocopy ut_executable, + a_item in out nocopy ut_suite_item, + a_ignored_exception_names in ut_varchar2_rows := null, + a_ignored_exception_numbers in ut_varchar2_rows :=null + ) return boolean, member function get_error_stack_trace return varchar2 ) not final / diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb index 6f25ea1c8..8e48bd70a 100644 --- a/source/core/types/ut_executable_test.tpb +++ b/source/core/types/ut_executable_test.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_executable_test as +create or replace noneditionable type body ut_executable_test as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -31,141 +31,129 @@ 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_varchar2_rows + a_expected_errors in ut_varchar2_rows ) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_item, a_expected_error_codes); + l_completed_without_errors := self.do_execute(a_item, a_expected_errors); end do_execute; 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_varchar2_rows + a_expected_errors 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_failure_message varchar2(32767); + l_expected_error_names ut_varchar2_rows; + l_expected_error_numbers ut_varchar2_rows; + l_all_expected_errors ut_varchar2_rows; + + procedure build_expected_exceptions( + a_item in out nocopy ut_suite_item, + a_exception_definitions in ut_varchar2_rows, + a_exception_names_list out ut_varchar2_rows, + a_exception_numbers_list out ut_varchar2_rows + ) 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 + l_exception_name varchar2(128); + function to_exception_number (a_exception_var in varchar2, a_item ut_suite_item) return integer is + function get_exception_no(a_exception_var in varchar2) return integer is + l_exc_no integer; begin - execute immediate 'begin null; exception when '||a_exception_name||' then null; end;'; - l_exception_type := c_named_exception; + execute immediate 'declare l_exception integer; begin :l_exception := '||a_exception_var||'; end;' + using out l_exc_no; + return l_exc_no; 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; + return null; 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( + coalesce( + get_exception_no(a_exception_var), + get_exception_no(a_item.object_name||'.'||a_exception_var), + get_exception_no(a_item.object_owner||'.'||a_exception_var), + get_exception_no(a_item.object_owner||'.'||a_item.object_name||'.'||a_exception_var) + ) + ); + end; + + function to_exception_name (a_exception_var in varchar2, a_item ut_suite_item) return varchar2 is + function verify_name(a_exception_name in varchar2) return varchar2 is + begin + --check if it is an existing, named exception + execute immediate 'begin null; exception when '||a_exception_name||' then null; end;'; + return a_exception_name; + exception + when others then + return null; + end; + begin + return coalesce( + verify_name(a_exception_var), + verify_name(a_item.object_name||'.'||a_exception_var), + verify_name(a_item.object_owner||'.'||a_exception_var), + verify_name(a_item.object_owner||'.'||a_item.object_name||'.'||a_exception_var) + ); - 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_exception_names_list := ut_varchar2_rows(); + a_exception_numbers_list := ut_varchar2_rows(); + if a_exception_definitions is not empty then + for i in 1 .. a_exception_definitions.count loop + l_exception_number := + coalesce( + to_exception_number(a_exception_definitions(i), a_item), + to_number(a_exception_definitions(i) default null on conversion error ) + ); + l_exception_name := to_exception_name(a_exception_definitions(i), a_item); + + if l_exception_number is null and l_exception_name is null then a_item.put_warning( - 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Parameter ignored.', + 'Invalid parameter value "'||a_exception_definitions(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.', + 'Invalid parameter value "'||a_exception_definitions(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; + elsif l_exception_number is not null then + a_exception_numbers_list.extend; + a_exception_numbers_list(a_exception_numbers_list.last) := l_exception_number; + elsif l_exception_name is not null then + a_exception_names_list.extend; + a_exception_names_list(a_exception_names_list.last) := l_exception_name; 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 + + function get_exception_failure_message(a_expected_errors_list ut_varchar2_rows) return varchar2 is l_actual_error_no integer; - l_expected_error_codes varchar2(4000); + l_expected_errors varchar2(4000); l_fail_message varchar2(4000); begin --Convert the ut_varchar2_list to string to can construct the message - l_expected_error_codes := ut_utils.table_to_clob(a_expected_error_codes, ', '); + l_expected_errors := ut_utils.table_to_clob(a_expected_errors_list, ', '); if self.error_stack is null then - l_fail_message := 'Expected one of exceptions ('||l_expected_error_codes||') but nothing was raised.'; + l_fail_message := 'Expected one of exceptions ('|| l_expected_errors|| ') 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 or l_actual_error_no is null then + l_actual_error_no := regexp_substr(self.error_stack, '^[[:alpha:]]{3}(-[0-9]+)', subexpression=>1); + if not l_actual_error_no member of a_expected_errors_list 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||')'; + if cardinality(a_expected_errors_list) > 1 then + l_fail_message := l_fail_message || 'be one of: ('|| l_expected_errors|| ')'; else - l_fail_message := l_fail_message || 'equal: '||l_expected_error_codes; + l_fail_message := l_fail_message || 'equal: '||l_expected_errors; end if; l_fail_message := substr( l_fail_message||chr(10)||self.error_stack||chr(10)||self.error_backtrace, 1, 4000 ); end if; @@ -175,14 +163,16 @@ create or replace type body ut_executable_test as end; 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); - 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 ); + build_expected_exceptions(a_item, a_expected_errors, l_expected_error_names, l_expected_error_numbers ); + + self.do_execute(a_item, l_expected_error_names, l_expected_error_numbers); + l_all_expected_errors := l_expected_error_names multiset union all l_expected_error_numbers; + if l_all_expected_errors is not empty and self.ignored_exception_detected = 0 then + l_failure_message := get_exception_failure_message( l_all_expected_errors); - if l_expected_except_message is not null then + if l_failure_message is not null then ut_expectation_processor.add_expectation_result( - ut_expectation_result(ut_utils.gc_failure, null, l_expected_except_message, false) + ut_expectation_result(ut_utils.gc_failure, null, l_failure_message, false) ); end if; self.error_stack := null; diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps index 88500c9be..d477456f6 100644 --- a/source/core/types/ut_executable_test.tps +++ b/source/core/types/ut_executable_test.tps @@ -1,7 +1,7 @@ -create or replace type ut_executable_test authid current_user under ut_executable ( +create or replace noneditionable type ut_executable_test authid current_user under ut_executable ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -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_varchar2_rows + a_expected_errors 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_varchar2_rows + a_expected_errors in ut_varchar2_rows ) return boolean ) final; diff --git a/source/core/types/ut_executables.tps b/source/core/types/ut_executables.tps index f5732d5a1..7718c44b8 100644 --- a/source/core/types/ut_executables.tps +++ b/source/core/types/ut_executables.tps @@ -1,7 +1,7 @@ -create or replace type ut_executables as +create or replace noneditionable type ut_executables as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_expectation_result.tpb b/source/core/types/ut_expectation_result.tpb index 9aa8b81c0..3ffd11789 100644 --- a/source/core/types/ut_expectation_result.tpb +++ b/source/core/types/ut_expectation_result.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_expectation_result is +create or replace noneditionable type body ut_expectation_result is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_expectation_result.tps b/source/core/types/ut_expectation_result.tps index 815d90820..e7eedd5ce 100644 --- a/source/core/types/ut_expectation_result.tps +++ b/source/core/types/ut_expectation_result.tps @@ -1,7 +1,7 @@ -create or replace type ut_expectation_result under ut_event_item( +create or replace noneditionable type ut_expectation_result under ut_event_item( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_expectation_results.tps b/source/core/types/ut_expectation_results.tps index 963c726f8..168941112 100644 --- a/source/core/types/ut_expectation_results.tps +++ b/source/core/types/ut_expectation_results.tps @@ -1,7 +1,7 @@ -create or replace type ut_expectation_results as +create or replace noneditionable type ut_expectation_results as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_file_mapping.tpb b/source/core/types/ut_file_mapping.tpb index 247aa5ac8..d693ac633 100644 --- a/source/core/types/ut_file_mapping.tpb +++ b/source/core/types/ut_file_mapping.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_file_mapping as +create or replace noneditionable type body ut_file_mapping as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_file_mapping.tps b/source/core/types/ut_file_mapping.tps index c032e0b77..d28ef40ae 100644 --- a/source/core/types/ut_file_mapping.tps +++ b/source/core/types/ut_file_mapping.tps @@ -1,7 +1,7 @@ -create or replace type ut_file_mapping as object( +create or replace noneditionable type ut_file_mapping as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_file_mappings.tps b/source/core/types/ut_file_mappings.tps index a48c3e872..715530ee0 100644 --- a/source/core/types/ut_file_mappings.tps +++ b/source/core/types/ut_file_mappings.tps @@ -1,7 +1,7 @@ -create or replace type ut_file_mappings as +create or replace noneditionable type ut_file_mappings as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_integer_list.tps b/source/core/types/ut_integer_list.tps index 25f1679b2..b15cf743c 100644 --- a/source/core/types/ut_integer_list.tps +++ b/source/core/types/ut_integer_list.tps @@ -1,7 +1,7 @@ -create or replace type ut_integer_list as +create or replace noneditionable type ut_integer_list as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_key_value_pair.tps b/source/core/types/ut_key_value_pair.tps index 873a354fd..e26fff031 100644 --- a/source/core/types/ut_key_value_pair.tps +++ b/source/core/types/ut_key_value_pair.tps @@ -1,7 +1,7 @@ -create or replace type ut_key_value_pair force as object( +create or replace noneditionable type ut_key_value_pair force as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_key_value_pairs.tps b/source/core/types/ut_key_value_pairs.tps index 28fa87cf0..d9e829dc9 100644 --- a/source/core/types/ut_key_value_pairs.tps +++ b/source/core/types/ut_key_value_pairs.tps @@ -1,7 +1,7 @@ -create or replace type ut_key_value_pairs as +create or replace noneditionable type ut_key_value_pairs as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 74c7ff81c..c91320d8a 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_logical_suite as +create or replace noneditionable type body ut_logical_suite as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ create or replace type body ut_logical_suite as limitations under the License. */ - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite) is + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_skip_reason in varchar2) is begin ut_event_manager.trigger_event(ut_event_manager.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop - self.items(i).mark_as_skipped(); + self.items(i).mark_as_skipped(coalesce(a_skip_reason,self.disabled_reason)); end loop; self.end_time := self.start_time; ut_event_manager.trigger_event(ut_event_manager.gc_after_suite, self); diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index 71cfdb23d..245c422a1 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -1,7 +1,7 @@ -create or replace type ut_logical_suite under ut_suite_item ( +create or replace noneditionable type ut_logical_suite force under ut_suite_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ create or replace type ut_logical_suite under ut_suite_item ( */ items ut_suite_items, - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite), + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_skip_reason in varchar2), overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer, a_force boolean := false), overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), diff --git a/source/core/types/ut_object_name.tpb b/source/core/types/ut_object_name.tpb index cf1ab2a78..67834a9ed 100644 --- a/source/core/types/ut_object_name.tpb +++ b/source/core/types/ut_object_name.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_object_name as +create or replace noneditionable type body ut_object_name as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_object_name.tps b/source/core/types/ut_object_name.tps index 28e41958f..7f6341a32 100644 --- a/source/core/types/ut_object_name.tps +++ b/source/core/types/ut_object_name.tps @@ -1,7 +1,7 @@ -create or replace type ut_object_name as object ( +create or replace noneditionable type ut_object_name as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_object_names.tps b/source/core/types/ut_object_names.tps index 2f7e5ea16..879493a63 100644 --- a/source/core/types/ut_object_names.tps +++ b/source/core/types/ut_object_names.tps @@ -1,7 +1,7 @@ -create or replace type ut_object_names as +create or replace noneditionable type ut_object_names as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_output_reporter_base.tpb b/source/core/types/ut_output_reporter_base.tpb index c4ba73ba5..4553f1780 100644 --- a/source/core/types/ut_output_reporter_base.tpb +++ b/source/core/types/ut_output_reporter_base.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_output_reporter_base is +create or replace noneditionable type body ut_output_reporter_base is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -41,13 +41,6 @@ create or replace type body ut_output_reporter_base is return l_result; end; - overriding member procedure before_calling_run(self in out nocopy ut_output_reporter_base, a_run in ut_run) is - l_output_table_buffer ut_output_table_buffer; - begin - (self as ut_reporter_base).before_calling_run(a_run); - l_output_table_buffer := treat(self.output_buffer as ut_output_table_buffer); - end; - member procedure print_text(self in out nocopy ut_output_reporter_base, a_text varchar2, a_item_type varchar2 := null) is begin self.output_buffer.send_line(a_text, a_item_type); @@ -65,7 +58,7 @@ create or replace type body ut_output_reporter_base is final member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural) return ut_output_data_rows pipelined is begin - for i in (select value(x) val from table(self.output_buffer.get_lines(a_initial_timeout, a_timeout_sec)) x ) loop + for i in (select /*+ no_parallel */ value(x) val from table(self.output_buffer.get_lines(a_initial_timeout, a_timeout_sec)) x ) loop pipe row (i.val); end loop; end; @@ -87,6 +80,7 @@ create or replace type body ut_output_reporter_base is overriding member procedure on_initialize(self in out nocopy ut_output_reporter_base, a_run in ut_run) is begin + self.output_buffer.lock_buffer(); self.output_buffer.send_line(null, 'initialize'); end; diff --git a/source/core/types/ut_output_reporter_base.tps b/source/core/types/ut_output_reporter_base.tps index 01350208a..b9ec4f7cd 100644 --- a/source/core/types/ut_output_reporter_base.tps +++ b/source/core/types/ut_output_reporter_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_output_reporter_base under ut_reporter_base( +create or replace noneditionable type ut_output_reporter_base under ut_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,8 +20,7 @@ create or replace type ut_output_reporter_base under ut_reporter_base( member procedure init(self in out nocopy ut_output_reporter_base, a_self_type varchar2, a_output_buffer ut_output_buffer_base := null), overriding member procedure set_reporter_id(self in out nocopy ut_output_reporter_base, a_reporter_id raw), member function set_reporter_id(self in ut_output_reporter_base, a_reporter_id raw) return ut_output_reporter_base, - overriding member procedure before_calling_run(self in out nocopy ut_output_reporter_base, a_run in ut_run), - + member procedure print_text(self in out nocopy ut_output_reporter_base, a_text varchar2, a_item_type varchar2 := null), member procedure print_text_lines(self in out nocopy ut_output_reporter_base, a_text_lines ut_varchar2_rows, a_item_type varchar2 := null), member procedure print_clob(self in out nocopy ut_output_reporter_base, a_clob clob, a_item_type varchar2 := null), diff --git a/source/core/types/ut_path_item.tpb b/source/core/types/ut_path_item.tpb new file mode 100644 index 000000000..486fde865 --- /dev/null +++ b/source/core/types/ut_path_item.tpb @@ -0,0 +1,42 @@ +create or replace noneditionable type body ut_path_item as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2) return self as result is + begin + self.schema_name := schema_name; + self.object_name := object_name; + self.procedure_name := procedure_name; + return; + end; + + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2,suite_path varchar2) return self as result is + begin + self.schema_name := schema_name; + self.suite_path := suite_path; + return; + end; + + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2,suite_path varchar2) return self as result is + begin + self.schema_name := schema_name; + self.object_name := object_name; + self.procedure_name := procedure_name; + self.suite_path := suite_path; + return; + end; +end; +/ diff --git a/source/core/types/ut_path_item.tps b/source/core/types/ut_path_item.tps new file mode 100644 index 000000000..fdc1e8ac2 --- /dev/null +++ b/source/core/types/ut_path_item.tps @@ -0,0 +1,26 @@ +create or replace noneditionable type ut_path_item as object ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + schema_name varchar2(4000), + object_name varchar2(250), + procedure_name varchar2(250), + suite_path varchar2(4000), + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2) return self as result, + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, suite_path varchar2) return self as result, + constructor function ut_path_item(self in out nocopy ut_path_item, schema_name varchar2, object_name varchar2,procedure_name varchar2,suite_path varchar2) return self as result +) +/ diff --git a/source/core/types/ut_path_items.tps b/source/core/types/ut_path_items.tps new file mode 100644 index 000000000..64f8dd5fe --- /dev/null +++ b/source/core/types/ut_path_items.tps @@ -0,0 +1,19 @@ +create or replace noneditionable type ut_path_items as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + table of ut_path_item +/ diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb index 94d518af4..e457645f4 100644 --- a/source/core/types/ut_reporter_base.tpb +++ b/source/core/types/ut_reporter_base.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_reporter_base is +create or replace noneditionable type body ut_reporter_base is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -165,52 +165,54 @@ create or replace type body ut_reporter_base is end; overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is + /* Resolves issue with ORA-21779 being thrown see issue: 1309 -> ( https://github.com/utPLSQL/utPLSQL/issues/1309#issuecomment-4020289898 ) */ + l_event_item ut_event_item := a_event_item; begin case a_event_name when ut_event_manager.gc_initialize - then self.on_initialize(treat(a_event_item as ut_run)); + then self.on_initialize(treat(l_event_item as ut_run)); when ut_event_manager.gc_before_run - then self.before_calling_run(treat(a_event_item as ut_run)); + then self.before_calling_run(treat(l_event_item as ut_run)); when ut_event_manager.gc_before_suite - then self.before_calling_suite(treat(a_event_item as ut_logical_suite)); + then self.before_calling_suite(treat(l_event_item as ut_logical_suite)); when ut_event_manager.gc_before_before_all - then self.before_calling_before_all(treat(a_event_item as ut_executable)); + then self.before_calling_before_all(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_before_each - then self.before_calling_before_each(treat(a_event_item as ut_executable)); + then self.before_calling_before_each(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_test - then self.before_calling_test(treat(a_event_item as ut_test)); + then self.before_calling_test(treat(l_event_item as ut_test)); when ut_event_manager.gc_before_before_test - then self.before_calling_before_test(treat(a_event_item as ut_executable)); + then self.before_calling_before_test(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_test_execute - then self.before_calling_test_execute(treat(a_event_item as ut_executable)); + then self.before_calling_test_execute(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_after_test - then self.before_calling_after_test(treat(a_event_item as ut_executable)); + then self.before_calling_after_test(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_after_each - then self.before_calling_after_each(treat(a_event_item as ut_executable)); + then self.before_calling_after_each(treat(l_event_item as ut_executable)); when ut_event_manager.gc_before_after_all - then self.before_calling_after_all(treat(a_event_item as ut_executable)); + then self.before_calling_after_all(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_run - then self.after_calling_run(treat(a_event_item as ut_run)); + then self.after_calling_run(treat(l_event_item as ut_run)); when ut_event_manager.gc_after_suite - then self.after_calling_suite(treat(a_event_item as ut_logical_suite)); + then self.after_calling_suite(treat(l_event_item as ut_logical_suite)); when ut_event_manager.gc_after_before_all - then self.after_calling_before_all(treat(a_event_item as ut_executable)); + then self.after_calling_before_all(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_before_each - then self.after_calling_before_each(treat(a_event_item as ut_executable)); + then self.after_calling_before_each(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_test - then self.after_calling_test(treat(a_event_item as ut_test)); + then self.after_calling_test(treat(l_event_item as ut_test)); when ut_event_manager.gc_after_before_test - then self.after_calling_before_test(treat(a_event_item as ut_executable)); + then self.after_calling_before_test(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_test_execute - then self.after_calling_test_execute(treat(a_event_item as ut_executable)); + then self.after_calling_test_execute(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_after_test - then self.after_calling_after_test(treat(a_event_item as ut_executable)); + then self.after_calling_after_test(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_after_each - then self.after_calling_after_each(treat(a_event_item as ut_executable)); + then self.after_calling_after_each(treat(l_event_item as ut_executable)); when ut_event_manager.gc_after_after_all - then self.after_calling_after_all(treat(a_event_item as ut_executable)); + then self.after_calling_after_all(treat(l_event_item as ut_executable)); when ut_event_manager.gc_finalize - then self.on_finalize(treat(a_event_item as ut_run)); + then self.on_finalize(treat(l_event_item as ut_run)); else null; end case; end; diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps index 36af70776..af0472dc5 100644 --- a/source/core/types/ut_reporter_base.tps +++ b/source/core/types/ut_reporter_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_reporter_base under ut_event_listener ( +create or replace noneditionable type ut_reporter_base under ut_event_listener ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_reporter_info.tps b/source/core/types/ut_reporter_info.tps index d1f159211..29c7559f1 100644 --- a/source/core/types/ut_reporter_info.tps +++ b/source/core/types/ut_reporter_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_reporter_info as object ( +create or replace noneditionable type ut_reporter_info as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_reporters.tps b/source/core/types/ut_reporters.tps index b16e74653..fba22480f 100644 --- a/source/core/types/ut_reporters.tps +++ b/source/core/types/ut_reporters.tps @@ -1,7 +1,7 @@ -create or replace type ut_reporters as +create or replace noneditionable type ut_reporters as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_reporters_info.tps b/source/core/types/ut_reporters_info.tps index 6e9a866ef..4f6f054a3 100644 --- a/source/core/types/ut_reporters_info.tps +++ b/source/core/types/ut_reporters_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_reporters_info as +create or replace noneditionable type ut_reporters_info as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_results_counter.tpb b/source/core/types/ut_results_counter.tpb index 0007acd9b..54f9e0b48 100644 --- a/source/core/types/ut_results_counter.tpb +++ b/source/core/types/ut_results_counter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_results_counter as +create or replace noneditionable type body ut_results_counter as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_results_counter.tps b/source/core/types/ut_results_counter.tps index 1b26b673a..477d4b217 100644 --- a/source/core/types/ut_results_counter.tps +++ b/source/core/types/ut_results_counter.tps @@ -1,7 +1,7 @@ -create or replace type ut_results_counter as object( +create or replace noneditionable type ut_results_counter as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index 395b164f0..80621b5ad 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_run as +create or replace noneditionable type body ut_run as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ create or replace type body ut_run as a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, a_random_test_order_seed positive := null, - a_run_tags ut_varchar2_rows := null + a_run_tags varchar2 := null ) return self as result is begin self.run_paths := a_run_paths; @@ -39,7 +39,7 @@ create or replace type body ut_run as return; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_run) is + overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2) is begin null; end; diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index 617347b25..551e56ac4 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -1,7 +1,7 @@ -create or replace type ut_run under ut_suite_item ( +create or replace noneditionable type ut_run under ut_suite_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ create or replace type ut_run under ut_suite_item ( project_name varchar2(4000), items ut_suite_items, run_paths ut_varchar2_list, - run_tags ut_varchar2_rows, + run_tags varchar2(4000), coverage_options ut_coverage_options, test_file_mappings ut_file_mappings, client_character_set varchar2(100), @@ -34,9 +34,9 @@ create or replace type ut_run under ut_suite_item ( a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, a_random_test_order_seed positive := null, - a_run_tags ut_varchar2_rows := null + a_run_tags varchar2 := null ) return self as result, - overriding member procedure mark_as_skipped(self in out nocopy ut_run), + overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2), overriding member function do_execute(self in out nocopy ut_run) return boolean, overriding member procedure set_rollback_type(self in out nocopy ut_run, a_rollback_type integer, a_force boolean := false), overriding member procedure calc_execution_result(self in out nocopy ut_run), diff --git a/source/core/types/ut_run_info.tpb b/source/core/types/ut_run_info.tpb index 8e06d6137..3de74b2b4 100644 --- a/source/core/types/ut_run_info.tpb +++ b/source/core/types/ut_run_info.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_run_info as +create or replace noneditionable type body ut_run_info as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,27 +20,27 @@ create or replace type body ut_run_info as begin self.self_type := $$plsql_unit; execute immediate - 'select '||l_ut_owner||'.ut.version() from dual' + 'select /*+ no_parallel */ '||l_ut_owner||'.ut.version() from dual' into self.ut_version; dbms_utility.db_version( self.db_version, self.db_compatibility ); db_os_type := dbms_utility.port_string(); execute immediate - 'select '||l_ut_owner||'.ut_key_value_pair(x.product, x.version) from product_component_version x' + 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.product, x.version) from product_component_version x' bulk collect into self.db_component_version; execute immediate - 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) + 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_session_parameters x' bulk collect into self.nls_session_params; execute immediate - 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_instance_parameters x' + 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_instance_parameters x' bulk collect into self.nls_instance_params; execute immediate - 'select '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_database_parameters x' + 'select /*+ no_parallel */ '||l_ut_owner||'.ut_key_value_pair(x.parameter, x.value) from nls_database_parameters x' bulk collect into self.nls_db_params; return; end; diff --git a/source/core/types/ut_run_info.tps b/source/core/types/ut_run_info.tps index f59c22c05..00919b172 100644 --- a/source/core/types/ut_run_info.tps +++ b/source/core/types/ut_run_info.tps @@ -1,7 +1,7 @@ -create or replace type ut_run_info under ut_event_item ( +create or replace noneditionable type ut_run_info under ut_event_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_stack.tpb b/source/core/types/ut_stack.tpb new file mode 100644 index 000000000..4298ae35c --- /dev/null +++ b/source/core/types/ut_stack.tpb @@ -0,0 +1,58 @@ +create or replace noneditionable type body ut_stack as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_stack( self in out nocopy ut_stack) return self as result is + begin + self.tokens := ut_varchar2_list(); + self.top := 0; + return; + end ut_stack; + + member function peek(self in out nocopy ut_stack) return varchar2 is + l_token varchar2(32767); + begin + if self.tokens.count =0 or self.tokens is null then + l_token := null; + else + l_token := self.tokens(self.tokens.last); + end if; + return l_token; + end; + + member procedure push(self in out nocopy ut_stack, a_token varchar2) is + begin + self.tokens.extend; + self.tokens(self.tokens.last) := a_token; + self.top := self.tokens.count; + end push; + + member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1) is + begin + self.tokens.trim(a_cnt); + self.top := self.tokens.count; + end pop; + + member function pop(self in out nocopy ut_stack) return varchar2 is + l_token varchar2(32767) := self.tokens(self.tokens.last); + begin + self.pop(); + return l_token; + end; +end; +/ + diff --git a/source/core/types/ut_stack.tps b/source/core/types/ut_stack.tps new file mode 100644 index 000000000..ed79bea55 --- /dev/null +++ b/source/core/types/ut_stack.tps @@ -0,0 +1,26 @@ +create or replace noneditionable type ut_stack as object ( + top integer, + tokens ut_varchar2_list, + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + constructor function ut_stack( self in out nocopy ut_stack) return self as result, + member function peek(self in out nocopy ut_stack) return varchar2, + member procedure push(self in out nocopy ut_stack, a_token varchar2), + member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1), + member function pop(self in out nocopy ut_stack) return varchar2 +) +/ \ No newline at end of file diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index baaaf044d..b4e3eabf0 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_suite as +create or replace noneditionable type body ut_suite as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ create or replace type body ut_suite as ut_utils.debug_log('ut_suite.execute'); if self.get_disabled_flag() then - self.mark_as_skipped(); + self.mark_as_skipped(a_skip_reason => self.disabled_reason); else self.start_time := current_timestamp; ut_event_manager.trigger_event(ut_event_manager.gc_before_suite, self); diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index ffe2d2d42..edce778f1 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite under ut_logical_suite ( +create or replace noneditionable type ut_suite under ut_logical_suite ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_suite_cache_row.tps b/source/core/types/ut_suite_cache_row.tps index d9f542de3..1ee371a88 100644 --- a/source/core/types/ut_suite_cache_row.tps +++ b/source/core/types/ut_suite_cache_row.tps @@ -1,7 +1,7 @@ -create type ut_suite_cache_row as object ( +create or replace noneditionable type ut_suite_cache_row as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ create type ut_suite_cache_row as object ( description varchar2(4000 byte), rollback_type number, disabled_flag number, + disabled_reason varchar2(4000 byte), warnings ut_varchar2_rows, before_all_list ut_executables, after_all_list ut_executables, diff --git a/source/core/types/ut_suite_cache_rows.tps b/source/core/types/ut_suite_cache_rows.tps index 660f934cf..84bcc9f75 100644 --- a/source/core/types/ut_suite_cache_rows.tps +++ b/source/core/types/ut_suite_cache_rows.tps @@ -1,7 +1,7 @@ -create type ut_suite_cache_rows as +create or replace noneditionable type ut_suite_cache_rows as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_suite_context.tpb b/source/core/types/ut_suite_context.tpb index 3669d3d7e..dad24e42e 100644 --- a/source/core/types/ut_suite_context.tpb +++ b/source/core/types/ut_suite_context.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_suite_context as +create or replace noneditionable type body ut_suite_context as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_suite_context.tps b/source/core/types/ut_suite_context.tps index a328e7657..b890e2633 100644 --- a/source/core/types/ut_suite_context.tps +++ b/source/core/types/ut_suite_context.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite_context under ut_suite ( +create or replace noneditionable type ut_suite_context under ut_suite ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index 9b939727a..8e8c6fd90 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_suite_item as +create or replace noneditionable type body ut_suite_item as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ create or replace type body ut_suite_item as self.line_no := a_line_no; self.transaction_invalidators := ut_varchar2_list(); self.disabled_flag := ut_utils.boolean_to_int(false); + self.disabled_reason := null; end; member function get_disabled_flag return boolean is diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 87f577459..2e990353f 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite_item force under ut_event_item ( +create or replace noneditionable type ut_suite_item force under ut_event_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -46,6 +46,10 @@ create or replace type ut_suite_item force under ut_event_item ( */ disabled_flag integer(1), /** + * Indicates reason whysa test is to be disabled by execution + */ + disabled_reason varchar2(4000), + /** * Line no where annotation identifying this item is placed in package */ line_no integer, @@ -66,7 +70,7 @@ create or replace type ut_suite_item force under ut_event_item ( tags ut_varchar2_rows, member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_line_no integer), member function get_disabled_flag return boolean, - not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), + not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item, a_skip_reason in varchar2), member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer, a_force boolean := false), member function get_rollback_type return integer, member function create_savepoint_if_needed return varchar2, diff --git a/source/core/types/ut_suite_items.tps b/source/core/types/ut_suite_items.tps index e80bcc7b4..b7ce9873c 100644 --- a/source/core/types/ut_suite_items.tps +++ b/source/core/types/ut_suite_items.tps @@ -1,7 +1,7 @@ -create or replace type ut_suite_items as +create or replace noneditionable type ut_suite_items as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 4ed3f665e..61a1e39f8 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_test as +create or replace noneditionable type body ut_test as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -35,11 +35,12 @@ create or replace type body ut_test as return; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_test) is + overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_skip_reason in varchar2) is begin ut_event_manager.trigger_event(ut_event_manager.gc_before_test, self); self.start_time := current_timestamp; self.result := ut_utils.gc_disabled; + self.disabled_reason := coalesce(a_skip_reason,self.disabled_reason); ut_utils.debug_log('ut_test.execute - disabled'); self.results_count.set_counter_values(self.result); self.end_time := self.start_time; @@ -54,7 +55,7 @@ create or replace type body ut_test as ut_utils.debug_log('ut_test.execute'); if self.get_disabled_flag() then - mark_as_skipped(); + mark_as_skipped(self.disabled_reason); else self.start_time := current_timestamp; ut_event_manager.trigger_event(ut_event_manager.gc_before_test, self); diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index 85caf7380..cb50c2fd4 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -1,7 +1,7 @@ -create or replace type ut_test force under ut_suite_item ( +create or replace noneditionable type ut_test force under ut_suite_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ create or replace type ut_test force under ut_suite_item ( 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_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 procedure mark_as_skipped(self in out nocopy ut_test, a_skip_reason in varchar2), overriding member function do_execute(self in out nocopy ut_test) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_test), overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2), diff --git a/source/core/types/ut_varchar2_list.tps b/source/core/types/ut_varchar2_list.tps index dd15f0d8f..8203a4bab 100644 --- a/source/core/types/ut_varchar2_list.tps +++ b/source/core/types/ut_varchar2_list.tps @@ -1,7 +1,7 @@ -create or replace type ut_varchar2_list as +create or replace noneditionable type ut_varchar2_list as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/types/ut_varchar2_rows.tps b/source/core/types/ut_varchar2_rows.tps index d56eeee46..a2e25e9fb 100644 --- a/source/core/types/ut_varchar2_rows.tps +++ b/source/core/types/ut_varchar2_rows.tps @@ -1,7 +1,7 @@ -create or replace type ut_varchar2_rows as +create or replace noneditionable type ut_varchar2_rows as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_dbms_output_cache.sql b/source/core/ut_dbms_output_cache.sql index 1fe4251c7..802f30942 100644 --- a/source/core/ut_dbms_output_cache.sql +++ b/source/core/ut_dbms_output_cache.sql @@ -1,15 +1,16 @@ /* -utPLSQL - Version 3 -Copyright 2016 - 2019 utPLSQL Project -Licensed under the Apache License, Version 2.0 (the "License"): -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ /* * This table is not a global temporary table as it needs to allow cross-session data exchange @@ -19,7 +20,7 @@ limitations under the License. declare l_tab_exist number; begin - select count(*) into l_tab_exist from + select /*+ no_parallel */ count(*) into l_tab_exist from (select table_name from all_tables where table_name = 'UT_DBMS_OUTPUT_CACHE' and owner = sys_context('USERENV','CURRENT_SCHEMA') union all select synonym_name from all_synonyms where synonym_name = 'UT_DBMS_OUTPUT_CACHE' and owner = sys_context('USERENV','CURRENT_SCHEMA')); diff --git a/source/core/ut_expectation_processor.pkb b/source/core/ut_expectation_processor.pkb index d65d210c8..3fb39a5b9 100644 --- a/source/core/ut_expectation_processor.pkb +++ b/source/core/ut_expectation_processor.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_expectation_processor as +create or replace noneditionable package body ut_expectation_processor as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -104,7 +104,7 @@ create or replace package body ut_expectation_processor as function get_session_parameters return tt_nls_params is l_session_params tt_nls_params; begin - select nsp.parameter, nsp.value + select /*+ no_parallel */ nsp.parameter, nsp.value bulk collect into l_session_params from nls_session_parameters nsp where parameter @@ -159,17 +159,17 @@ create or replace package body ut_expectation_processor as -- when 11g and 12c reports only package name function cut_header_and_expectations( a_stack varchar2 ) return varchar2 is begin - return regexp_substr( a_stack, '(.*\.(UT_EXPECTATION[A-Z0-9#_$]*|UT|UTASSERT2?)(\.[A-Z0-9#_$]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4); + return regexp_substr( a_stack, '(.*\.(UT_EQUAL|UT_BE_WITHIN[[:alnum:]$#_]*|UT_EXPECTATION[[:alnum:]$#_]*|UT|UTASSERT2?)(\.[[:alnum:]$#_]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4); end; function cut_address_columns( a_stack varchar2 ) return varchar2 is begin - return regexp_replace( a_stack, '^(0x)?[0-9a-f]+\s+', '', 1, 0, 'mi' ); + return regexp_replace( a_stack, '^(0x)?[[:digit:]abcdef]+\s+', '', 1, 0, 'mi' ); end; function cut_framework_stack( a_stack varchar2 ) return varchar2 is begin return regexp_replace( a_stack, - '[0-9]+\s+anonymous\s+block\s+[0-9]+\s+package\s+body\s+sys\.dbms_sql(\.execute)?\s+[0-9]+\s+[0-9_$#a-z ]+\.ut_executable.*', + '[0-9]+\s+anonymous\s+block\s+[0-9]+\s+package\s+body\s+sys\.dbms_sql(\.execute)?\s+[0-9]+\s+[[:alnum:]_$# ]+\.ut_executable.*', '', 1, 1, 'mni' ); @@ -178,7 +178,7 @@ create or replace package body ut_expectation_processor as begin return regexp_replace( a_stack, - '([0-9]+)\s+(.* )?((anonymous block)|(([0-9_$#a-z]+\.[0-9_$#a-z]+(\.([0-9_$#a-z])+)?)))', + '([0-9]+)\s+(.* )?((anonymous block)|(([[:alnum:]$#_]+\.[[:alnum:]$#_]+(\.([[:alnum:]$#_])+)?)))', 'at "\3", line \1', 1, 0, 'i' ); end; @@ -190,8 +190,8 @@ create or replace package body ut_expectation_processor as l_caller_stack_line := regexp_substr(l_call_stack,'^(.*)'); if l_caller_stack_line like '%.%' then l_line_no := to_number( regexp_substr( l_caller_stack_line, ', line (\d+)', subexpression => 1 ) ); - l_owner := regexp_substr( l_caller_stack_line, 'at "([A-Za-z0-9$#_]+)\.(([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?)", line (\d+)', subexpression => 1 ); - l_object_name := regexp_substr( l_caller_stack_line, 'at "([A-Za-z0-9$#_]+)\.(([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?)", line (\d+)', subexpression => 3 ); + l_owner := regexp_substr( l_caller_stack_line, 'at "([[:alnum:]$#_]+)\.(([[:alnum:]$#_]+)(\.([[:alnum:]$#_]+))?)", line (\d+)', subexpression => 1 ); + l_object_name := regexp_substr( l_caller_stack_line, 'at "([[:alnum:]$#_]+)\.(([[:alnum:]$#_]+)(\.([[:alnum:]$#_]+))?)", line (\d+)', subexpression => 3 ); l_result := l_caller_stack_line || ' ' || rtrim(ut_metadata.get_source_definition_line(l_owner, l_object_name, l_line_no),chr(10)) || replace( l_call_stack, l_caller_stack_line ); diff --git a/source/core/ut_expectation_processor.pks b/source/core/ut_expectation_processor.pks index cfcae8b42..5349ee70a 100644 --- a/source/core/ut_expectation_processor.pks +++ b/source/core/ut_expectation_processor.pks @@ -1,7 +1,7 @@ -create or replace package ut_expectation_processor authid current_user as +create or replace noneditionable package ut_expectation_processor authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_file_mapper.pkb b/source/core/ut_file_mapper.pkb index adecb7dc8..40204a5da 100644 --- a/source/core/ut_file_mapper.pkb +++ b/source/core/ut_file_mapper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_file_mapper is +create or replace noneditionable package body ut_file_mapper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_file_mapper.pks b/source/core/ut_file_mapper.pks index 4ed670ee5..1363e81ec 100644 --- a/source/core/ut_file_mapper.pks +++ b/source/core/ut_file_mapper.pks @@ -1,7 +1,7 @@ -create or replace package ut_file_mapper authid current_user is +create or replace noneditionable package ut_file_mapper authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_metadata.pkb b/source/core/ut_metadata.pkb index 727dd6318..d9939ad45 100644 --- a/source/core/ut_metadata.pkb +++ b/source/core/ut_metadata.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_metadata as +create or replace noneditionable package body ut_metadata as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ create or replace package body ut_metadata as l_view_name varchar2(200) := get_objects_view_name; begin - execute immediate q'[select count(*) + execute immediate q'[select /*+ no_parallel */ count(*) from ]'||l_view_name||q'[ where owner = :a_owner_name and object_name = :a_package_name @@ -58,7 +58,7 @@ create or replace package body ut_metadata as l_view_name varchar2(200) := get_dba_view('dba_procedures'); begin execute immediate - 'select count(*) from '||l_view_name + 'select /*+ no_parallel */ count(*) from '||l_view_name ||' where owner = :l_schema and object_name = :l_package_name and procedure_name = :l_procedure_name and rownum = 1' into l_cnt using a_owner_name, a_package_name, a_procedure_name; @@ -77,7 +77,7 @@ create or replace package body ut_metadata as if not nvl(c_key = g_cached_object, false) then g_cached_object := c_key; execute immediate - 'select trim(text) text + 'select /*+ no_parallel */ trim(text) text from '||l_view_name||q'[ s where s.owner = :a_owner and s.name = :a_object_name @@ -123,7 +123,7 @@ create or replace package body ut_metadata as function user_has_execute_any_proc return boolean is l_has_execute_any varchar2(1); begin - select decode( count( 1 ), 0, 'N', 'Y' ) + select /*+ no_parallel */ decode( count( 1 ), 0, 'N', 'Y' ) into l_has_execute_any from dual where @@ -157,7 +157,7 @@ create or replace package body ut_metadata as l_cnt number; c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA'); begin - select count(*) + select /*+ no_parallel */ count(*) into l_cnt from all_objects t where t.object_name = a_object_name @@ -304,7 +304,7 @@ create or replace package body ut_metadata as begin l_result := regexp_substr( a_full_object_name, - '^([A-Za-z0-9$#_]+|".*?")\.([A-Za-z0-9$#_]+|".*?")', subexpression => 2 + '^([[:alnum:]$#_]+|".*?")\.([[:alnum:]$#_]+|".*?")', subexpression => 2 ); if not l_result like '"%"' then l_result := upper(l_result); diff --git a/source/core/ut_metadata.pks b/source/core/ut_metadata.pks index bf62c4e52..42f136504 100644 --- a/source/core/ut_metadata.pks +++ b/source/core/ut_metadata.pks @@ -1,7 +1,7 @@ -create or replace package ut_metadata authid current_user as +create or replace noneditionable package ut_metadata authid current_user as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_savepoint_seq.sql b/source/core/ut_savepoint_seq.sql index b87d18e6d..2026ab0e3 100644 --- a/source/core/ut_savepoint_seq.sql +++ b/source/core/ut_savepoint_seq.sql @@ -1,7 +1,7 @@ create sequence ut_savepoint_seq /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index d13cbaed9..4f0e28606 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_suite_builder is +create or replace noneditionable package body ut_suite_builder is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -355,6 +355,7 @@ create or replace package body ut_suite_builder is l_annotation_texts tt_annotation_texts; l_proc_annotations tt_annotations_by_name := a_annotations.by_proc(a_procedure_name); begin + if not l_proc_annotations.exists(gc_test) then return; end if; @@ -412,6 +413,12 @@ create or replace package body ut_suite_builder is end if; l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled)); + if l_proc_annotations.exists(gc_disabled) then + l_annotation_texts := l_proc_annotations( gc_disabled); + --take the last definition if more than one was provided + l_test.disabled_reason := l_annotation_texts(l_annotation_texts.first); + end if; + a_suite_items.extend; a_suite_items( a_suite_items.last ) := l_test; @@ -560,8 +567,12 @@ create or replace package body ut_suite_builder is 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; + a_suite.disabled_flag := ut_utils.boolean_to_int(a_annotations.by_name.exists(gc_disabled)); - + if a_annotations.by_name.exists(gc_disabled) then + a_suite.disabled_reason := a_annotations.by_name(gc_disabled)(a_annotations.by_name(gc_disabled).first); + end if; + --process procedure annotations for suite get_annotated_procedures(a_annotations, a_suite, a_suite_items, l_before_each_list, l_after_each_list, l_before_all_list, l_after_all_list); @@ -743,7 +754,7 @@ create or replace package body ut_suite_builder is l_used_context_names(l_context_name) := true; l_context := ut_suite_context(a_parent.object_owner, a_parent.object_name, l_context_name, l_context_pos ); - l_context.path := a_parent.path||'.'||l_context_name; + l_context.path := a_parent.path||'.'||l_context.name; l_context.description := coalesce( a_annotations.by_line( l_context_pos ).text, l_context_name ); l_context.parse_time := a_annotations.parse_time; diff --git a/source/core/ut_suite_builder.pks b/source/core/ut_suite_builder.pks index e982fdbc1..35f207ea4 100644 --- a/source/core/ut_suite_builder.pks +++ b/source/core/ut_suite_builder.pks @@ -1,7 +1,7 @@ -create or replace package ut_suite_builder authid current_user is +create or replace noneditionable package ut_suite_builder authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/core/ut_suite_cache.sql b/source/core/ut_suite_cache.sql index a7f998f83..1d7389e9f 100644 --- a/source/core/ut_suite_cache.sql +++ b/source/core/ut_suite_cache.sql @@ -1,7 +1,7 @@ create table ut_suite_cache /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb index 06c4c76a5..9aa3cf63d 100644 --- a/source/core/ut_suite_cache_manager.pkb +++ b/source/core/ut_suite_cache_manager.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_suite_cache_manager is +create or replace noneditionable package body ut_suite_cache_manager is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -19,26 +19,16 @@ create or replace package body ut_suite_cache_manager is /* * Private code */ - - gc_get_cache_suite_sql constant varchar2(32767) := - q'[with + cursor c_get_bulk_cache_suite(cp_suite_items in ut_suite_cache_rows) is + with suite_items as ( select /*+ cardinality(c 500) */ value(c) as obj - from ut_suite_cache c - where 1 = 1 - and c.object_owner = :l_object_owner - and ( {:path:} - and {:object_name:} - and {:procedure_name:} - ) - ) - ), - {:tags:} + from table(cp_suite_items) c), suitepaths as ( select distinct substr(c.obj.path,1,instr(c.obj.path,'.',-1)-1) as suitepath, c.obj.path as path, c.obj.object_owner as object_owner - from {:suite_item_name:} c + from suite_items c where c.obj.self_type = 'UT_SUITE' ), gen as ( @@ -67,21 +57,16 @@ create or replace package body ut_suite_cache_manager is select ut_suite_cache_row( null, s.self_type, s.path, s.object_owner, s.object_name, - s.object_name, null, null, null, null, 0, + s.object_name, null, null, null, null, 0,null, ut_varchar2_rows(), s.x, s.x, s.x, s.x, s.x, s.x, s.y, null, s.z ) as obj from logical_suite_data s - ), - items as ( - select obj from {:suite_item_name:} - union all - select obj from logical_suites ) - select c.obj - from items c - order by c.obj.object_owner,{:random_seed:}]'; + select /*+ no_parallel */ obj from suite_items + union all + select /*+ no_parallel */ obj from logical_suites; function get_missing_cache_objects(a_object_owner varchar2) return ut_varchar2_rows is l_result ut_varchar2_rows; @@ -89,7 +74,7 @@ create or replace package body ut_suite_cache_manager is begin l_data := ut_annotation_cache_manager.get_cached_objects_list(a_object_owner, 'PACKAGE'); - select i.object_name + select /*+ no_parallel */ i.object_name bulk collect into l_result from ut_suite_cache_package i where not exists ( @@ -102,163 +87,208 @@ create or replace package body ut_suite_cache_manager is return l_result; end; - - function get_path_sql(a_path in varchar2) return varchar2 is + function group_paths_by_schema(a_paths ut_varchar2_list) return ut_path_items is + c_package_path_regex constant varchar2(100) := '^([[:alnum:]$#_]+)(\.([[:alnum:]$#_\*]+))?(\.([[:alnum:]$#_\*]+))?$'; + l_results ut_path_items := ut_path_items(); + l_path_item ut_path_item; + i pls_integer; begin - return case when a_path is not null then q'[ - :l_path||'.' like c.path || '.%' /*all parents and self*/ - or ( c.path||'.' like :l_path || '.%' /*all children and self*/ - ]' - else ' :l_path is null and ( :l_path is null ' end; - end; - - function get_object_name_sql(a_object_name in varchar2) return varchar2 is - begin - return case when a_object_name is not null - then ' c.object_name = :a_object_name ' - else ' :a_object_name is null' end; + i := a_paths.first; + while (i is not null) loop + l_results.extend; + if a_paths(i) like '%:%' then + l_path_item := ut_path_item(schema_name => upper(regexp_substr(a_paths(i),'^[^.:]+')), + suite_path => ltrim(regexp_substr(a_paths(i),'[.:].*$'),':')); + l_results(l_results.last) := l_path_item; + else + l_path_item := ut_path_item(schema_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 1), + object_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 3), + procedure_name => regexp_substr(a_paths(i), c_package_path_regex, subexpression => 5)); + l_results(l_results.last) := l_path_item; + end if; + i := a_paths.next(i); + end loop; + + return l_results; end; - - function get_procedure_name_sql(a_procedure_name in varchar2) return varchar2 is - begin - return case when a_procedure_name is not null - then ' c.name = :a_procedure_name' - else ' :a_procedure_name is null' end; + + /* + First SQL queries for objects where procedure is null or its only wildcard. + We split that due to fact that we can use func min to combine rows. + + Second union is responsible expanding paths where the procedure filter is given + We cannot select min here as filter can cover only half of tests within + package. Even if the filter doesnt return anything we still capture a proc filter + name for error reporting later on. + + Third SQL cover scenario where a suitapath only is populated and wildcard is given + + Fourth SQL cover scenario where suitepath is populated with no filters + */ + function expand_paths(a_schema_paths ut_path_items) return ut_path_items is + l_schema_paths ut_path_items:= ut_path_items(); + begin + with + schema_paths as ( + select * from table(a_schema_paths) + ), + paths_for_object as ( + select /*+ no_parallel */ min(path) as suite_path,sp.schema_name as schema_name,nvl(c.object_name,sp.object_name) as object_name, + null as procedure_name + from schema_paths sp left outer join ut_suite_cache c + on ( c.object_owner = upper(sp.schema_name) + and c.object_name like replace(upper(sp.object_name),'*','%')) + where sp.suite_path is null and sp.object_name is not null + and ( sp.procedure_name is null or sp.procedure_name = '*') + group by sp.schema_name,nvl(c.object_name,sp.object_name) + ), + paths_for_procedures as ( + select /*+ no_parallel */ path as suite_path,sp.schema_name as schema_name,nvl(c.object_name,sp.object_name) as object_name, + nvl(c.name,sp.procedure_name) as procedure_name + from schema_paths sp left outer join ut_suite_cache c + on ( c.object_owner = upper(sp.schema_name) + and c.object_name like replace(upper(sp.object_name),'*','%') + and c.name like nvl(replace(upper(sp.procedure_name),'*','%'), c.name)) + where sp.suite_path is null and sp.object_name is not null + and (sp.procedure_name is not null and sp.procedure_name != '*') + ), + paths_for_suite_path_with_ast as ( + select /*+ no_parallel */ nvl(c.path,sp.suite_path) as suite_path,sp.schema_name,sp.object_name,sp.procedure_name as procedure_name + from schema_paths sp left outer join ut_suite_cache c on + ( c.object_owner = upper(sp.schema_name) + --and c.path like replace(sp.suite_path,'*','%')) + and regexp_like(c.path,'^'||replace(sp.suite_path,'*','[[:alnum:]$#_]*'))) + where sp.suite_path is not null and instr(sp.suite_path,'*') > 0 + ), + straigth_suite_paths as ( + select /*+ no_parallel */ sp.suite_path as suite_path,sp.schema_name,sp.object_name,sp.procedure_name as procedure_name + from schema_paths sp + where + (sp.suite_path is not null and instr(sp.suite_path,'*') = 0) + or + (sp.suite_path is null and sp.object_name is null) + ), + all_suitepaths_together as ( + select * from paths_for_object + union all + select * from paths_for_procedures + union all + select * from paths_for_suite_path_with_ast + union all + select * from straigth_suite_paths + ) + select ut_path_item(schema_name,object_name,procedure_name,suite_path) + bulk collect into l_schema_paths + from + (select schema_name,object_name,procedure_name,suite_path, + row_number() over ( partition by schema_name,object_name,procedure_name,suite_path order by 1) as r_num + from all_suitepaths_together) + where r_num = 1 ; + return l_schema_paths; end; - - function get_tags_sql(a_tags_count in integer) return varchar2 is + + /* + Get a suite items rows that matching our criteria like + path,object_name etc. + We need to consider also an wildcard character on our procedures and object + names. + Were the path is populated we need to make sure we dont return duplicates + as the wildcard can produce multiple results from same path and + parents and child for each can be same resulting in duplicates + */ + function get_suite_items ( + a_schema_paths ut_path_items + ) return ut_suite_cache_rows is + l_suite_items ut_suite_cache_rows := ut_suite_cache_rows(); begin - return case when a_tags_count > 0 then - q'[included_tags as ( - select c.obj.path as path - from suite_items c - where c.obj.tags multiset intersect :a_include_tag_list is not empty or :a_include_tag_list is empty - ), - excluded_tags as ( - select c.obj.path as path - from suite_items c - where c.obj.tags multiset intersect :a_exclude_tag_list is not empty - ), - suite_items_tags as ( - select c.* - from suite_items c - where exists ( - select 1 from included_tags t - where t.path||'.' like c.obj.path || '.%' /*all parents and self*/ - or c.obj.path||'.' like t.path || '.%' /*all children and self*/ - ) - and not exists ( - select 1 from excluded_tags t - where c.obj.path||'.' like t.path || '.%' /*all children and self*/ - ) - ),]' - else - q'[dummy as (select 'x' from dual where :a_include_tag_list is null and :a_include_tag_list is null and :a_exclude_tag_list is null),]' - end; + select obj bulk collect into l_suite_items + from ( + select /*+ cardinality(c 500) */ value(c) as obj,row_number() over ( partition by path,object_owner order by path,object_owner asc) as r_num + from ut_suite_cache c, + table(a_schema_paths) sp + where c.object_owner = upper(sp.schema_name) + and ((sp.suite_path is not null and sp.suite_path||'.' like c.path||'.%' /*all parents and self*/ + or + ( + c.path||'.' like sp.suite_path||'.%' /*all children and self*/ + and c.object_name like nvl(upper(sp.object_name),c.object_name) + and c.name like nvl(upper(sp.procedure_name),c.name) + )) + or + ( sp.suite_path is null + and c.object_name = nvl(upper(sp.object_name),c.object_name) + and c.name = nvl(upper(sp.procedure_name),c.name)))) where r_num =1; + return l_suite_items; end; - - function get_random_seed_sql(a_random_seed positive) return varchar2 is + + /* + We will sort a suites in hierarchical structure. + Sorting from bottom to top so when we consolidate + we will go in proper order. + For random seed we will add an extra sort that can be null. + The object owner is irrelevant on joing via path as we already + resolved a list of test we want to use so as long they share a suitepath + they are correct. + */ + procedure sort_and_randomize_tests( + a_suite_rows in out ut_suite_cache_rows, + a_random_seed positive := null) + is + l_suite_rows ut_suite_cache_rows; begin - return case - when a_random_seed is null then q'[ - nlssort( - replace( - /*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 order*/ - chr(0) - ), - 'nls_sort=binary' - )desc nulls last, - 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( - c.obj.path, :a_random_seed - ) desc nulls last' - end; + with + extract_parent_child as ( + select s.path, substr(s.path,1,instr(s.path,'.',-1,1)-1) as parent_path,s.object_owner, + case when a_random_seed is null then s.line_no end line_no, + case when a_random_seed is not null then ut_utils.hash_suite_path(s.path, a_random_seed) end random_seed + from table(a_suite_rows) s), + t1(path,parent_path,object_owner,line_no,random_seed) as ( + --Anchor member + select s.path, parent_path,s.object_owner,s.line_no,random_seed + from extract_parent_child s + where parent_path is null + union all + --Recursive member + select t2.path, t2.parent_path,t2.object_owner,t2.line_no,t2.random_seed + from t1,extract_parent_child t2 + where t2.parent_path = t1.path + and t2.object_owner = t1.object_owner) + search depth first by line_no desc,random_seed desc nulls last set order1 + select value(i) as obj + bulk collect into l_suite_rows + from t1 c + join table(a_suite_rows) i on i.object_owner = c.object_owner and i.path = c.path + order by order1 desc; + + a_suite_rows := l_suite_rows; end; - - - + /* * Public code */ + + function get_schema_paths(a_paths in ut_varchar2_list) return ut_path_items is + begin + return expand_paths(group_paths_by_schema(a_paths)); + end; + function get_cached_suite_rows( - a_object_owner varchar2, - a_path varchar2 := null, - a_object_name varchar2 := null, - a_procedure_name varchar2 := null, - a_random_seed positive := null, - a_tags ut_varchar2_rows := null + a_suites_filtered ut_suite_cache_rows ) return ut_suite_cache_rows is - l_path varchar2(4000); l_results ut_suite_cache_rows := ut_suite_cache_rows(); - l_sql varchar2(32767); - l_suite_item_name varchar2(20); - l_tags ut_varchar2_rows := coalesce(a_tags,ut_varchar2_rows()); - l_include_tags ut_varchar2_rows; - l_exclude_tags ut_varchar2_rows; - l_object_owner varchar2(250) := ut_utils.qualified_sql_name(a_object_owner); - l_object_name varchar2(250) := ut_utils.qualified_sql_name(a_object_name); - l_procedure_name varchar2(250) := ut_utils.qualified_sql_name(a_procedure_name); - begin - - select column_value - bulk collect into l_include_tags - from table(l_tags) - where column_value not like '-%'; - - select ltrim(column_value,'-') - bulk collect into l_exclude_tags - from table(l_tags) - where column_value like '-%'; - - if a_path is null and a_object_name is not null then - select min(c.path) - into l_path - from ut_suite_cache c - where c.object_owner = upper(l_object_owner) - and c.object_name = upper(l_object_name) - and c.name = nvl(upper(l_procedure_name), c.name); - else - l_path := lower(ut_utils.qualified_sql_name(a_path)); - end if; - l_suite_item_name := case when l_tags.count > 0 then 'suite_items_tags' else 'suite_items' end; - - l_sql := gc_get_cache_suite_sql; - l_sql := replace(l_sql,'{:suite_item_name:}',l_suite_item_name); - l_sql := replace(l_sql,'{:object_owner:}',upper(l_object_owner)); - l_sql := replace(l_sql,'{:path:}',get_path_sql(l_path)); - l_sql := replace(l_sql,'{:object_name:}',get_object_name_sql(l_object_name)); - l_sql := replace(l_sql,'{:procedure_name:}',get_procedure_name_sql(l_procedure_name)); - l_sql := replace(l_sql,'{:tags:}',get_tags_sql(l_tags.count)); - l_sql := replace(l_sql,'{:random_seed:}',get_random_seed_sql(a_random_seed)); - - ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) ); + begin - execute immediate l_sql - bulk collect into l_results - using upper(l_object_owner), l_path, l_path, upper(a_object_name), upper(a_procedure_name), l_include_tags, l_include_tags, l_exclude_tags, a_random_seed; + open c_get_bulk_cache_suite(a_suites_filtered); + fetch c_get_bulk_cache_suite bulk collect into l_results; + close c_get_bulk_cache_suite; + return l_results; end; - + function get_schema_parse_time(a_schema_name varchar2) return timestamp result_cache is l_cache_parse_time timestamp; begin - select min(t.parse_time) + select /*+ no_parallel */ min(t.parse_time) into l_cache_parse_time from ut_suite_cache_schema t where object_owner = upper(a_schema_name); @@ -276,7 +306,7 @@ create or replace package body ut_suite_cache_manager is l_object_owner varchar2(250) := upper(a_object_owner); l_object_name varchar2(250) := upper(a_object_name); begin - if a_suite_items is not null and a_suite_items.count = 0 then + if a_suite_items is null or a_suite_items.count = 0 then delete from ut_suite_cache t where t.object_owner = l_object_owner @@ -288,7 +318,7 @@ create or replace package body ut_suite_cache_manager is else - select min(parse_time) + select /*+ no_parallel */ min(parse_time) into l_cached_parse_time from ut_suite_cache_package t where t.object_name = l_object_name @@ -296,23 +326,23 @@ create or replace package body ut_suite_cache_manager is if a_parse_time > l_cached_parse_time or l_cached_parse_time is null then - update ut_suite_cache_schema t - set t.parse_time = a_parse_time + update /*+ no_parallel */ ut_suite_cache_schema t + set t.parse_time = greatest(t.parse_time,a_parse_time) where object_owner = l_object_owner; if sql%rowcount = 0 then - insert into ut_suite_cache_schema + insert /*+ no_parallel */ into ut_suite_cache_schema (object_owner, parse_time) values (l_object_owner, a_parse_time); end if; - update ut_suite_cache_package t + update /*+ no_parallel */ ut_suite_cache_package t set t.parse_time = a_parse_time where t.object_owner = l_object_owner and t.object_name = l_object_name; if sql%rowcount = 0 then - insert into ut_suite_cache_package + insert /*+ no_parallel */ into ut_suite_cache_package (object_owner, object_name, parse_time) values (l_object_owner, l_object_name, a_parse_time ); end if; @@ -321,11 +351,11 @@ create or replace package body ut_suite_cache_manager is where t.object_owner = l_object_owner and t.object_name = l_object_name; - insert into ut_suite_cache t + insert /*+ no_parallel */ into ut_suite_cache t ( id, self_type, path, object_owner, object_name, name, line_no, parse_time, description, - rollback_type, disabled_flag, warnings, + rollback_type, disabled_flag,disabled_reason, warnings, before_all_list, after_all_list, before_each_list, after_each_list, before_test_list, after_test_list, @@ -336,10 +366,10 @@ create or replace package body ut_suite_cache_manager is select treat(value(x) as ut_suite) i from table(a_suite_items) x where x.self_type in( 'UT_SUITE', 'UT_SUITE_CONTEXT' ) ) - select ut_suite_cache_seq.nextval, s.i.self_type as self_type, s.i.path as path, + select /*+ no_parallel */ ut_suite_cache_seq.nextval, s.i.self_type as self_type, s.i.path as path, upper(s.i.object_owner) as object_owner, upper(s.i.object_name) as object_name, upper(s.i.name) as name, s.i.line_no as line_no, s.i.parse_time as parse_time, s.i.description as description, - s.i.rollback_type as rollback_type, s.i.disabled_flag as disabled_flag, s.i.warnings as warnings, + s.i.rollback_type as rollback_type, s.i.disabled_flag as disabled_flag,s.i.disabled_reason as disabled_reason, s.i.warnings as warnings, s.i.before_all_list as before_all_list, s.i.after_all_list as after_all_list, null before_each_list, null after_each_list, null before_test_list, null after_test_list, @@ -347,11 +377,11 @@ create or replace package body ut_suite_cache_manager is null item from suites s; - insert into ut_suite_cache t + insert /*+ no_parallel */ into ut_suite_cache t ( id, self_type, path, object_owner, object_name, name, line_no, parse_time, description, - rollback_type, disabled_flag, warnings, + rollback_type, disabled_flag,disabled_reason, warnings, before_all_list, after_all_list, before_each_list, after_each_list, before_test_list, after_test_list, @@ -362,10 +392,10 @@ create or replace package body ut_suite_cache_manager is select treat(value(x) as ut_test) t from table ( a_suite_items ) x where x.self_type in ( 'UT_TEST' ) ) - select ut_suite_cache_seq.nextval, s.t.self_type as self_type, s.t.path as path, + select /*+ no_parallel */ ut_suite_cache_seq.nextval, s.t.self_type as self_type, s.t.path as path, upper(s.t.object_owner) as object_owner, upper(s.t.object_name) as object_name, upper(s.t.name) as name, s.t.line_no as line_no, s.t.parse_time as parse_time, s.t.description as description, - s.t.rollback_type as rollback_type, s.t.disabled_flag as disabled_flag, s.t.warnings as warnings, + s.t.rollback_type as rollback_type, s.t.disabled_flag as disabled_flag, s.t.disabled_reason as disabled_reason, s.t.warnings as warnings, null before_all_list, null after_all_list, s.t.before_each_list as before_each_list, s.t.after_each_list as after_each_list, s.t.before_test_list as before_test_list, s.t.after_test_list as after_test_list, @@ -385,35 +415,38 @@ create or replace package body ut_suite_cache_manager is l_objects := get_missing_cache_objects(a_schema_name); if l_objects is not empty then - delete from ut_suite_cache i + delete /*+ no_parallel */ from ut_suite_cache i where i.object_owner = a_schema_name - and i.object_name in ( select column_value from table (l_objects) ); + and i.object_name in ( select /*+ no_parallel */ column_value from table (l_objects) ); - delete from ut_suite_cache_package i + delete /*+ no_parallel */ 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) ); + and i.object_name in ( select /*+ no_parallel */ column_value from table (l_objects) ); end if; commit; end; function get_cached_suite_info( - a_object_owner varchar2, - a_object_name varchar2 + a_schema_paths ut_path_items + ) return ut_suite_cache_rows is + begin + return get_cached_suite_rows(get_suite_items(a_schema_paths)); + end; + + function get_suite_items_info( + a_suite_cache_items ut_suite_cache_rows ) return ut_suite_items_info is - l_cache_rows ut_suite_cache_rows; l_results ut_suite_items_info; begin - l_cache_rows := get_cached_suite_rows( a_object_owner => a_object_owner, a_object_name =>a_object_name ); - select ut_suite_item_info( + select /*+ no_parallel */ ut_suite_item_info( c.object_owner, c.object_name, c.name, c.description, c.self_type, c.line_no, - c.path, c.disabled_flag, c.tags + c.path, c.disabled_flag, c.disabled_reason, c.tags ) bulk collect into l_results - from table(l_cache_rows) c; - - return l_results; + from table(a_suite_cache_items) c; + return l_results; end; function get_cached_packages( @@ -421,7 +454,7 @@ create or replace package body ut_suite_cache_manager is ) return ut_object_names is l_results ut_object_names; begin - select ut_object_name( c.object_owner, c.object_name ) + select /*+ no_parallel */ ut_object_name( c.object_owner, c.object_name ) bulk collect into l_results from ut_suite_cache_package c join table ( a_schema_names ) s @@ -437,7 +470,7 @@ create or replace package body ut_suite_cache_manager is l_count integer; begin if a_procedure_name is not null then - select count( 1 ) into l_count from dual + select /*+ no_parallel */ count( 1 ) into l_count from dual where exists( select 1 from ut_suite_cache c @@ -446,7 +479,7 @@ create or replace package body ut_suite_cache_manager is and c.name = a_procedure_name ); elsif a_package_name is not null then - select count( 1 ) into l_count from dual + select /*+ no_parallel */ count( 1 ) into l_count from dual where exists( select 1 from ut_suite_cache c @@ -454,7 +487,7 @@ create or replace package body ut_suite_cache_manager is and c.object_name = a_package_name ); else - select count( 1 ) into l_count from dual + select /*+ no_parallel */ count( 1 ) into l_count from dual where exists( select 1 from ut_suite_cache c diff --git a/source/core/ut_suite_cache_manager.pks b/source/core/ut_suite_cache_manager.pks index d5b31ccee..6ac1d7bd6 100644 --- a/source/core/ut_suite_cache_manager.pks +++ b/source/core/ut_suite_cache_manager.pks @@ -1,7 +1,7 @@ -create or replace package ut_suite_cache_manager authid definer is +create or replace noneditionable package ut_suite_cache_manager authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -40,30 +40,43 @@ create or replace package ut_suite_cache_manager authid definer is */ procedure remove_missing_objs_from_cache(a_schema_name varchar2); + /* + * We will sort a suites in hierarchical structure. + * Sorting from bottom to top so when we consolidate + * we will go in proper order. + */ + procedure sort_and_randomize_tests( + a_suite_rows in out ut_suite_cache_rows, + a_random_seed positive := null); + /* * Retrieves suite items data from cache. * Returned data is not filtered by user access rights. * Not to be used publicly. Used internally for building suites at runtime. */ function get_cached_suite_rows( - a_object_owner varchar2, - a_path varchar2 := null, - a_object_name varchar2 := null, - a_procedure_name varchar2 := null, - a_random_seed positive := null, - a_tags ut_varchar2_rows := null + a_suites_filtered ut_suite_cache_rows ) return ut_suite_cache_rows; + function get_schema_paths(a_paths in ut_varchar2_list) return ut_path_items; + /* * Retrieves suite item info rows from cache. * Returned data is not filtered by user access rights. * Not to be used publicly. Used internally for building suites info. */ function get_cached_suite_info( - a_object_owner varchar2, - a_object_name varchar2 + a_schema_paths ut_path_items + ) return ut_suite_cache_rows; + + function get_suite_items_info( + a_suite_cache_items ut_suite_cache_rows ) return ut_suite_items_info; + function get_suite_items ( + a_schema_paths ut_path_items + ) return ut_suite_cache_rows; + /* * Retrieves list of cached suite packages. * Returned data is not filtered by user access rights. @@ -83,6 +96,6 @@ create or replace package ut_suite_cache_manager authid definer is a_package_name varchar2, a_procedure_name varchar2 ) return boolean; - + end ut_suite_cache_manager; / diff --git a/source/core/ut_suite_cache_package.sql b/source/core/ut_suite_cache_package.sql index 06f786949..40b7b8dec 100644 --- a/source/core/ut_suite_cache_package.sql +++ b/source/core/ut_suite_cache_package.sql @@ -1,7 +1,7 @@ create table ut_suite_cache_package ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/ut_suite_cache_schema.sql b/source/core/ut_suite_cache_schema.sql index 46c7b0ef3..cace332e3 100644 --- a/source/core/ut_suite_cache_schema.sql +++ b/source/core/ut_suite_cache_schema.sql @@ -1,7 +1,7 @@ create table ut_suite_cache_schema ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/ut_suite_cache_seq.sql b/source/core/ut_suite_cache_seq.sql index 36de28809..020c25c4d 100644 --- a/source/core/ut_suite_cache_seq.sql +++ b/source/core/ut_suite_cache_seq.sql @@ -1,7 +1,7 @@ create sequence ut_suite_cache_seq /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 603e713ec..fa60be59e 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_suite_manager is +create or replace noneditionable package body ut_suite_manager is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -17,19 +17,9 @@ create or replace package body ut_suite_manager is */ gc_suitpath_error_message constant varchar2(100) := 'Suitepath exceeds 1000 CHAR on: '; - - type t_path_item is record ( - object_name varchar2(250), - procedure_name varchar2(250), - suite_path varchar2(4000) - ); - type t_path_items is table of t_path_item; - type t_schema_paths is table of t_path_items index by varchar2(250 char); - cursor c_cached_suites_cursor is select * from table(ut_suite_cache_rows()); type tt_cached_suites is table of c_cached_suites_cursor%rowtype; type t_cached_suites_cursor is ref cursor return c_cached_suites_cursor%rowtype; - type t_item_levels is table of ut_suite_items index by binary_integer; ------------------ @@ -41,7 +31,8 @@ create or replace package body ut_suite_manager is else for i in 1 .. a_paths.count loop l_path := a_paths(i); - if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then + if l_path is null or not ( + regexp_like(l_path, '^[[:alnum:]$#_\*]+(\.[[:alnum:]$#_\*]+){0,2}$') or regexp_like(l_path, '^([[:alnum:]$#_]+)?:[[:alnum:]$#_\*]+(\.[[:alnum:]$#_\*]+)*$')) then raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL')); end if; end loop; @@ -70,9 +61,9 @@ create or replace package body ut_suite_manager is for i in 1 .. a_paths.count loop --if path is suite-path - if regexp_like(a_paths(i), '^([A-Za-z0-9$#_]+)?:') then + if regexp_like(a_paths(i), '^([[:alnum:]$#_]+)?:') then --get schema name / path - l_schema := regexp_substr(a_paths(i), '^([A-Za-z0-9$#_]+)?:',subexpression => 1); + l_schema := regexp_substr(a_paths(i), '^([[:alnum:]$#_]+)?:',subexpression => 1); -- transform ":path1[.path2]" to "schema:path1[.path2]" if l_schema is not null then l_schema := sys.dbms_assert.schema_name(upper(l_schema)); @@ -84,12 +75,14 @@ create or replace package body ut_suite_manager is -- get schema name / object.[procedure] name -- When path is one of: schema or schema.package[.object] or package[.object] -- transform it back to schema[.package[.object]] + -- Object name or procedure is allowed to have filter char + -- However this is not allowed on schema begin - l_object := regexp_substr(a_paths(i), '^[A-Za-z0-9$#_]+'); + l_object := regexp_substr(a_paths(i), '^[[:alnum:]$#_\*]+'); l_schema := sys.dbms_assert.schema_name(upper(l_object)); exception when sys.dbms_assert.invalid_schema_name then - if ut_metadata.package_exists_in_cur_schema(upper(l_object)) then + if l_object like '%*%' or ut_metadata.package_exists_in_cur_schema(upper(l_object)) then a_paths(i) := c_current_schema || '.' || a_paths(i); l_schema := c_current_schema; else @@ -110,34 +103,6 @@ create or replace package body ut_suite_manager is l_schema_names := resolve_schema_names(a_paths); end; - function group_paths_by_schema(a_paths ut_varchar2_list) return t_schema_paths is - c_package_path_regex constant varchar2(100) := '^([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?(\.([A-Za-z0-9$#_]+))?$'; - l_schema varchar2(4000); - l_empty_result t_path_item; - l_result t_path_item; - l_results t_schema_paths; - begin - for i in 1 .. a_paths.count loop - l_result := l_empty_result; - if a_paths(i) like '%:%' then - l_schema := upper(regexp_substr(a_paths(i),'^[^.:]+')); - l_result.suite_path := ltrim(regexp_substr(a_paths(i),'[.:].*$'),':'); - else - l_schema := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 1); - l_result.object_name := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 3); - l_result.procedure_name := regexp_substr(a_paths(i), c_package_path_regex, subexpression => 5); - end if; - if l_results.exists(l_schema) then - l_results(l_schema).extend; - l_results(l_schema)(l_results(l_schema).last) := l_result; - else - l_results(l_schema) := t_path_items(l_result); - end if; - end loop; - return l_results; - end; - - function sort_by_seq_no( a_list ut_executables ) return ut_executables is @@ -187,7 +152,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -200,7 +165,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -216,7 +181,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -229,7 +194,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -245,7 +210,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -256,7 +221,7 @@ create or replace package body ut_suite_manager is self_type => a_rows( a_idx ).self_type, object_owner => a_rows( a_idx ).object_owner, object_name => lower( a_rows( a_idx ).object_name), name => lower( a_rows( a_idx ).name), description => a_rows( a_idx ).description, path => a_rows( a_idx ).path, - rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, + rollback_type => a_rows( a_idx ).rollback_type, disabled_flag => a_rows( a_idx ).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -269,7 +234,7 @@ create or replace package body ut_suite_manager is self_type => a_rows(a_idx).self_type, object_owner => a_rows(a_idx).object_owner, object_name => lower(a_rows(a_idx).object_name), name => lower(a_rows(a_idx).name), description => a_rows(a_idx).description, path => a_rows(a_idx).path, - rollback_type => a_rows(a_idx).rollback_type, disabled_flag => a_rows(a_idx).disabled_flag, + rollback_type => a_rows(a_idx).rollback_type, disabled_flag => a_rows(a_idx).disabled_flag, disabled_reason => a_rows(a_idx).disabled_reason, line_no => a_rows(a_idx).line_no, parse_time => a_rows(a_idx).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows(a_idx).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), @@ -331,42 +296,80 @@ create or replace package body ut_suite_manager is close a_suite_data_cursor; end reconstruct_from_cache; - function get_cached_suite_data( - a_object_owner varchar2, - a_path varchar2 := null, - a_object_name varchar2 := null, - a_procedure_name varchar2 := null, - a_skip_all_objects boolean := false, - a_random_seed positive, - a_tags ut_varchar2_rows := null - ) return t_cached_suites_cursor is - l_unfiltered_rows ut_suite_cache_rows; - l_result t_cached_suites_cursor; + function get_filtered_cursor( + a_unfiltered_rows in ut_suite_cache_rows, + a_skip_all_objects boolean := false + ) + return ut_suite_cache_rows is + l_result ut_suite_cache_rows := ut_suite_cache_rows(); begin - l_unfiltered_rows := ut_suite_cache_manager.get_cached_suite_rows( - a_object_owner, - a_path, - a_object_name, - a_procedure_name, - a_random_seed, - a_tags - ); - if a_skip_all_objects then - open l_result for - select c.* from table(l_unfiltered_rows) c; + if ut_metadata.user_has_execute_any_proc() or a_skip_all_objects then + l_result := a_unfiltered_rows; else - open l_result for - select c.* from table(l_unfiltered_rows) c - where exists + select obj bulk collect into l_result + from ( + select /*+ no_parallel */ value(c) as obj from table(a_unfiltered_rows) c + where sys_context( 'userenv', 'current_user' ) = upper(c.object_owner) + union all + select /*+ no_parallel */ value(c) as obj from table(a_unfiltered_rows) c + where sys_context( 'userenv', 'current_user' ) != upper(c.object_owner) + and ( exists ( select 1 from all_objects a 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'; + and a.owner = c.object_owner + and a.object_type = 'PACKAGE' + ) + or c.self_type = 'UT_LOGICAL_SUITE')); end if; - + return l_result; + end; + + procedure reconcile_paths_and_suites( + a_schema_paths ut_path_items, + a_filtered_rows ut_suite_cache_rows + ) is + begin + for i in ( select /*+ no_parallel */ sp.schema_name,sp.object_name,sp.procedure_name, + sp.suite_path,sc.path + from table(a_schema_paths) sp left outer join + table(a_filtered_rows) sc on + (( upper(sp.schema_name) = upper(sc.object_owner) and upper(sp.object_name) = upper(sc.object_name) + and nvl(upper(sp.procedure_name),sc.name) = sc.name ) + or (sc.path = sp.suite_path)) + where sc.path is null) + loop + if i.suite_path is not null then + raise_application_error(ut_utils.gc_suite_package_not_found,'No suite packages found for path '||i.schema_name||':'||i.suite_path|| '.'); + elsif i.procedure_name is not null then + raise_application_error(ut_utils.gc_suite_package_not_found,'Suite test '||i.schema_name||'.'||i.object_name|| '.'||i.procedure_name||' does not exist'); + elsif i.object_name is not null then + raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||i.schema_name||'.'||i.object_name|| ' does not exist'); + end if; + end loop; + end; + + function get_cached_suite_data( + a_schema_paths ut_path_items, + a_random_seed positive, + a_tags varchar2 := null, + a_skip_all_objects boolean := false + ) return t_cached_suites_cursor is + l_unfiltered_rows ut_suite_cache_rows; + l_tag_filter_applied ut_suite_cache_rows; + l_filtered_rows ut_suite_cache_rows; + l_result t_cached_suites_cursor; + begin + l_unfiltered_rows := ut_suite_cache_manager.get_suite_items(a_schema_paths); + + l_tag_filter_applied := ut_suite_tag_filter.apply(l_unfiltered_rows,a_tags); + l_filtered_rows := get_filtered_cursor(ut_suite_cache_manager.get_cached_suite_rows(l_tag_filter_applied),a_skip_all_objects); + reconcile_paths_and_suites(a_schema_paths,l_filtered_rows); + + ut_suite_cache_manager.sort_and_randomize_tests(l_filtered_rows,a_random_seed); + + open l_result for + select * from table(l_filtered_rows); return l_result; end; @@ -442,31 +445,21 @@ create or replace package body ut_suite_manager is ut_event_manager.trigger_event('refresh_cache - end'); end; - procedure add_suites_for_path( - a_owner_name varchar2, - a_path varchar2 := null, - a_object_name varchar2 := null, - a_procedure_name varchar2 := null, + procedure add_suites_for_paths( + a_schema_paths ut_path_items, a_suites in out nocopy ut_suite_items, a_random_seed positive, - a_tags ut_varchar2_rows := null + a_tags varchar2 := null ) is begin - refresh_cache(a_owner_name); - reconstruct_from_cache( a_suites, get_cached_suite_data( - a_owner_name, - a_path, - a_object_name, - a_procedure_name, - can_skip_all_objects_scan(a_owner_name), + a_schema_paths, a_random_seed, a_tags ) ); - end; ----------------------------------------------- @@ -482,31 +475,37 @@ create or replace package body ut_suite_manager is a_skip_all_objects boolean := false ) return ut_suite_items is l_suites ut_suite_items := ut_suite_items(); + l_schema_paths ut_path_items; begin build_and_cache_suites(a_owner_name, a_annotated_objects); - + l_schema_paths := ut_path_items(ut_path_item(a_owner_name,a_object_name,a_procedure_name,a_path)); reconstruct_from_cache( l_suites, get_cached_suite_data( - a_owner_name, - a_path, - a_object_name, - a_procedure_name, - a_skip_all_objects, + l_schema_paths, null, - null + null, + a_skip_all_objects ) ); return l_suites; end; - function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is + function get_schema_ut_packages(a_schema_names ut_varchar2_rows, a_schema_name_expr varchar2) return ut_object_names is + l_schema_names ut_varchar2_rows := a_schema_names; begin - for i in 1 .. a_schema_names.count loop - refresh_cache(a_schema_names(i)); + if a_schema_name_expr is not null then + select username + bulk collect into l_schema_names + from all_users + where regexp_like(username,a_schema_name_expr,'i'); + end if; + + for i in 1 .. l_schema_names.count loop + refresh_cache(l_schema_names(i)); end loop; - return ut_suite_cache_manager.get_cached_packages( a_schema_names ); + return ut_suite_cache_manager.get_cached_packages( l_schema_names ); end; function get_schema_names(a_paths ut_varchar2_list) return ut_varchar2_rows is @@ -527,92 +526,72 @@ create or replace package body ut_suite_manager is a_paths ut_varchar2_list, a_suites out nocopy ut_suite_items, a_random_seed positive := null, - a_tags ut_varchar2_rows := ut_varchar2_rows() + a_tags varchar2 := null ) is l_paths ut_varchar2_list := a_paths; - l_path_items t_path_items; - l_path_item t_path_item; + l_schema_names ut_varchar2_rows; + l_schema_paths ut_path_items; l_schema varchar2(4000); - l_suites_count pls_integer := 0; - l_index varchar2(4000 char); - l_schema_paths t_schema_paths; begin ut_event_manager.trigger_event('configure_execution_by_path - start'); a_suites := ut_suite_items(); --resolve schema names from paths and group paths by schema name - resolve_schema_names(l_paths); - - l_schema_paths := group_paths_by_schema(l_paths); + l_schema_names := resolve_schema_names(l_paths); - l_schema := l_schema_paths.first; + --refresh cache + l_schema := l_schema_names.first; while l_schema is not null loop - l_path_items := l_schema_paths(l_schema); - for i in 1 .. l_path_items.count loop - l_path_item := l_path_items(i); - add_suites_for_path( - upper(l_schema), - l_path_item.suite_path, - l_path_item.object_name, - l_path_item.procedure_name, - a_suites, - a_random_seed, - a_tags - ); - if a_suites.count = l_suites_count then - if l_path_item.suite_path is not null then - raise_application_error(ut_utils.gc_suite_package_not_found,'No suite packages found for path '||l_schema||':'||l_path_item.suite_path|| '.'); - elsif l_path_item.procedure_name is not null then - raise_application_error(ut_utils.gc_suite_package_not_found,'Suite test '||l_schema||'.'||l_path_item.object_name|| '.'||l_path_item.procedure_name||' does not exist'); - elsif l_path_item.object_name is not null then - raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_path_item.object_name|| ' does not exist'); - end if; - end if; - l_index := a_suites.first; - l_suites_count := a_suites.count; - end loop; - l_schema := l_schema_paths.next(l_schema); + refresh_cache(upper(l_schema_names(l_schema))); + l_schema := l_schema_names.next(l_schema); end loop; + l_schema_paths := ut_suite_cache_manager.get_schema_paths(l_paths); + + --We will get a single list of paths rather than loop by loop. + add_suites_for_paths( + l_schema_paths, + a_suites, + a_random_seed, + a_tags + ); + --propagate rollback type to suite items after organizing suites into hierarchy for i in 1 .. a_suites.count loop a_suites(i).set_rollback_type( a_suites(i).get_rollback_type() ); end loop; - ut_event_manager.trigger_event('configure_execution_by_path - start'); + ut_event_manager.trigger_event('configure_execution_by_path - end'); end configure_execution_by_path; function get_suites_info( - a_owner_name varchar2, - a_package_name varchar2 := null + a_paths ut_varchar2_list ) return sys_refcursor is - l_result sys_refcursor; - l_all_suite_info ut_suite_items_info; - l_owner_name varchar2(250) := ut_utils.qualified_sql_name(a_owner_name); - l_package_name varchar2(250) := ut_utils.qualified_sql_name(a_package_name); - begin + l_result sys_refcursor; + l_all_suite_info ut_suite_items_info; + l_schema_names ut_varchar2_rows; + l_schema_paths ut_path_items; + l_paths ut_varchar2_list := a_paths; + l_schema varchar2(4000); + l_unfiltered_rows ut_suite_cache_rows; + l_filtered_rows ut_suite_cache_rows; - refresh_cache(l_owner_name); + begin + l_schema_names := resolve_schema_names(l_paths); + --refresh cache + l_schema := l_schema_names.first; + while l_schema is not null loop + refresh_cache(upper(l_schema_names(l_schema))); + l_schema := l_schema_names.next(l_schema); + end loop; + l_schema_paths := ut_suite_cache_manager.get_schema_paths(l_paths); + l_unfiltered_rows := ut_suite_cache_manager.get_cached_suite_info(l_schema_paths); + l_filtered_rows := get_filtered_cursor(l_unfiltered_rows); + l_all_suite_info := ut_suite_cache_manager.get_suite_items_info(l_filtered_rows); + open l_result for + select /*+ no_parallel */ value(c) + from table(l_all_suite_info) c + order by c.object_owner, c.object_name, c.item_line_no; - l_all_suite_info := ut_suite_cache_manager.get_cached_suite_info( l_owner_name, l_package_name ); - if can_skip_all_objects_scan( l_owner_name ) then - open l_result for - select value(c) - from table(l_all_suite_info) c - order by c.object_owner, c.object_name, c.item_line_no; - else - open l_result for - select value(c) - from table(l_all_suite_info) c - where exists - ( select 1 - from all_objects a - where a.object_name = c.object_name - 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; end; @@ -631,7 +610,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 ) and l_package_name is not null then - select count(1) + select /*+ no_parallel */ count(1) into l_count from dual c where exists diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index 093201b36..60c3595bb 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -1,7 +1,7 @@ -create or replace package ut_suite_manager authid current_user is +create or replace noneditionable package ut_suite_manager authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ create or replace package ut_suite_manager authid current_user is * @param a_schema_names list of schemas to return the information for * @return array containing unit test schema and object names */ - function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names; + function get_schema_ut_packages(a_schema_names ut_varchar2_rows, a_schema_name_expr varchar2) return ut_object_names; /** * Builds a hierarchical suites based on given suite-paths @@ -39,6 +39,7 @@ create or replace package ut_suite_manager authid current_user is * @return array containing root suites-ready to be executed * */ + --TODO:Zerknij czy mozna wywalic function configure_execution_by_path(a_paths ut_varchar2_list, a_random_seed positive := null) return ut_suite_items; /** @@ -52,7 +53,7 @@ create or replace package ut_suite_manager authid current_user is a_paths in ut_varchar2_list, a_suites out nocopy ut_suite_items, a_random_seed in positive := null, - a_tags ut_varchar2_rows := ut_varchar2_rows() + a_tags in varchar2 := null ); /** @@ -79,15 +80,11 @@ create or replace package ut_suite_manager authid current_user is /** * Returns a ref cursor containing information about unit test suites and the tests contained in them * - * @param a_owner owner of unit tests to retrieve - * @param a_package_name name of test package (optional) - * @param a_procedure_name name of test procedure (optional) - * @return ut_suite_items_info table of objects + * @param a_paths list of paths to be resolved and return a suites. */ function get_suites_info( - a_owner_name varchar2, - a_package_name varchar2 := null - ) return sys_refcursor; + a_paths ut_varchar2_list + ) return sys_refcursor; /** * Returns true if given suite item exists diff --git a/source/core/ut_suite_tag_filter.pkb b/source/core/ut_suite_tag_filter.pkb new file mode 100644 index 000000000..447ba47d5 --- /dev/null +++ b/source/core/ut_suite_tag_filter.pkb @@ -0,0 +1,290 @@ +create or replace noneditionable package body ut_suite_tag_filter is + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + /** + * Constants use in postfix and infix transformations + */ + gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!'); + gc_unary_operators constant ut_varchar2_list := ut_varchar2_list('!'); -- right side associative operator + gc_binary_operators constant ut_varchar2_list := ut_varchar2_list('|','&'); -- left side associative operator + gc_reserved_tag_words constant ut_varchar2_list := ut_varchar2_list('none','any'); + gc_exception_msg constant varchar2(200) := 'Invalid tag expression'; + + type t_precedence_table is table of number index by varchar2(1); + g_precedence t_precedence_table; + + function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list is + l_tags_tokens ut_varchar2_list := ut_varchar2_list(); + begin + --Tokenize a string into operators and tags + select regexp_substr(a_tags,'([^!()|&]+)|([!()|&])', 1, level) as string_parts + bulk collect into l_tags_tokens + from dual connect by regexp_substr (a_tags, '([^!()|&]+)|([!()|&])', 1, level) is not null; + + return l_tags_tokens; + end; + + /* + To support a legact tag notation + , = OR + - = NOT + we will perform a replace of that characters into + new notation. + | = OR + & = AND + ! = NOT + */ + function replace_legacy_tag_notation(a_tags varchar2 + ) return varchar2 is + l_tags ut_varchar2_list := ut_utils.string_to_table(a_tags,','); + l_tags_include varchar2(4000); + l_tags_exclude varchar2(4000); + l_return_tag varchar2(4000); + begin + if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then + + select '('||listagg( t.column_value,'|') + within group( order by column_value)||')' + into l_tags_include + from table(l_tags) t + where t.column_value not like '-%'; + + select '('||listagg( replace(t.column_value,'-','!'),' & ') + within group( order by column_value)||')' + into l_tags_exclude + from table(l_tags) t + where t.column_value like '-%'; + + + l_return_tag:= + case + when l_tags_include <> '()' and l_tags_exclude <> '()' + then l_tags_include || ' & ' || l_tags_exclude + when l_tags_include <> '()' + then l_tags_include + when l_tags_exclude <> '()' + then l_tags_exclude + end; + else + l_return_tag := a_tags; + end if; + return l_return_tag; + end; + + /* + https://stackoverflow.com/questions/29634992/shunting-yard-validate-expression + */ + function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list is + l_operator_stack ut_stack := ut_stack(); + l_rnp_tokens ut_varchar2_list := ut_varchar2_list(); + l_token varchar2(32767); + l_expect_operand boolean := true; + l_expect_operator boolean := false; + l_idx pls_integer; + begin + l_idx := a_tags.first; + --Exuecute modified shunting algorithm + WHILE (l_idx is not null) loop + l_token := a_tags(l_idx); + if (l_token member of gc_operators and l_token member of gc_binary_operators) then + if not(l_expect_operator) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + while l_operator_stack.top > 0 and (g_precedence(l_operator_stack.peek) > g_precedence(l_token)) loop + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop; + end loop; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif (l_token member of gc_operators and l_token member of gc_unary_operators) then + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif l_token = '(' then + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif l_token = ')' then + if not(l_expect_operator) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + while l_operator_stack.peek <> '(' loop + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop; + end loop; + l_operator_stack.pop; --Pop the open bracket and discard it + l_expect_operand := false; + l_expect_operator:= true; + else + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) :=l_token; + l_expect_operator := true; + l_expect_operand := false; + end if; + + l_idx := a_tags.next(l_idx); + end loop; + + while l_operator_stack.peek is not null loop + if l_operator_stack.peek in ('(',')') then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last):=l_operator_stack.pop; + end loop; + + return l_rnp_tokens; + end shunt_logical_expression; + + function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2) + return varchar2 is + l_infix_stack ut_stack := ut_stack(); + l_right_side varchar2(32767); + l_left_side varchar2(32767); + l_infix_exp varchar2(32767); + l_member_token varchar2(20) := ' member of '||a_tags_column_name; + l_idx pls_integer; + begin + l_idx := a_postfix_exp.first; + while ( l_idx is not null) loop + --If the token we got is a none or any keyword + if a_postfix_exp(l_idx) member of gc_reserved_tag_words then + + l_infix_stack.push( + case + when a_postfix_exp(l_idx) = 'none' then '('||a_tags_column_name||' is empty or '||a_tags_column_name||' is null)' + else a_tags_column_name||' is not empty' + end + ); + --If token is operand but also single tag + elsif regexp_count(a_postfix_exp(l_idx),'[!()|&]') = 0 then + l_infix_stack.push(q'[']'||a_postfix_exp(l_idx)||q'[']'||l_member_token); + --If token is unary operator not + elsif a_postfix_exp(l_idx) member of gc_unary_operators then + l_right_side := l_infix_stack.pop; + l_infix_exp := a_postfix_exp(l_idx)||'('||l_right_side||')'; + l_infix_stack.push(l_infix_exp); + --If token is binary operator + elsif a_postfix_exp(l_idx) member of gc_binary_operators then + l_right_side := l_infix_stack.pop; + l_left_side := l_infix_stack.pop; + l_infix_exp := '('||l_left_side||a_postfix_exp(l_idx)||l_right_side||')'; + l_infix_stack.push(l_infix_exp); + end if; + l_idx := a_postfix_exp.next(l_idx); + end loop; + + return l_infix_stack.pop; + end conv_postfix_to_infix_sql; + + function create_where_filter(a_tags varchar2 + ) return varchar2 is + l_tags varchar2(4000); + begin + l_tags := replace(replace_legacy_tag_notation(a_tags),' '); + l_tags := conv_postfix_to_infix_sql(shunt_logical_expression(tokenize_tags_string(l_tags)),'tags'); + l_tags := replace(l_tags, '|',' or '); + l_tags := replace(l_tags ,'&',' and '); + l_tags := replace(l_tags ,'!','not'); + return l_tags; + end; + + + /* + Having a base set of suites we will do a further filter down if there are + any tags defined. + */ + function get_tags_suites ( + a_suite_items ut_suite_cache_rows, + a_tags varchar2 + ) return ut_suite_cache_rows is + l_results ut_suite_cache_rows; + l_sql varchar2(32000); + l_tags_filter_clause varchar2(4000):= create_where_filter(a_tags); + begin + l_sql := + q'[ + with + suites_mv as ( + select c.id,value(c) as obj,c.path as path,c.self_type,c.object_owner,c.tags as tags + from table(:suite_items) c + ), + propagate_tags_from_ancestors as ( + select c.id, c.self_type, c.path, c.object_owner, cast(collect(c_tags.tag) as ut_varchar2_rows) as tags + from suites_mv c + left join suites_mv t + on t.self_type in ('UT_SUITE','UT_SUITE_CONTEXT') + and c.path like t.path||'.%' /* all descendants */ + and c.object_owner = t.object_owner + outer apply (select column_value tag from table(c.tags) union select column_value tag from table(t.tags) ) c_tags + group by c.id, c.self_type, c.path, c.object_owner + ), + filter_by_tags as ( + select * + from propagate_tags_from_ancestors x + where ( ]'||l_tags_filter_clause||q'[ ) + ), + only_items_with_tests as ( + select item.obj + from suites_mv item + where exists ( + select 1 + from filter_by_tags tst + where tst.self_type in ('UT_TEST') + and (tst.path||'.' like item.path || '.%' /*all descendants and self*/ and item.object_owner = tst.object_owner) + ) + ) + select obj from only_items_with_tests]'; + execute immediate l_sql bulk collect into l_results using a_suite_items; + + return l_results; + end; + + function apply( + a_unfiltered_rows ut_suite_cache_rows, + a_tags varchar2 := null + ) return ut_suite_cache_rows is + l_suite_items ut_suite_cache_rows := a_unfiltered_rows; + begin + if length(a_tags) > 0 then + l_suite_items := get_tags_suites(l_suite_items,a_tags); + end if; + + return l_suite_items; + end; + +begin + --Define operators precedence + g_precedence('!'):=4; + g_precedence('&'):=3; + g_precedence('|'):=2; + g_precedence(')'):=1; + g_precedence('('):=1; + +end ut_suite_tag_filter; +/ diff --git a/source/core/ut_suite_tag_filter.pks b/source/core/ut_suite_tag_filter.pks new file mode 100644 index 000000000..bb3d4ad73 --- /dev/null +++ b/source/core/ut_suite_tag_filter.pks @@ -0,0 +1,55 @@ +create or replace noneditionable package ut_suite_tag_filter authid definer is + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + /** + * Package that will filter suites by tags + * + */ + + /* + * Return table of tokens character by character + */ + function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list; + + /* + * Function that uses Dijkstra algorithm to parse mathematical and logical expression + * and return a list of elements in Reverse Polish Notation ( postfix ) + * As part of execution it will validate expression. + */ + function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list; + + /* + * Function that converts postfix notation into infix and creating a string of sql filter + * that checking a tags collections for tags according to posted logic. + */ + function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2) + return varchar2; + + /* + * Generates a part where clause sql + */ + function create_where_filter(a_tags varchar2) + return varchar2; + + function apply( + a_unfiltered_rows ut_suite_cache_rows, + a_tags varchar2 := null + ) return ut_suite_cache_rows; + +end ut_suite_tag_filter; +/ diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 05339a831..c8ced3bb9 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_utils is +create or replace noneditionable package body ut_utils is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -19,10 +19,13 @@ create or replace package body ut_utils is /** * Constants regex used to validate XML name */ - 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_invalid_first_xml_char constant varchar2(50) := '[^_[:alpha:]]'; + gc_invalid_xml_char constant varchar2(50) := '[^_[:alnum:]\.-]'; + gc_full_valid_xml_name constant varchar2(50) := '^([[:alpha:]])([_[:alnum:]\.-])*$'; gc_owner_hash constant integer(11) := dbms_utility.get_hash_value( ut_owner(), 0, power(2,31)-1); + gc_open_chars constant varchar2(4):= '[{(<'; + gc_close_chars constant varchar2(4):= ']})>'; + gc_max_plsql_source_len constant integer := 32767; function surround_with(a_value varchar2, a_quote_char varchar2) return varchar2 is begin @@ -91,8 +94,8 @@ create or replace package body ut_utils is a_max_output_len in number := gc_max_output_string_length ) return varchar2 is l_result varchar2(32767); - c_length constant integer := coalesce( length( a_value ), 0 ); - c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 ); + c_length constant integer := coalesce( lengthb( a_value ), 0 ); + c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 ); c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len; begin if c_length = 0 then @@ -111,8 +114,8 @@ create or replace package body ut_utils is a_max_output_len in number := gc_max_output_string_length ) return varchar2 is l_result varchar2(32767); - c_length constant integer := coalesce(dbms_lob.getlength(a_value), 0); - c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 ); + c_length constant integer := coalesce(ut_utils.lengthb_clob(a_value), 0); + c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 ); c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len; begin if a_value is null then @@ -134,7 +137,7 @@ create or replace package body ut_utils is ) return varchar2 is l_result varchar2(32767); c_length constant integer := coalesce(dbms_lob.getlength(a_value), 0); - c_max_input_string_length constant integer := a_max_output_len - coalesce( length( a_quote_char ) * 2, 0 ); + c_max_input_string_length constant integer := a_max_output_len - coalesce( lengthb( a_quote_char ) * 2, 0 ); c_overflow_substr_len constant integer := c_max_input_string_length - gc_more_data_string_len; begin if a_value is null then @@ -350,7 +353,7 @@ create or replace package body ut_utils is function get_utplsql_objects_list return ut_object_names is l_result ut_object_names; begin - select distinct ut_object_name(sys_context('userenv','current_user'), o.object_name) + select /*+ no_parallel */ distinct ut_object_name(sys_context('userenv','current_user'), o.object_name) bulk collect into l_result from user_objects o where o.object_name = 'UT' or object_name like 'UT\_%' escape '\' @@ -411,7 +414,7 @@ create or replace package body ut_utils is if a_list is null then a_list := ut_varchar2_rows(); end if; - if length(a_item) > gc_max_storage_varchar2_len then + if lengthb(a_item) > gc_max_storage_varchar2_len then append_to_list( a_list, ut_utils.convert_collection( @@ -467,7 +470,7 @@ create or replace package body ut_utils is l_result := ut_varchar2_rows(); for i in 1 .. a_collection.count loop l_result.extend(); - l_result(i) := substr(a_collection(i),1,gc_max_storage_varchar2_len); + l_result(i) := substrb(a_collection(i),1,gc_max_storage_varchar2_len); end loop; end if; return l_result; @@ -532,8 +535,8 @@ create or replace package body ut_utils is procedure flush_lines(a_lines ut_varchar2_rows, a_offset integer) is begin if a_lines is not empty then - insert into ut_dbms_output_cache (seq_no,text) - select rownum+a_offset, column_value + insert /*+ no_parallel */ into ut_dbms_output_cache (seq_no,text) + select /*+ no_parallel */ rownum+a_offset, column_value from table(a_lines); end if; end; @@ -558,7 +561,7 @@ create or replace package body ut_utils is 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; + open l_lines_data for select /*+ no_parallel */ text from ut_dbms_output_cache order by seq_no; loop fetch l_lines_data bulk collect into l_lines limit c_lines_limit; for i in 1 .. l_lines.count loop @@ -570,6 +573,7 @@ create or replace package body ut_utils is end loop; exit when l_lines_data%notfound; end loop; + close l_lines_data; execute immediate 'truncate table ut_dbms_output_cache'; commit; end; @@ -646,104 +650,244 @@ create or replace package body ut_utils is function xmlgen_escaped_string(a_string in varchar2) return varchar2 is l_result varchar2(4000) := a_string; - l_sql varchar2(32767) := q'!select q'[!'||a_string||q'!]' as "!'||a_string||'" from dual'; + l_sql varchar2(32767) := q'!select /*+ no_parallel */ q'[!'||a_string||q'!]' as "!'||a_string||'" from dual'; begin if a_string is not null then - select extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement() + select /*+ no_parallel */ extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement() into l_result from dual; end if; return l_result; end; - function replace_multiline_comments(a_source clob) return clob is - l_result clob; - l_ml_comment_start binary_integer := 1; - l_comment_start binary_integer := 1; - l_text_start binary_integer := 1; - l_escaped_text_start binary_integer := 1; - l_escaped_text_end_char varchar2(1 char); - l_end binary_integer := 1; - l_ml_comment clob; - l_newlines_count binary_integer; - l_offset binary_integer := 1; - l_length binary_integer := coalesce(dbms_lob.getlength(a_source), 0); - begin - l_ml_comment_start := instr(a_source,'/*'); - l_comment_start := instr(a_source,'--'); - l_text_start := instr(a_source,''''); - l_escaped_text_start := instr(a_source,q'[q']'); - while l_offset > 0 and l_ml_comment_start > 0 loop - - if l_ml_comment_start > 0 and (l_ml_comment_start < l_comment_start or l_comment_start = 0) - and (l_ml_comment_start < l_text_start or l_text_start = 0)and (l_ml_comment_start < l_escaped_text_start or l_escaped_text_start = 0) - then - l_end := instr(a_source,'*/',l_ml_comment_start+2); - append_to_clob(l_result, dbms_lob.substr(a_source, l_ml_comment_start-l_offset, l_offset)); + function scan_line(a_line in varchar2, a_in_ml_comment in out boolean) return varchar2 is + -- Normal scan + l_remaining varchar2(32767) := a_line; + l_line varchar2(32767); + l_ml_start binary_integer; + l_comment_start binary_integer; + l_text_start binary_integer; + l_eq_text_start binary_integer; + l_eq_end_char varchar2(1 char); + l_pos binary_integer; + l_end binary_integer; + l_ml_end binary_integer; + begin + if a_in_ml_comment then + l_ml_end := instr(l_remaining, '*/'); + if l_ml_end > 0 then + a_in_ml_comment := false; + l_remaining := substr(l_remaining, l_ml_end + 2); + else + return null; + end if; + end if; + + loop + exit when l_remaining is null; + l_ml_start := instr(l_remaining, '/*'); + l_comment_start := instr(l_remaining, '--'); + l_text_start := instr(l_remaining, ''''); + -- q' always puts ' at l_text_start; just check the char immediately before it + l_eq_text_start := case + when l_text_start > 1 and substr(l_remaining, l_text_start - 1, 1) = 'q' + then l_text_start - 1 + else 0 + end; + -- Sentinel gc_max_plsql_source_len means "not present"; 32767 is beyond any VARCHAR2 position + l_pos := least( + case when l_ml_start > 0 then l_ml_start else gc_max_plsql_source_len end, + case when l_comment_start > 0 then l_comment_start else gc_max_plsql_source_len end, + case when l_text_start > 0 then l_text_start else gc_max_plsql_source_len end, + case when l_eq_text_start > 0 then l_eq_text_start else gc_max_plsql_source_len end + ); + + if l_pos = gc_max_plsql_source_len then + l_line := l_line || l_remaining; + exit; + end if; + + l_line := l_line || substr(l_remaining, 1, l_pos - 1); + l_remaining := substr(l_remaining, l_pos); + -- l_remaining now starts exactly at the token; all branch offsets below are relative to 1 + if l_pos = l_eq_text_start then + -- q-quoted string: l_remaining starts at 'q', delimiter is at position 3 + l_eq_end_char := translate(substr(l_remaining, 3, 1), gc_open_chars, gc_close_chars); + l_end := instr(l_remaining, l_eq_end_char || '''', 4); if l_end > 0 then - l_ml_comment := substr(a_source, l_ml_comment_start, l_end-l_ml_comment_start); - l_newlines_count := length( l_ml_comment ) - length( translate( l_ml_comment, 'a'||chr(10), 'a') ); - if l_newlines_count > 0 then - append_to_clob(l_result, lpad( chr(10), l_newlines_count, chr(10) ) ); - end if; - l_end := l_end + 2; + l_line := l_line || substr(l_remaining, 1, l_end + 1); + l_remaining := substr(l_remaining, l_end + 2); + else + l_line := l_line || l_remaining; + exit; end if; - else - if l_comment_start > 0 and (l_comment_start < l_ml_comment_start or l_ml_comment_start = 0) - and (l_comment_start < l_text_start or l_text_start = 0) and (l_comment_start < l_escaped_text_start or l_escaped_text_start = 0) - then - l_end := instr(a_source,chr(10),l_comment_start+2); - if l_end > 0 then - l_end := l_end + 1; - end if; - elsif l_text_start > 0 and (l_text_start < l_ml_comment_start or l_ml_comment_start = 0) - and (l_text_start < l_comment_start or l_comment_start = 0) and (l_text_start < l_escaped_text_start or l_escaped_text_start = 0) - then - l_end := instr(a_source,q'[']',l_text_start+1); - - --skip double quotes while searching for end of quoted text - while l_end > 0 and l_end = instr(a_source,q'['']',l_text_start+1) loop - l_end := instr(a_source,q'[']',l_end+1); - end loop; - if l_end > 0 then - l_end := l_end + 1; + elsif l_pos = l_ml_start then + -- Multi-line comment: l_remaining starts at '/*', so end search starts at 3 + l_ml_end := instr(l_remaining, '*/', 3); + if l_ml_end > 0 then + l_remaining := substr(l_remaining, l_ml_end + 2); + else + a_in_ml_comment := true; + -- preserve trailing newline if present — it belongs to this line, not the comment + if substr(l_remaining, -1) = chr(10) then + l_line := l_line || chr(10); end if; + return l_line; + end if; + + elsif l_pos = l_comment_start then + -- Single-line comment: everything from here is comment, keep and stop + l_line := l_line || l_remaining; + return l_line; - elsif l_escaped_text_start > 0 and (l_escaped_text_start < l_ml_comment_start or l_ml_comment_start = 0) - and (l_escaped_text_start < l_comment_start or l_comment_start = 0) and (l_escaped_text_start < l_text_start or l_text_start = 0) - then - --translate char "[" from the start of quoted text "q'[someting]'" into "]" - l_escaped_text_end_char := translate( substr(a_source, l_escaped_text_start + 2, 1), '[{(<', ']})>'); - l_end := instr(a_source,l_escaped_text_end_char||'''',l_escaped_text_start + 3 ); - if l_end > 0 then - l_end := l_end + 2; + else + -- Regular string literal: l_remaining starts at the opening quote + -- scan from position 2 to skip the opening quote + l_end := 2; + loop + l_end := instr(l_remaining, '''', l_end); + exit when l_end = 0; + if substr(l_remaining, l_end, 2) = '''''' then + l_end := l_end + 2; -- skip escaped quote pair + else + exit; -- real closing quote end if; - end if; + end loop; - if l_end = 0 then - append_to_clob(l_result, substr(a_source, l_offset, l_length-l_offset)); + if l_end > 0 then + l_line := l_line || substr(l_remaining, 1, l_end); + l_remaining := substr(l_remaining, l_end + 1); else - append_to_clob(l_result, substr(a_source, l_offset, l_end-l_offset)); + l_line := l_line || l_remaining; + exit; end if; + end if; - l_offset := l_end; - if l_offset >= l_ml_comment_start then - l_ml_comment_start := instr(a_source,'/*',l_offset); + end loop; + + return l_line; + end; + + function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t) + return dbms_preprocessor.source_lines_t + is + l_result dbms_preprocessor.source_lines_t; + l_line varchar2(32767); + l_remaining varchar2(32767); + l_in_ml_comment boolean := false; + l_ml_end binary_integer; + l_has_ml_comment boolean := false; + begin + if a_source.count = 0 then + return a_source; + end if; + + -- Fast pre-scan to check for presence of multi-line comments; if none, return original source unmodified + for i in 1 .. a_source.count loop + if instr(a_source(i), '/*') > 0 then + l_has_ml_comment := true; + exit; end if; - if l_offset >= l_comment_start then - l_comment_start := instr(a_source,'--',l_offset); + end loop; + + if not l_has_ml_comment then + return a_source; + end if; + + for i in 1 .. a_source.count loop + l_line := a_source(i); + + -- Fast path: inside multi-line comment + if l_in_ml_comment then + l_ml_end := instr(l_line, '*/'); + if l_ml_end > 0 then + l_in_ml_comment := false; + l_line := substr(l_line, l_ml_end + 2); + -- fall through to normal scan + else + l_result(i) := ''; + continue; + end if; end if; - if l_offset >= l_text_start then - l_text_start := instr(a_source,'''',l_offset); + + -- Fast path: no special tokens on this line + if instr(l_line, '/') = 0 + and instr(l_line, '-') = 0 + and instr(l_line, '''') = 0 + then + l_result(i) := l_line; + continue; end if; - if l_offset >= l_escaped_text_start then - l_escaped_text_start := instr(a_source,q'[q']',l_offset); + + -- Normal scan + l_remaining := l_line; + l_line := scan_line(l_remaining, l_in_ml_comment); + if l_line is null then + l_line := ''; end if; + l_result(i) := l_line; end loop; - append_to_clob(l_result, substr(a_source, l_end)); + return l_result; - end; + end replace_multiline_comments; + + function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t) + return dbms_preprocessor.source_lines_t + is + l_result dbms_preprocessor.source_lines_t := a_source; + l_rebased dbms_preprocessor.source_lines_t; + l_create_line pls_integer; + l_header_line pls_integer; + l_header_pos pls_integer := 0; + begin + if l_result.count = 0 then + return l_result; + end if; + + -- remove comment lines that contain "-- create or replace" and find first CREATE + for i in 1..l_result.count loop + l_result(i) := regexp_replace(l_result(i), '^.*[-]{2,}\s*create(\s+or\s+replace).*$', null, 1, 1, 'i'); + if l_create_line is null and regexp_like(l_result(i), '(^|[[:space:]])create([[:space:]]|$)', 'i') then + l_create_line := i; + end if; + end loop; + + -- find first occurrence of object keyword after CREATE (may be on later line) + if l_create_line is not null then + for i in l_create_line..l_result.count loop + l_header_pos := regexp_instr( + l_result(i), + '(^|[[:space:]])(package|type|procedure|function)([[:space:]]|$)', + 1, 1, 0, 'i', 2 + ); + if l_header_pos > 0 then + l_header_line := i; + exit; + end if; + end loop; + + if l_header_line is not null then + -- keep from keyword onward on the header line + l_result(l_header_line) := substr(l_result(l_header_line), l_header_pos); + -- remove "OWNER." from create or replace statement. + -- Owner is not supported along with AUTHID - see issue https://github.com/utPLSQL/utPLSQL/issues/1088 + l_result(l_header_line) := regexp_replace( + l_result(l_header_line), + '^(package|type|procedure|function)\s+("?[[:alpha:]][[:alnum:]$#_]*"?\.)(.*)', + '\1 \3', 1, 1, 'ni' + ); + + -- rebase so header line becomes line 1 (matches preprocessor expectations) + for i in l_header_line .. l_result.count loop + l_rebased(i - l_header_line + 1) := l_result(i); + end loop; + return l_rebased; + end if; + end if; + + return l_result; + end strip_create_header_lines; function get_child_reporters(a_for_reporters ut_reporters_info := null) return ut_reporters_info is l_for_reporters ut_reporters_info := a_for_reporters; @@ -753,7 +897,7 @@ create or replace package body ut_utils is l_for_reporters := ut_reporters_info(ut_reporter_info('UT_REPORTER_BASE','N','N','N')); end if; - select /*+ cardinality(f 10) */ + select /*+ no_parallel cardinality(f 10) */ ut_reporter_info( object_name => t.type_name, is_output_reporter => @@ -787,9 +931,9 @@ create or replace package body ut_utils is /** * Change string into unicode to match xmlgen format _00_ + * See the section of Oracle documentation called: Escape of Characters in Generated XML Data * 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.11.3475-develop - */ + */ function char_to_xmlgen_unicode(a_character varchar2) return varchar2 is begin return '_x00'||rawtohex(utl_raw.cast_to_raw(a_character))||'_'; @@ -801,7 +945,7 @@ create or replace package body ut_utils is function build_valid_xml_name(a_preprocessed_name varchar2) return varchar2 is l_post_processed varchar2(4000); begin - for i in (select regexp_substr( a_preprocessed_name ,'(.{1})', 1, level, null, 1 ) AS string_char,level level_no + for i in (select /*+ no_parallel */ regexp_substr( a_preprocessed_name ,'(.{1})', 1, level, null, 1 ) AS string_char,level level_no from dual connect by level <= regexp_count(a_preprocessed_name, '(.{1})')) loop if i.level_no = 1 and regexp_like(i.string_char,gc_invalid_first_xml_char) then @@ -880,14 +1024,47 @@ create or replace package body ut_utils is return regexp_replace(a_item,a_prefix||a_connector); end; - function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is + function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash is begin - return dbms_crypto.hash(a_data, a_hash_type); + --We cannot run hash on null + return case when a_data is null then null else dbms_crypto.hash(a_data, a_hash_type) end; end; - function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash is + function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash is begin - return dbms_crypto.hash(a_data, a_hash_type); + --We cannot run hash on null + return case when a_data is null then null else dbms_crypto.hash(a_data, a_hash_type) end; + end; + + function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2 is + l_start_pos pls_integer := 1; + l_end_pos pls_integer := 1; + l_result varchar2(4000); + l_item varchar2(4000); + l_at_end boolean := false; + begin + if a_random_seed is null then + l_result := a_path; + end if; + if a_path is not null then + loop + l_end_pos := instr(a_path,'.',l_start_pos); + if l_end_pos = 0 then + l_end_pos := length(a_path)+1; + l_at_end := true; + end if; + l_item := substr(a_path,l_start_pos,l_end_pos-l_start_pos); + if l_item is not null then + l_result := + l_result || + ut_utils.get_hash( to_char( dbms_utility.get_hash_value( l_item, 1, a_random_seed ) ) ); + end if; + exit when l_at_end; + l_result := l_result || chr(0); + l_start_pos := l_end_pos + 1; + end loop; + end if; + return l_result; end; function qualified_sql_name(a_name varchar2) return varchar2 is @@ -899,5 +1076,95 @@ create or replace package body ut_utils is end; end; + function interval_to_text(a_interval dsinterval_unconstrained) return varchar2 is + l_day varchar2(100) := extract(day from a_interval); + l_hour varchar2(100) := extract(hour from a_interval); + l_minute varchar2(100) := extract(minute from a_interval); + l_second varchar2(100) := extract(second from a_interval); + l_result varchar2(32767); + begin + l_result := case + when l_day = 1 then l_day ||' day' + when l_day > 1 then l_day ||' days' + end || + case + when l_hour = 1 then ' '|| l_hour ||' hour' + when l_hour > 1 then ' '|| l_hour ||' hours' + end || + case + when l_minute = 1 then ' '||l_minute ||' minute' + when l_minute > 1 then ' '||l_minute ||' minutes' + end || + case + when l_second > 1 then ' '||l_second ||' seconds' + when l_second = 1 then ' '||l_second ||' second' + when l_second > 0 then ' '||l_second ||' seconds' + end; + l_result := + case + when a_interval is null then 'NULL' + when l_result is null then '0 seconds' + else ltrim(l_result,' ') + end; + + return l_result; + end; + + function interval_to_text(a_interval yminterval_unconstrained) return varchar2 is + l_year varchar2(4) := extract(year from a_interval); + l_month varchar2(20) := extract(month from a_interval); + l_result varchar2(32767); + begin + l_result := case + when l_year = 1 then l_year ||' year' + when l_year > 1 then l_year ||' years' + end || + case + when l_month > 1 then ' '||l_month ||' months' + when l_month = 1 then ' '||l_month ||' month' + end; + l_result := + case + when a_interval is null then 'NULL' + when l_result is null then '0 months' + else ltrim(l_result,' ') + end; + + return l_result; + end; + + + /* + * Inspired by + * https://stackoverflow.com/a/48782891 + */ + function lengthb_clob( a_clob clob) return integer is + l_blob blob; + l_desc_offset PLS_INTEGER := 1; + l_src_offset PLS_INTEGER := 1; + l_lang PLS_INTEGER := 0; + l_warning PLS_INTEGER := 0; + l_result integer; + begin + if a_clob = empty_clob() then + l_result := 0; + elsif a_clob is not null then + dbms_lob.createtemporary(l_blob,true); + dbms_lob.converttoblob + ( l_blob + , a_clob + , dbms_lob.getlength(a_clob) + , l_desc_offset + , l_src_offset + , dbms_lob.default_csid + , l_lang + , l_warning + ); + l_result := length(l_blob); + dbms_lob.freetemporary(l_blob); + end if; + return l_result; + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 3faa88f99..46c28f0f0 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -1,7 +1,7 @@ -create or replace package ut_utils authid definer is +create or replace noneditionable package ut_utils authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ create or replace package ut_utils authid definer is * */ - gc_version constant varchar2(50) := 'v3.1.11.3475-develop'; + gc_version constant varchar2(50) := 'v3.2.4.4532-develop'; subtype t_executable_type is varchar2(30); gc_before_all constant t_executable_type := 'beforeall'; @@ -121,15 +121,19 @@ create or replace package ut_utils authid definer is ex_failed_open_cur exception; gc_failed_open_cur constant pls_integer := -20218; pragma exception_init (ex_failed_open_cur, -20218); + + ex_invalid_tag_expression exception; + gc_invalid_tag_expression constant pls_integer := -20219; + pragma exception_init (ex_invalid_tag_expression, -20219); gc_max_storage_varchar2_len constant integer := 4000; gc_max_output_string_length constant integer := 4000; gc_more_data_string constant varchar2(5) := '[...]'; gc_more_data_string_len constant integer := length( gc_more_data_string ); gc_number_format constant varchar2(100) := 'TM9'; - gc_date_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ss'; - gc_timestamp_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff'; - gc_timestamp_tz_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm'; + gc_date_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ss'; + gc_timestamp_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff'; + gc_timestamp_tz_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm'; gc_null_string constant varchar2(4) := 'NULL'; gc_empty_string constant varchar2(5) := 'EMPTY'; @@ -401,7 +405,15 @@ create or replace package ut_utils authid definer is /** * Replaces multi-line comments in given source-code with empty lines */ - function replace_multiline_comments(a_source clob) return clob; + function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t) + return dbms_preprocessor.source_lines_t; + + /** + * Strips the CREATE header (possibly split across lines) so source starts at + * package/type/procedure/function keyword, preserving line numbers. + */ + function strip_create_header_lines(a_source dbms_preprocessor.source_lines_t) + return dbms_preprocessor.source_lines_t; /** * Returns list of sub-type reporters for given list of super-type reporters @@ -445,18 +457,38 @@ create or replace package ut_utils authid definer is /* * Wrapper function for calling dbms_crypto.hash */ - function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash; + function get_hash(a_data raw, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash; /* * Wrapper function for calling dbms_crypto.hash */ - function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh1) return t_hash; + function get_hash(a_data clob, a_hash_type binary_integer := dbms_crypto.hash_sh256) return t_hash; + + /* + * Returns a hash value of suitepath based on input path and random seed + */ + function hash_suite_path(a_path varchar2, a_random_seed positiven) return varchar2; /* * Verifies that the input string is a qualified SQL name using sys.dbms_assert.qualified_sql_name * If null value passed returns null */ function qualified_sql_name(a_name varchar2) return varchar2; + + /* + * Return value of interval in plain english + */ + function interval_to_text(a_interval dsinterval_unconstrained) return varchar2; + + /* + * Return value of interval in plain english + */ + function interval_to_text(a_interval yminterval_unconstrained) return varchar2; + /* + * Return length of CLOB in bytes. Null for NULL + */ + function lengthb_clob( a_clob clob) return integer; + end ut_utils; / diff --git a/source/create_grants.sql b/source/create_grants.sql index db26faa24..9318a7220 100644 --- a/source/create_grants.sql +++ b/source/create_grants.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -60,6 +60,7 @@ grant execute on &&ut3_owner..ut_file_mapping to &ut3_user; grant execute on &&ut3_owner..ut_file_mapper to &ut3_user; grant execute on &&ut3_owner..ut_suite_items_info to &ut3_user; grant execute on &&ut3_owner..ut_suite_item_info to &ut3_user; +grant execute on &&ut3_owner..ut_suite_cache_rows to &ut3_user; grant execute on &&ut3_owner..ut_run_info to &ut3_user; grant execute on &&ut3_owner..ut_coverage_options to &ut3_user; @@ -88,6 +89,8 @@ grant execute on &&ut3_owner..ut_be_like to &ut3_user; grant execute on &&ut3_owner..ut_be_not_null to &ut3_user; grant execute on &&ut3_owner..ut_be_null to &ut3_user; grant execute on &&ut3_owner..ut_be_true to &ut3_user; +grant execute on &&ut3_owner..ut_be_within to &ut3_user; +grant execute on &&ut3_owner..ut_be_within_pct to &ut3_user; grant execute on &&ut3_owner..ut_contain to &ut3_user; grant execute on &&ut3_owner..ut_equal to &ut3_user; grant execute on &&ut3_owner..ut_have_count to &ut3_user; @@ -101,10 +104,10 @@ grant execute on &&ut3_owner..ut_tfs_junit_reporter to &ut3_user; grant execute on &&ut3_owner..ut_documentation_reporter to &ut3_user; grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user; grant execute on &&ut3_owner..ut_realtime_reporter to &ut3_user; +grant execute on &&ut3_owner..ut_tap_reporter to &ut3_user; --reporters - coverage grant execute on &&ut3_owner..ut_coverage_html_reporter to &ut3_user; grant execute on &&ut3_owner..ut_coverage_sonar_reporter to &ut3_user; -grant execute on &&ut3_owner..ut_coveralls_reporter to &ut3_user; grant execute on &&ut3_owner..ut_coverage_cobertura_reporter to &ut3_user; --reporters - debug grant execute on &&ut3_owner..ut_debug_reporter to &ut3_user; @@ -120,6 +123,7 @@ grant execute on &&ut3_owner..ut_console_reporter_base to &ut3_user; grant execute on &&ut3_owner..ut_output_buffer_base to &ut3_user; grant execute on &&ut3_owner..ut_output_table_buffer to &ut3_user; grant execute on &&ut3_owner..ut_output_clob_table_buffer to &ut3_user; +grant execute on &&ut3_owner..ut_output_bulk_buffer to &ut3_user; --needed internally for selecting from annotation objects within packages that use invoker rights grant execute on &&ut3_owner..ut_annotation_objs_cache_info to &ut3_user; diff --git a/source/create_synonyms.sql b/source/create_synonyms.sql index 3f18ba3ed..ab338f4f7 100644 --- a/source/create_synonyms.sql +++ b/source/create_synonyms.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -36,10 +36,10 @@ set termout off spool params.sql.tmp select case - when upper('&&ut3_user') = 'PUBLIC' then q'[define action_type='or replace public' + when upper('&&ut3_user') = 'PUBLIC' then q'[define action_type='or replace noneditionable public' ]'||q'[define ut3_user='' ]'||q'[define grantee='PUBLIC']' - else q'[define action_type='or replace' + else q'[define action_type='or replace noneditionable' ]'||q'[define grantee='&&ut3_user'] ]'||q'[define ut3_user='&&ut3_user..']' end @@ -104,6 +104,8 @@ create &action_type. synonym &ut3_user.be_like for &&ut3_owner..be_like; create &action_type. synonym &ut3_user.be_not_null for &&ut3_owner..be_not_null; create &action_type. synonym &ut3_user.be_null for &&ut3_owner..be_null; create &action_type. synonym &ut3_user.be_true for &&ut3_owner..be_true; +create &action_type. synonym &ut3_user.be_within for &&ut3_owner..be_within; +create &action_type. synonym &ut3_user.be_within_pct for &&ut3_owner..be_within_pct; create &action_type. synonym &ut3_user.contain for &&ut3_owner..contain; create &action_type. synonym &ut3_user.equal for &&ut3_owner..equal; create &action_type. synonym &ut3_user.have_count for &&ut3_owner..have_count; @@ -117,10 +119,10 @@ create &action_type. synonym &ut3_user.ut_tfs_junit_reporter for &&ut3_owner..ut create &action_type. synonym &ut3_user.ut_documentation_reporter for &&ut3_owner..ut_documentation_reporter; create &action_type. synonym &ut3_user.ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter; create &action_type. synonym &ut3_user.ut_realtime_reporter for &&ut3_owner..ut_realtime_reporter; +create &action_type. synonym &ut3_user.ut_tap_reporter for &&ut3_owner..ut_tap_reporter; --reporters - coverage create &action_type. synonym &ut3_user.ut_coverage_html_reporter for &&ut3_owner..ut_coverage_html_reporter; create &action_type. synonym &ut3_user.ut_coverage_sonar_reporter for &&ut3_owner..ut_coverage_sonar_reporter; -create &action_type. synonym &ut3_user.ut_coveralls_reporter for &&ut3_owner..ut_coveralls_reporter; create &action_type. synonym &ut3_user.ut_coverage_cobertura_reporter for &&ut3_owner..ut_coverage_cobertura_reporter; --reporters - debug create &action_type. synonym &ut3_user.ut_debug_reporter for &&ut3_owner..ut_debug_reporter; diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index 6d43c6b7a..25f14217e 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/create_user_grants.sql b/source/create_user_grants.sql index 4ca721cf4..c1b81efa4 100644 --- a/source/create_user_grants.sql +++ b/source/create_user_grants.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/create_user_synonyms.sql b/source/create_user_synonyms.sql index ee9f60fdd..7b0e49553 100644 --- a/source/create_user_synonyms.sql +++ b/source/create_user_synonyms.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/create_utplsql_owner.sql b/source/create_utplsql_owner.sql index 81325e925..a5b70ffda 100644 --- a/source/create_utplsql_owner.sql +++ b/source/create_utplsql_owner.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -31,16 +31,13 @@ create user &ut3_owner_schema identified by "&ut3_password" default tablespace & grant create session, create sequence, create procedure, create type, create table, create view, create synonym to &ut3_owner_schema; -begin - $if dbms_db_version.version < 18 $then - execute immediate 'grant execute on dbms_lock to &ut3_owner_schema'; - $else - null; - $end -end; -/ - +grant execute on dbms_lock to &ut3_owner_schema; grant execute on dbms_crypto to &ut3_owner_schema; +grant execute on dbms_lob to &ut3_owner_schema; +grant execute on dbms_xmlgen to &ut3_owner_schema; +grant execute on dbms_sql to &ut3_owner_schema; +grant execute on dbms_random to &ut3_owner_schema; + grant alter session to &ut3_owner_schema; diff --git a/source/define_ut3_owner_param.sql b/source/define_ut3_owner_param.sql index 1b183b581..192e810b7 100644 --- a/source/define_ut3_owner_param.sql +++ b/source/define_ut3_owner_param.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_compound_data_diff_tmp.sql b/source/expectations/data_values/ut_compound_data_diff_tmp.sql index e96d716b5..edd27f766 100644 --- a/source/expectations/data_values/ut_compound_data_diff_tmp.sql +++ b/source/expectations/data_values/ut_compound_data_diff_tmp.sql @@ -1,7 +1,7 @@ create global temporary table ut_compound_data_diff_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb index 69ba90811..763c133d7 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pkb +++ b/source/expectations/data_values/ut_compound_data_helper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_compound_data_helper is +create or replace noneditionable package body ut_compound_data_helper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ create or replace package body ut_compound_data_helper is where x.data_id = :act_guid ) ucd ) - select + select /*+ no_parallel */ a."UT3$_Item#Data" as act_item_data, a."UT3$_Data#Id" act_data_id, e."UT3$_Item#Data" as exp_item_data, @@ -103,7 +103,7 @@ create or replace package body ut_compound_data_helper is full outer join actual_cols a on e.exp_column_name = a.act_column_name ) - select case + select /*+ no_parallel */ case when exp_col_pos is null and act_col_pos is not null then '+' when exp_col_pos is not null and act_col_pos is null then '-' when exp_col_type_compare != act_col_type_compare then 't' @@ -278,7 +278,7 @@ create or replace package body ut_compound_data_helper is begin if a_data_info is not empty then for i in 1..a_data_info.count loop - if a_data_info(i).has_nested_col = 0 then + if a_data_info(i).has_nested_col = 0 and a_data_info(i).column_type <> 'OBJECT' then --Get XMLTABLE column list add_element_to_list(l_xmltab_list,generate_xmltab_stmt(a_data_info(i))); --Get Select statment list of columns @@ -300,8 +300,8 @@ create or replace package body ut_compound_data_helper is ut_utils.append_to_clob(a_partition_stmt,' row_number() over (partition by '||l_partition_tmp||' order by '||l_partition_tmp||' ) '); if a_pk_table.count > 0 then - -- If key defined do the join or these and where on diffrences - a_join_by_stmt := ut_utils.table_to_clob(l_join_by_list, ' and '); + -- If key defined do the join or these and where on diffrences as well as on duplicate number when rows are same. + a_join_by_stmt := ' e."UT3$_Dup#No" = a."UT3$_Dup#No" and '||ut_utils.table_to_clob(l_join_by_list, ' and '); elsif a_unordered then -- If no key defined do the join on all columns a_join_by_stmt := ' e."UT3$_Dup#No" = a."UT3$_Dup#No" and '||ut_utils.table_to_clob(l_equal_list, ' and '); @@ -398,8 +398,12 @@ create or replace package body ut_compound_data_helper is l_column_list ut_varchar2_list := ut_varchar2_list(); begin for i in 1..a_cursor_info.count loop - l_column_list.extend; - l_column_list(l_column_list.last) := a_cursor_info(i).access_path; + --This avoids extracting single columns from nested objects. + --as we can go down to any level but we will lose visibility of parent. + if a_cursor_info(i).hierarchy_level = 1 then + l_column_list.extend; + l_column_list(l_column_list.last) := a_cursor_info(i).access_path; + end if; end loop; return l_column_list; end; @@ -486,7 +490,7 @@ create or replace package body ut_compound_data_helper is ) unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') ) ) - select rn, diff_type, diffed_row, pk_value + select /*+ no_parallel */ 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, @@ -564,7 +568,7 @@ create or replace package body ut_compound_data_helper is procedure insert_diffs_result(a_diff_tab t_diff_tab, a_diff_id raw) is begin forall idx in 1..a_diff_tab.count save exceptions - insert into ut_compound_data_diff_tmp + insert /*+ no_parallel */ into ut_compound_data_diff_tmp ( diff_id, act_item_data, act_data_id, exp_item_data, exp_data_id, item_no, duplicate_no ) values (a_diff_id, @@ -639,13 +643,13 @@ create or replace package body ut_compound_data_helper is procedure save_cursor_data_for_diff(a_data_id raw, a_set_id integer, a_xml xmltype) is begin - insert into ut_compound_data_tmp (data_id, item_no, item_data) values (a_data_id, a_set_id, a_xml); + insert /*+ no_parallel */ into ut_compound_data_tmp (data_id, item_no, item_data) values (a_data_id, a_set_id, a_xml); end; function get_row_data_as_xml(a_data_id raw, a_max_rows integer) return ut_utils.t_clob_tab is l_results ut_utils.t_clob_tab; begin - select xmlserialize( content ucd.item_data no indent) + select /*+ no_parallel */ xmlserialize( content ucd.item_data no indent) bulk collect into l_results from ut_compound_data_tmp tmp ,xmltable ( '/ROWSET' passing tmp.item_data @@ -720,7 +724,7 @@ create or replace package body ut_compound_data_helper is or (a.json_type != e.json_type) or (decode(a.element_value,e.element_value,1,0) = 0) ) - select difference_type, + select /*+ no_parallel */ difference_type, act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path bulk collect into l_result_diff @@ -742,7 +746,7 @@ create or replace package body ut_compound_data_helper is l_diffs tt_json_diff_tab := compare_json_data(a_act_json_data,a_exp_json_data); begin forall i in 1..l_diffs.count - insert into ut_json_data_diff_tmp ( + insert /*+ no_parallel */ into ut_json_data_diff_tmp ( diff_id, difference_type, act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path @@ -759,7 +763,7 @@ create or replace package body ut_compound_data_helper is function get_json_diffs_type(a_diff_id raw) return tt_json_diff_type_tab is l_diffs_summary tt_json_diff_type_tab := tt_json_diff_type_tab(); begin - select d.difference_type,count(1) + select /*+ no_parallel */ d.difference_type,count(1) bulk collect into l_diffs_summary from ut_json_data_diff_tmp d where diff_id = a_diff_id @@ -771,7 +775,7 @@ create or replace package body ut_compound_data_helper is function get_json_diffs_tmp(a_diff_id raw) return tt_json_diff_tab is l_diffs tt_json_diff_tab; begin - select difference_type, + select /*+ no_parallel */ difference_type, act_element_name, act_element_value, act_json_type, act_access_path, act_parent_path, exp_element_name, exp_element_value, exp_json_type, exp_access_path, exp_parent_path bulk collect into l_diffs @@ -781,6 +785,15 @@ create or replace package body ut_compound_data_helper is return l_diffs; end; + function get_json_object(a_json_t json) return json_element_t is + l_obj json_element_t; + begin + $if dbms_db_version.version >= 21 $then + l_obj := case when a_json_t is null then cast (null as json_element_t ) else json_element_t.parse(json_query(a_json_t, '$' returning clob)) end; + $end + return l_obj; + end; + begin g_anytype_name_map(dbms_types.typecode_date) := 'DATE'; g_anytype_name_map(dbms_types.typecode_number) := 'NUMBER'; diff --git a/source/expectations/data_values/ut_compound_data_helper.pks b/source/expectations/data_values/ut_compound_data_helper.pks index b39df0039..6069920c9 100644 --- a/source/expectations/data_values/ut_compound_data_helper.pks +++ b/source/expectations/data_values/ut_compound_data_helper.pks @@ -1,7 +1,7 @@ -create or replace package ut_compound_data_helper authid definer is +create or replace noneditionable package ut_compound_data_helper authid definer is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -134,5 +134,7 @@ create or replace package ut_compound_data_helper authid definer is function get_json_diffs_type(a_diff_id raw) return tt_json_diff_type_tab; + function get_json_object(a_json_t json) return json_element_t; + end; / diff --git a/source/expectations/data_values/ut_compound_data_tmp.sql b/source/expectations/data_values/ut_compound_data_tmp.sql index c860a1872..01cedd97b 100644 --- a/source/expectations/data_values/ut_compound_data_tmp.sql +++ b/source/expectations/data_values/ut_compound_data_tmp.sql @@ -1,7 +1,7 @@ create global temporary table ut_compound_data_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/expectations/data_values/ut_compound_data_value.tpb b/source/expectations/data_values/ut_compound_data_value.tpb index 627e14680..7db47827e 100644 --- a/source/expectations/data_values/ut_compound_data_value.tpb +++ b/source/expectations/data_values/ut_compound_data_value.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_compound_data_value as +create or replace noneditionable type body ut_compound_data_value as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_compound_data_value.tps b/source/expectations/data_values/ut_compound_data_value.tps index 027b5ca26..c84a59db6 100644 --- a/source/expectations/data_values/ut_compound_data_value.tps +++ b/source/expectations/data_values/ut_compound_data_value.tps @@ -1,7 +1,7 @@ -create or replace type ut_compound_data_value force under ut_data_value( +create or replace noneditionable type ut_compound_data_value force under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_cursor_column.tpb b/source/expectations/data_values/ut_cursor_column.tpb index 13b1b3158..3513de23e 100644 --- a/source/expectations/data_values/ut_cursor_column.tpb +++ b/source/expectations/data_values/ut_cursor_column.tpb @@ -1,5 +1,21 @@ -create or replace type body ut_cursor_column as - +create or replace noneditionable type body ut_cursor_column as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + member procedure init( self in out nocopy ut_cursor_column, a_col_name varchar2, a_col_schema_name varchar2, @@ -14,8 +30,11 @@ create or replace type body ut_cursor_column as self.column_len := a_col_max_len; --length of column self.column_precision := a_col_precision; self.column_scale := a_col_scale; - self.column_name := TRIM( BOTH '''' FROM a_col_name); --name of the column self.column_type_name := coalesce(a_col_type_name,a_col_type); --type name e.g. test_dummy_object or varchar2 + self.column_name := case when a_col_name is null and a_collection = 1 then + self.column_type_name + else TRIM( BOTH '''' FROM a_col_name) + end; --name of the column, however in nested object for collection name is not defined in cursor. self.xml_valid_name := ut_utils.get_valid_xml_name(self.column_name); self.display_path := case when a_access_path is null then self.column_name @@ -25,15 +44,16 @@ create or replace type body ut_cursor_column as self.access_path := case when a_access_path is null then self.xml_valid_name else - a_access_path||'/'||self.xml_valid_name + a_access_path||'/'||self.xml_valid_name end; --Access path used for XMLTABLE query self.filter_path := '/'||self.access_path; --Filter path will differ from access path in anydata type + --Transformed name needs to be build on full access path to avoid ambiguity when there is 3 or more levels of nesting. self.transformed_name := case when length(self.xml_valid_name) > 30 then - '"'||ut_compound_data_helper.get_fixed_size_hash(self.parent_name||self.xml_valid_name)||'"' + '"'||ut_compound_data_helper.get_fixed_size_hash(self.access_path)||'"' when self.parent_name is null then '"'||self.xml_valid_name||'"' else - '"'||ut_compound_data_helper.get_fixed_size_hash(self.parent_name||self.xml_valid_name)||'"' + '"'||ut_compound_data_helper.get_fixed_size_hash(self.access_path)||'"' end; --when is nestd we need to hash name to make sure we dont exceed 30 char self.column_type := a_col_type; --column type e.g. user_defined , varchar2 self.column_schema := a_col_schema_name; -- schema name diff --git a/source/expectations/data_values/ut_cursor_column.tps b/source/expectations/data_values/ut_cursor_column.tps index c865ea64b..fcf3fe002 100644 --- a/source/expectations/data_values/ut_cursor_column.tps +++ b/source/expectations/data_values/ut_cursor_column.tps @@ -1,7 +1,7 @@ -create or replace type ut_cursor_column authid current_user as object ( +create or replace noneditionable type ut_cursor_column authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_cursor_column_tab.tps b/source/expectations/data_values/ut_cursor_column_tab.tps index b1b33f7fe..169ba3ce4 100644 --- a/source/expectations/data_values/ut_cursor_column_tab.tps +++ b/source/expectations/data_values/ut_cursor_column_tab.tps @@ -1,7 +1,7 @@ -create or replace type ut_cursor_column_tab as +create or replace noneditionable type ut_cursor_column_tab as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_cursor_details.tpb b/source/expectations/data_values/ut_cursor_details.tpb index 20313ed96..ff204e3bd 100644 --- a/source/expectations/data_values/ut_cursor_details.tpb +++ b/source/expectations/data_values/ut_cursor_details.tpb @@ -1,9 +1,26 @@ -create or replace type body ut_cursor_details as +create or replace noneditionable type body ut_cursor_details as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + member function equals( a_other ut_cursor_details, a_match_options ut_matcher_options ) return boolean is l_diffs integer; begin - select count(1) into l_diffs + select /*+ no_parallel */ count(1) into l_diffs from table(self.cursor_columns_info) a full outer join table(a_other.cursor_columns_info) e on decode(a.parent_name,e.parent_name,1,0)= 1 @@ -91,7 +108,11 @@ create or replace type body ut_cursor_details as a_cursor_number in number ) return self as result is l_columns_count pls_integer; + $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then l_columns_desc dbms_sql.desc_tab3; + $else + l_columns_desc dbms_sql.desc_tab4; + $end l_is_collection boolean; l_hierarchy_level integer := 1; begin @@ -139,7 +160,7 @@ create or replace type body ut_cursor_details as member function contains_collection return boolean is l_collection_elements number; begin - select count(1) into l_collection_elements + select /*+ no_parallel */ count(1) into l_collection_elements from table(cursor_columns_info) c where c.is_collection = 1 and rownum = 1; return l_collection_elements > 0; @@ -149,7 +170,7 @@ create or replace type body ut_cursor_details as l_result ut_varchar2_list; begin --regexp_replace(c.access_path,'^\/?([^\/]+\/){1}') - select fl.column_value + select /*+ no_parallel */ fl.column_value bulk collect into l_result from table(a_expected_columns) fl where not exists ( @@ -179,7 +200,7 @@ create or replace type body ut_cursor_details as select regexp_replace( column_value, c_xpath_extract_reg, '\5' ) col_names from table(a_match_options.exclude.items) ) - select value(x) + select /*+ no_parallel */ value(x) bulk collect into l_result.cursor_columns_info from table(self.cursor_columns_info) x where exists( @@ -192,7 +213,7 @@ create or replace type body ut_cursor_details as select regexp_replace( column_value, c_xpath_extract_reg, '\5' ) col_names from table(a_match_options.exclude.items) ) - select value(x) + select /*+ no_parallel */ value(x) bulk collect into l_result.cursor_columns_info from table(self.cursor_columns_info) x where not exists( @@ -202,7 +223,7 @@ create or replace type body ut_cursor_details as --Rewrite column order after columns been excluded for i in ( - select parent_name, access_path, display_path, has_nested_col, + select /*+ no_parallel */ parent_name, access_path, display_path, has_nested_col, transformed_name, hierarchy_level, rownum as new_position, xml_valid_name, column_name, column_type, column_type_name, column_schema, @@ -224,7 +245,7 @@ create or replace type body ut_cursor_details as member function get_xml_children(a_parent_name varchar2 := null) return xmltype is l_result xmltype; begin - select xmlagg(xmlelement(evalname t.column_name,t.column_type_name)) + select /*+ no_parallel */ xmlagg(xmlelement(evalname t.column_name,t.column_type_name)) into l_result from table(self.cursor_columns_info) t where (a_parent_name is null and parent_name is null and hierarchy_level = 1 and column_name is not null) @@ -236,7 +257,7 @@ create or replace type body ut_cursor_details as l_root varchar2(250); begin if self.cursor_columns_info.count > 0 then - select x.access_path into l_root from table(self.cursor_columns_info) x + select /*+ no_parallel */ x.access_path into l_root from table(self.cursor_columns_info) x where x.hierarchy_level = 1; else l_root := null; diff --git a/source/expectations/data_values/ut_cursor_details.tps b/source/expectations/data_values/ut_cursor_details.tps index 7092e834f..c50ed16d3 100644 --- a/source/expectations/data_values/ut_cursor_details.tps +++ b/source/expectations/data_values/ut_cursor_details.tps @@ -1,7 +1,7 @@ -create or replace type ut_cursor_details authid current_user as object ( +create or replace noneditionable type ut_cursor_details authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value.tpb b/source/expectations/data_values/ut_data_value.tpb index a644fe30e..05eb3022e 100644 --- a/source/expectations/data_values/ut_data_value.tpb +++ b/source/expectations/data_values/ut_data_value.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value as +create or replace noneditionable type body ut_data_value as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value.tps b/source/expectations/data_values/ut_data_value.tps index ca7e18308..6e362a56d 100644 --- a/source/expectations/data_values/ut_data_value.tps +++ b/source/expectations/data_values/ut_data_value.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value force authid current_user as object ( +create or replace noneditionable type ut_data_value force authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ create or replace type ut_data_value force authid current_user as object ( limitations under the License. */ data_type varchar2(250 char), + data_type_plsql varchar2(250 char), self_type varchar2(250 char), not instantiable member function is_null return boolean, not instantiable member function to_string return varchar2, diff --git a/source/expectations/data_values/ut_data_value_anydata.tpb b/source/expectations/data_values/ut_data_value_anydata.tpb index a81b0f5c7..9fccb7582 100644 --- a/source/expectations/data_values/ut_data_value_anydata.tpb +++ b/source/expectations/data_values/ut_data_value_anydata.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_anydata as +create or replace noneditionable type body ut_data_value_anydata as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -48,11 +48,11 @@ create or replace type body ut_data_value_anydata as begin l_status := l_value.get'||self.compound_type||'(l_data); '|| case when self.compound_type = 'collection' then - q'[ open :l_tmp_refcursor for select value(x) as "]'|| + q'[ open :l_tmp_refcursor for select /*+ no_parallel */ value(x) as "]'|| ut_metadata.get_object_name(ut_metadata.get_collection_element(a_data_value))|| q'[" from table(l_data) x;]' else - q'[ open :l_tmp_refcursor for select l_data as "]'||ut_metadata.get_object_name(self.data_type)|| + q'[ open :l_tmp_refcursor for select /*+ no_parallel */ l_data as "]'||ut_metadata.get_object_name(self.data_type)|| q'[" from dual;]' end || 'end;'; diff --git a/source/expectations/data_values/ut_data_value_anydata.tps b/source/expectations/data_values/ut_data_value_anydata.tps index 5ed0406ca..6ad692d31 100644 --- a/source/expectations/data_values/ut_data_value_anydata.tps +++ b/source/expectations/data_values/ut_data_value_anydata.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_anydata under ut_data_value_refcursor( +create or replace noneditionable type ut_data_value_anydata under ut_data_value_refcursor( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_blob.tpb b/source/expectations/data_values/ut_data_value_blob.tpb index 11af6d2f7..c961ebaab 100644 --- a/source/expectations/data_values/ut_data_value_blob.tpb +++ b/source/expectations/data_values/ut_data_value_blob.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_blob as +create or replace noneditionable type body ut_data_value_blob as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_blob as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'blob'; + self.data_type_plsql := 'blob'; return; end; diff --git a/source/expectations/data_values/ut_data_value_blob.tps b/source/expectations/data_values/ut_data_value_blob.tps index f557ffbce..94124bfec 100644 --- a/source/expectations/data_values/ut_data_value_blob.tps +++ b/source/expectations/data_values/ut_data_value_blob.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_blob under ut_data_value( +create or replace noneditionable type ut_data_value_blob under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_boolean.tpb b/source/expectations/data_values/ut_data_value_boolean.tpb index 0645d3269..dd253442c 100644 --- a/source/expectations/data_values/ut_data_value_boolean.tpb +++ b/source/expectations/data_values/ut_data_value_boolean.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_boolean as +create or replace noneditionable type body ut_data_value_boolean as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_boolean as self.data_value := ut_utils.boolean_to_int(a_value); self.self_type := $$plsql_unit; self.data_type := 'boolean'; + self.data_type_plsql := 'boolean'; return; end; diff --git a/source/expectations/data_values/ut_data_value_boolean.tps b/source/expectations/data_values/ut_data_value_boolean.tps index a254e05c0..b6df4e0b4 100644 --- a/source/expectations/data_values/ut_data_value_boolean.tps +++ b/source/expectations/data_values/ut_data_value_boolean.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_boolean under ut_data_value( +create or replace noneditionable type ut_data_value_boolean under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_clob.tpb b/source/expectations/data_values/ut_data_value_clob.tpb index dacb390b6..76a55545e 100644 --- a/source/expectations/data_values/ut_data_value_clob.tpb +++ b/source/expectations/data_values/ut_data_value_clob.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_clob as +create or replace noneditionable type body ut_data_value_clob as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_clob as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'clob'; + self.data_type_plsql := 'clob'; return; end; diff --git a/source/expectations/data_values/ut_data_value_clob.tps b/source/expectations/data_values/ut_data_value_clob.tps index 1ff5d5623..686757e85 100644 --- a/source/expectations/data_values/ut_data_value_clob.tps +++ b/source/expectations/data_values/ut_data_value_clob.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_clob under ut_data_value( +create or replace noneditionable type ut_data_value_clob under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_date.tpb b/source/expectations/data_values/ut_data_value_date.tpb index dd87156bc..2a1232a1c 100644 --- a/source/expectations/data_values/ut_data_value_date.tpb +++ b/source/expectations/data_values/ut_data_value_date.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_date as +create or replace noneditionable type body ut_data_value_date as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_date as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'date'; + self.data_type_plsql := 'date'; return; end; diff --git a/source/expectations/data_values/ut_data_value_date.tps b/source/expectations/data_values/ut_data_value_date.tps index 4435a3512..0bb9a17cf 100644 --- a/source/expectations/data_values/ut_data_value_date.tps +++ b/source/expectations/data_values/ut_data_value_date.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_date under ut_data_value( +create or replace noneditionable type ut_data_value_date under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_dsinterval.tpb b/source/expectations/data_values/ut_data_value_dsinterval.tpb index ff9d5fe1e..adad0ef6d 100644 --- a/source/expectations/data_values/ut_data_value_dsinterval.tpb +++ b/source/expectations/data_values/ut_data_value_dsinterval.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_dsinterval as +create or replace noneditionable type body ut_data_value_dsinterval as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_dsinterval as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'interval day to second'; + self.data_type_plsql := 'dsinterval_unconstrained'; return; end; @@ -31,7 +32,7 @@ create or replace type body ut_data_value_dsinterval as overriding member function to_string return varchar2 is begin - return ut_utils.to_string(self.data_value); + return ut_utils.interval_to_text(self.data_value); end; overriding member function compare_implementation(a_other ut_data_value) return integer is diff --git a/source/expectations/data_values/ut_data_value_dsinterval.tps b/source/expectations/data_values/ut_data_value_dsinterval.tps index 114619bf2..3d15e2e09 100644 --- a/source/expectations/data_values/ut_data_value_dsinterval.tps +++ b/source/expectations/data_values/ut_data_value_dsinterval.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_dsinterval under ut_data_value( +create or replace noneditionable type ut_data_value_dsinterval under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ create or replace type ut_data_value_dsinterval under ut_data_value( See the License for the specific language governing permissions and limitations under the License. */ - data_value dsinterval_unconstrained, + data_value interval day(9) to second(9), constructor function ut_data_value_dsinterval(self in out nocopy ut_data_value_dsinterval, a_value dsinterval_unconstrained) return self as result, overriding member function is_null return boolean, overriding member function to_string return varchar2, diff --git a/source/expectations/data_values/ut_data_value_json.tpb b/source/expectations/data_values/ut_data_value_json.tpb index 83916c30e..e1b5aa995 100644 --- a/source/expectations/data_values/ut_data_value_json.tpb +++ b/source/expectations/data_values/ut_data_value_json.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_json as +create or replace noneditionable type body ut_data_value_json as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -32,6 +32,13 @@ create or replace type body ut_data_value_json as return; end; + constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json) return self as result is + l_value json_element_t := ut_compound_data_helper.get_json_object(a_value); + begin + init(l_value); + return; + end; + overriding member function is_null return boolean is begin return (ut_utils.int_to_boolean(self.is_data_null)); @@ -140,7 +147,7 @@ create or replace type body ut_data_value_json as l_result := case when ut_compound_data_helper.insert_json_diffs( - l_diff_id, self.json_tree.json_tree_info, l_other.json_tree.json_tree_info + l_diff_id, l_other.json_tree.json_tree_info, self.json_tree.json_tree_info ) > 0 then 1 else 0 end; diff --git a/source/expectations/data_values/ut_data_value_json.tps b/source/expectations/data_values/ut_data_value_json.tps index bfaccb2e2..c89088564 100644 --- a/source/expectations/data_values/ut_data_value_json.tps +++ b/source/expectations/data_values/ut_data_value_json.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_json under ut_compound_data_value( +create or replace noneditionable type ut_data_value_json under ut_compound_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ create or replace type ut_data_value_json under ut_compound_data_value( json_tree ut_json_tree_details, member procedure init (self in out nocopy ut_data_value_json, a_value json_element_t), constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json_element_t) return self as result, + constructor function ut_data_value_json(self in out nocopy ut_data_value_json, a_value json) return self as result, overriding member function is_null return boolean, overriding member function is_empty return boolean, overriding member function to_string return varchar2, diff --git a/source/expectations/data_values/ut_data_value_number.tpb b/source/expectations/data_values/ut_data_value_number.tpb index b8a28a124..3ea08bbd4 100644 --- a/source/expectations/data_values/ut_data_value_number.tpb +++ b/source/expectations/data_values/ut_data_value_number.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_number as +create or replace noneditionable type body ut_data_value_number as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_number as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'number'; + self.data_type_plsql := 'number'; return; end; diff --git a/source/expectations/data_values/ut_data_value_number.tps b/source/expectations/data_values/ut_data_value_number.tps index 872ab405e..e7031de72 100644 --- a/source/expectations/data_values/ut_data_value_number.tps +++ b/source/expectations/data_values/ut_data_value_number.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_number under ut_data_value( +create or replace noneditionable type ut_data_value_number under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_refcursor.tpb b/source/expectations/data_values/ut_data_value_refcursor.tpb index 801b35802..b7f79ac52 100644 --- a/source/expectations/data_values/ut_data_value_refcursor.tpb +++ b/source/expectations/data_values/ut_data_value_refcursor.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_refcursor as +create or replace noneditionable type body ut_data_value_refcursor as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -81,6 +81,7 @@ create or replace type body ut_data_value_refcursor as self.self_type := $$plsql_unit; self.data_id := sys_guid(); self.data_type := 'refcursor'; + self.data_type_plsql := 'sys_refcursor'; self.compound_type := 'refcursor'; self.extract_path := '/*'; ut_compound_data_helper.cleanup_diff; @@ -178,7 +179,7 @@ create or replace type body ut_data_value_refcursor as l_missing_cols(l_missing_cols.last) := coalesce(a_column_diffs(i).expected_name, a_column_diffs(i).actual_name); end if; end loop; - select value(i) bulk collect into l_result + select /*+ no_parallel */ value(i) bulk collect into l_result from table(a_cursor_details) i where i.access_path not in ( select c.column_value diff --git a/source/expectations/data_values/ut_data_value_refcursor.tps b/source/expectations/data_values/ut_data_value_refcursor.tps index 4de253b8f..90a305d32 100644 --- a/source/expectations/data_values/ut_data_value_refcursor.tps +++ b/source/expectations/data_values/ut_data_value_refcursor.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_refcursor under ut_compound_data_value( +create or replace noneditionable type ut_data_value_refcursor under ut_compound_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_timestamp.tpb b/source/expectations/data_values/ut_data_value_timestamp.tpb index adb18e360..c6aabf7a3 100644 --- a/source/expectations/data_values/ut_data_value_timestamp.tpb +++ b/source/expectations/data_values/ut_data_value_timestamp.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_timestamp as +create or replace noneditionable type body ut_data_value_timestamp as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'timestamp'; + self.data_type_plsql := 'timestamp_unconstrained'; return; end; diff --git a/source/expectations/data_values/ut_data_value_timestamp.tps b/source/expectations/data_values/ut_data_value_timestamp.tps index ebd00762c..938b0d63c 100644 --- a/source/expectations/data_values/ut_data_value_timestamp.tps +++ b/source/expectations/data_values/ut_data_value_timestamp.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_timestamp under ut_data_value( +create or replace noneditionable type ut_data_value_timestamp under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb b/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb index 0f41e16b1..025314f4b 100644 --- a/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb +++ b/source/expectations/data_values/ut_data_value_timestamp_ltz.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_timestamp_ltz as +create or replace noneditionable type body ut_data_value_timestamp_ltz as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp_ltz as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'timestamp with local time zone'; + self.data_type_plsql := 'timestamp_ltz_unconstrained'; return; end; diff --git a/source/expectations/data_values/ut_data_value_timestamp_ltz.tps b/source/expectations/data_values/ut_data_value_timestamp_ltz.tps index 3608ebd37..e7c61a878 100644 --- a/source/expectations/data_values/ut_data_value_timestamp_ltz.tps +++ b/source/expectations/data_values/ut_data_value_timestamp_ltz.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_timestamp_ltz under ut_data_value( +create or replace noneditionable type ut_data_value_timestamp_ltz under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_timestamp_tz.tpb b/source/expectations/data_values/ut_data_value_timestamp_tz.tpb index 710a313a7..6af5fea4d 100644 --- a/source/expectations/data_values/ut_data_value_timestamp_tz.tpb +++ b/source/expectations/data_values/ut_data_value_timestamp_tz.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_timestamp_tz as +create or replace noneditionable type body ut_data_value_timestamp_tz as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_timestamp_tz as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'timestamp with time zone'; + self.data_type_plsql := 'timestamp_tz_unconstrained'; return; end; diff --git a/source/expectations/data_values/ut_data_value_timestamp_tz.tps b/source/expectations/data_values/ut_data_value_timestamp_tz.tps index ca0bfc5cb..ad190d197 100644 --- a/source/expectations/data_values/ut_data_value_timestamp_tz.tps +++ b/source/expectations/data_values/ut_data_value_timestamp_tz.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_timestamp_tz under ut_data_value( +create or replace noneditionable type ut_data_value_timestamp_tz under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_varchar2.tpb b/source/expectations/data_values/ut_data_value_varchar2.tpb index da4276d51..7a26d7571 100644 --- a/source/expectations/data_values/ut_data_value_varchar2.tpb +++ b/source/expectations/data_values/ut_data_value_varchar2.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_varchar2 as +create or replace noneditionable type body ut_data_value_varchar2 as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_varchar2 as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'varchar2'; + self.data_type_plsql := 'varchar2(32767)'; return; end; diff --git a/source/expectations/data_values/ut_data_value_varchar2.tps b/source/expectations/data_values/ut_data_value_varchar2.tps index 27bb720ce..56effd460 100644 --- a/source/expectations/data_values/ut_data_value_varchar2.tps +++ b/source/expectations/data_values/ut_data_value_varchar2.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_varchar2 under ut_data_value( +create or replace noneditionable type ut_data_value_varchar2 under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_xmltype.tpb b/source/expectations/data_values/ut_data_value_xmltype.tpb index 3b10aadce..f5ffcbc9a 100644 --- a/source/expectations/data_values/ut_data_value_xmltype.tpb +++ b/source/expectations/data_values/ut_data_value_xmltype.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_xmltype as +create or replace noneditionable type body ut_data_value_xmltype as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_xmltype as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'xmltype'; + self.data_type_plsql := 'xmltype'; return; end; diff --git a/source/expectations/data_values/ut_data_value_xmltype.tps b/source/expectations/data_values/ut_data_value_xmltype.tps index 476097de3..2d0257918 100644 --- a/source/expectations/data_values/ut_data_value_xmltype.tps +++ b/source/expectations/data_values/ut_data_value_xmltype.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_xmltype under ut_data_value( +create or replace noneditionable type ut_data_value_xmltype under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_data_value_yminterval.tpb b/source/expectations/data_values/ut_data_value_yminterval.tpb index 6d6a05b4a..8d12d3544 100644 --- a/source/expectations/data_values/ut_data_value_yminterval.tpb +++ b/source/expectations/data_values/ut_data_value_yminterval.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_data_value_yminterval as +create or replace noneditionable type body ut_data_value_yminterval as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ create or replace type body ut_data_value_yminterval as self.data_value := a_value; self.self_type := $$plsql_unit; self.data_type := 'interval year to month'; + self.data_type_plsql := 'yminterval_unconstrained'; return; end; @@ -31,7 +32,7 @@ create or replace type body ut_data_value_yminterval as overriding member function to_string return varchar2 is begin - return ut_utils.to_string(self.data_value); + return ut_utils.interval_to_text(self.data_value); end; overriding member function compare_implementation(a_other ut_data_value) return integer is diff --git a/source/expectations/data_values/ut_data_value_yminterval.tps b/source/expectations/data_values/ut_data_value_yminterval.tps index d20dfcad7..8c3ad8079 100644 --- a/source/expectations/data_values/ut_data_value_yminterval.tps +++ b/source/expectations/data_values/ut_data_value_yminterval.tps @@ -1,7 +1,7 @@ -create or replace type ut_data_value_yminterval under ut_data_value( +create or replace noneditionable type ut_data_value_yminterval under ut_data_value( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ create or replace type ut_data_value_yminterval under ut_data_value( See the License for the specific language governing permissions and limitations under the License. */ - data_value yminterval_unconstrained, + data_value interval year(9) to month, constructor function ut_data_value_yminterval(self in out nocopy ut_data_value_yminterval, a_value yminterval_unconstrained) return self as result, overriding member function is_null return boolean, overriding member function to_string return varchar2, diff --git a/source/expectations/data_values/ut_json_data_diff_tmp.sql b/source/expectations/data_values/ut_json_data_diff_tmp.sql index bfaaa1b72..4eda07d7c 100644 --- a/source/expectations/data_values/ut_json_data_diff_tmp.sql +++ b/source/expectations/data_values/ut_json_data_diff_tmp.sql @@ -1,7 +1,7 @@ create global temporary table ut_json_data_diff_tmp( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/source/expectations/data_values/ut_json_leaf.tpb b/source/expectations/data_values/ut_json_leaf.tpb index 83f1009ef..d6c862286 100644 --- a/source/expectations/data_values/ut_json_leaf.tpb +++ b/source/expectations/data_values/ut_json_leaf.tpb @@ -1,5 +1,21 @@ -create or replace type body ut_json_leaf as - +create or replace noneditionable type body ut_json_leaf as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + member procedure init( self in out nocopy ut_json_leaf, a_element_name varchar2, a_element_value varchar2,a_parent_name varchar2, a_access_path varchar2, a_hierarchy_level integer, a_index_position integer, a_json_type in varchar2, diff --git a/source/expectations/data_values/ut_json_leaf.tps b/source/expectations/data_values/ut_json_leaf.tps index c38c3b238..ac478e34e 100644 --- a/source/expectations/data_values/ut_json_leaf.tps +++ b/source/expectations/data_values/ut_json_leaf.tps @@ -1,7 +1,7 @@ -create or replace type ut_json_leaf authid current_user as object ( +create or replace noneditionable type ut_json_leaf authid current_user as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_json_leaf_tab.tps b/source/expectations/data_values/ut_json_leaf_tab.tps index 0c7a40b9f..c00f2a3a1 100644 --- a/source/expectations/data_values/ut_json_leaf_tab.tps +++ b/source/expectations/data_values/ut_json_leaf_tab.tps @@ -1,7 +1,7 @@ -create or replace type ut_json_leaf_tab as +create or replace noneditionable type ut_json_leaf_tab as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_json_tree_details.tpb b/source/expectations/data_values/ut_json_tree_details.tpb index a333ea71c..0d457b2c5 100644 --- a/source/expectations/data_values/ut_json_tree_details.tpb +++ b/source/expectations/data_values/ut_json_tree_details.tpb @@ -1,4 +1,20 @@ -create or replace type body ut_json_tree_details as +create or replace noneditionable type body ut_json_tree_details as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ member function get_json_type(a_json_piece json_element_t) return varchar2 is begin diff --git a/source/expectations/data_values/ut_json_tree_details.tps b/source/expectations/data_values/ut_json_tree_details.tps index 0684ecafe..4f7e891be 100644 --- a/source/expectations/data_values/ut_json_tree_details.tps +++ b/source/expectations/data_values/ut_json_tree_details.tps @@ -1,7 +1,7 @@ -create or replace type ut_json_tree_details force as object ( +create or replace noneditionable type ut_json_tree_details force as object ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_key_anyval_pair.tps b/source/expectations/data_values/ut_key_anyval_pair.tps index 4903282e9..6ed63ae7e 100644 --- a/source/expectations/data_values/ut_key_anyval_pair.tps +++ b/source/expectations/data_values/ut_key_anyval_pair.tps @@ -1,7 +1,7 @@ -create or replace type ut_key_anyval_pair force as object( +create or replace noneditionable type ut_key_anyval_pair force as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_key_anyval_pairs.tps b/source/expectations/data_values/ut_key_anyval_pairs.tps index 94bd5df54..a75305f79 100644 --- a/source/expectations/data_values/ut_key_anyval_pairs.tps +++ b/source/expectations/data_values/ut_key_anyval_pairs.tps @@ -1,7 +1,7 @@ -create or replace type ut_key_anyval_pairs as +create or replace noneditionable type ut_key_anyval_pairs as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_key_anyvalues.tpb b/source/expectations/data_values/ut_key_anyvalues.tpb index 93bf1eb53..4e6dd2c0f 100644 --- a/source/expectations/data_values/ut_key_anyvalues.tpb +++ b/source/expectations/data_values/ut_key_anyvalues.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_key_anyvalues as +create or replace noneditionable type body ut_key_anyvalues as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/data_values/ut_key_anyvalues.tps b/source/expectations/data_values/ut_key_anyvalues.tps index 6dd32a76d..21a9a5c72 100644 --- a/source/expectations/data_values/ut_key_anyvalues.tps +++ b/source/expectations/data_values/ut_key_anyvalues.tps @@ -1,7 +1,7 @@ -create or replace type ut_key_anyvalues under ut_event_item ( +create or replace noneditionable type ut_key_anyvalues under ut_event_item ( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/json_objects_specs.sql b/source/expectations/json_objects_specs.sql index c0d42e8fb..c0fc15ed6 100644 --- a/source/expectations/json_objects_specs.sql +++ b/source/expectations/json_objects_specs.sql @@ -1,10 +1,14 @@ BEGIN - - $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then - dbms_output.put_line('Object exists , dont install'); - $else + null; + $if dbms_db_version.version < 21 $then + dbms_output.put_line('Installing json structures specs for native json.'); + execute immediate q'[create or replace noneditionable TYPE JSON FORCE AUTHID CURRENT_USER AS OBJECT( + dummyobjt NUMBER +) NOT FINAL NOT INSTANTIABLE;]'; + $end + $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then dbms_output.put_line('Installing json structures specs.'); - execute immediate q'[create or replace TYPE JSON_Element_T FORCE AUTHID CURRENT_USER AS OBJECT( + execute immediate q'[create or replace noneditionable TYPE JSON_Element_T FORCE AUTHID CURRENT_USER AS OBJECT( dummyobjt NUMBER, STATIC FUNCTION parse(jsn VARCHAR2) RETURN JSON_Element_T, STATIC FUNCTION parse(jsn CLOB) RETURN JSON_Element_T, @@ -30,9 +34,9 @@ BEGIN MEMBER FUNCTION get_Size(self IN JSON_ELEMENT_T) RETURN NUMBER ) NOT FINAL NOT INSTANTIABLE;]'; - execute immediate q'[create or replace TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]'; + execute immediate q'[create or replace noneditionable TYPE JSON_KEY_LIST FORCE AS VARRAY(32767) OF VARCHAR2(4000);]'; - execute immediate q'[create or replace TYPE JSON_Array_T FORCE AUTHID CURRENT_USER UNDER JSON_Element_T( + execute immediate q'[create or replace noneditionable TYPE JSON_Array_T FORCE AUTHID CURRENT_USER UNDER JSON_Element_T( CONSTRUCTOR FUNCTION JSON_Array_T RETURN SELF AS RESULT, MEMBER FUNCTION get(pos NUMBER) RETURN JSON_Element_T, MEMBER FUNCTION get_String(pos NUMBER) RETURN VARCHAR2, @@ -47,7 +51,7 @@ BEGIN MEMBER FUNCTION get_Type(pos NUMBER) RETURN VARCHAR2 ) FINAL;]'; - execute immediate q'[create or replace TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T( + execute immediate q'[create or replace noneditionable TYPE JSON_Object_T AUTHID CURRENT_USER UNDER JSON_Element_T( CONSTRUCTOR FUNCTION JSON_Object_T RETURN SELF AS RESULT, MEMBER FUNCTION get(key VARCHAR2) RETURN JSON_Element_T, MEMBER FUNCTION get_Object(key VARCHAR2) RETURN JSON_OBJECT_T, diff --git a/source/expectations/matchers/ut_be_between.tpb b/source/expectations/matchers/ut_be_between.tpb index 7368ad5a7..83dfc4c25 100644 --- a/source/expectations/matchers/ut_be_between.tpb +++ b/source/expectations/matchers/ut_be_between.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_between is +create or replace noneditionable type body ut_be_between is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_between.tps b/source/expectations/matchers/ut_be_between.tps index 7e254719e..7f1403847 100644 --- a/source/expectations/matchers/ut_be_between.tps +++ b/source/expectations/matchers/ut_be_between.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_between under ut_matcher( +create or replace noneditionable type ut_be_between under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_empty.tpb b/source/expectations/matchers/ut_be_empty.tpb index 08523fb02..af233aa8f 100644 --- a/source/expectations/matchers/ut_be_empty.tpb +++ b/source/expectations/matchers/ut_be_empty.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_empty as +create or replace noneditionable type body ut_be_empty as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_empty.tps b/source/expectations/matchers/ut_be_empty.tps index 76af402fe..a5672ba78 100644 --- a/source/expectations/matchers/ut_be_empty.tps +++ b/source/expectations/matchers/ut_be_empty.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_empty under ut_matcher( +create or replace noneditionable type ut_be_empty under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_false.tpb b/source/expectations/matchers/ut_be_false.tpb index 671f43cbe..a02ac58cb 100644 --- a/source/expectations/matchers/ut_be_false.tpb +++ b/source/expectations/matchers/ut_be_false.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_false as +create or replace noneditionable type body ut_be_false as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_false.tps b/source/expectations/matchers/ut_be_false.tps index 3d4e8dda9..d793844c1 100644 --- a/source/expectations/matchers/ut_be_false.tps +++ b/source/expectations/matchers/ut_be_false.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_false under ut_matcher( +create or replace noneditionable type ut_be_false under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_greater_or_equal.tpb b/source/expectations/matchers/ut_be_greater_or_equal.tpb index 6877eb32c..6e564a90c 100644 --- a/source/expectations/matchers/ut_be_greater_or_equal.tpb +++ b/source/expectations/matchers/ut_be_greater_or_equal.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_greater_or_equal AS +create or replace noneditionable type body ut_be_greater_or_equal AS /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_greater_or_equal.tps b/source/expectations/matchers/ut_be_greater_or_equal.tps index facd33b3d..d3e651958 100644 --- a/source/expectations/matchers/ut_be_greater_or_equal.tps +++ b/source/expectations/matchers/ut_be_greater_or_equal.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_greater_or_equal under ut_comparison_matcher( +create or replace noneditionable type ut_be_greater_or_equal under ut_comparison_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_greater_than.tpb b/source/expectations/matchers/ut_be_greater_than.tpb index da46347af..08628ae3a 100644 --- a/source/expectations/matchers/ut_be_greater_than.tpb +++ b/source/expectations/matchers/ut_be_greater_than.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_greater_than AS +create or replace noneditionable type body ut_be_greater_than AS /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_greater_than.tps b/source/expectations/matchers/ut_be_greater_than.tps index fa19a9bd4..745b19f08 100644 --- a/source/expectations/matchers/ut_be_greater_than.tps +++ b/source/expectations/matchers/ut_be_greater_than.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_greater_than under ut_comparison_matcher( +create or replace noneditionable type ut_be_greater_than under ut_comparison_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_less_or_equal.tpb b/source/expectations/matchers/ut_be_less_or_equal.tpb index 98c4238eb..2df06a078 100644 --- a/source/expectations/matchers/ut_be_less_or_equal.tpb +++ b/source/expectations/matchers/ut_be_less_or_equal.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_less_or_equal AS +create or replace noneditionable type body ut_be_less_or_equal AS /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_less_or_equal.tps b/source/expectations/matchers/ut_be_less_or_equal.tps index 225c99b72..f0e297ad1 100644 --- a/source/expectations/matchers/ut_be_less_or_equal.tps +++ b/source/expectations/matchers/ut_be_less_or_equal.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_less_or_equal under ut_comparison_matcher( +create or replace noneditionable type ut_be_less_or_equal under ut_comparison_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_less_than.tpb b/source/expectations/matchers/ut_be_less_than.tpb index e1c5c84b1..c0b059231 100644 --- a/source/expectations/matchers/ut_be_less_than.tpb +++ b/source/expectations/matchers/ut_be_less_than.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_less_than as +create or replace noneditionable type body ut_be_less_than as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_less_than.tps b/source/expectations/matchers/ut_be_less_than.tps index 8482f0327..594df9b71 100644 --- a/source/expectations/matchers/ut_be_less_than.tps +++ b/source/expectations/matchers/ut_be_less_than.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_less_than under ut_comparison_matcher( +create or replace noneditionable type ut_be_less_than under ut_comparison_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_like.tpb b/source/expectations/matchers/ut_be_like.tpb index dbcd1c798..b94c3fa91 100644 --- a/source/expectations/matchers/ut_be_like.tpb +++ b/source/expectations/matchers/ut_be_like.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_like as +create or replace noneditionable type body ut_be_like as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_like.tps b/source/expectations/matchers/ut_be_like.tps index 0c1609a98..ca9ad45de 100644 --- a/source/expectations/matchers/ut_be_like.tps +++ b/source/expectations/matchers/ut_be_like.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_like under ut_matcher( +create or replace noneditionable type ut_be_like under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_not_null.tpb b/source/expectations/matchers/ut_be_not_null.tpb index 94ee0ce0a..d291d2e35 100644 --- a/source/expectations/matchers/ut_be_not_null.tpb +++ b/source/expectations/matchers/ut_be_not_null.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_not_null as +create or replace noneditionable type body ut_be_not_null as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_not_null.tps b/source/expectations/matchers/ut_be_not_null.tps index 34cc2bfd0..61f6c871d 100644 --- a/source/expectations/matchers/ut_be_not_null.tps +++ b/source/expectations/matchers/ut_be_not_null.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_not_null under ut_matcher( +create or replace noneditionable type ut_be_not_null under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_null.tpb b/source/expectations/matchers/ut_be_null.tpb index 3194f092f..89fbaf563 100644 --- a/source/expectations/matchers/ut_be_null.tpb +++ b/source/expectations/matchers/ut_be_null.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_null as +create or replace noneditionable type body ut_be_null as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_null.tps b/source/expectations/matchers/ut_be_null.tps index 486e11f28..063c7db29 100644 --- a/source/expectations/matchers/ut_be_null.tps +++ b/source/expectations/matchers/ut_be_null.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_null under ut_matcher( +create or replace noneditionable type ut_be_null under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_true.tpb b/source/expectations/matchers/ut_be_true.tpb index 565ab590b..59368f342 100644 --- a/source/expectations/matchers/ut_be_true.tpb +++ b/source/expectations/matchers/ut_be_true.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_be_true as +create or replace noneditionable type body ut_be_true as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_true.tps b/source/expectations/matchers/ut_be_true.tps index b994913f5..19d397371 100644 --- a/source/expectations/matchers/ut_be_true.tps +++ b/source/expectations/matchers/ut_be_true.tps @@ -1,7 +1,7 @@ -create or replace type ut_be_true under ut_matcher( +create or replace noneditionable type ut_be_true under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_be_within.tpb b/source/expectations/matchers/ut_be_within.tpb new file mode 100644 index 000000000..f8f880ace --- /dev/null +++ b/source/expectations/matchers/ut_be_within.tpb @@ -0,0 +1,137 @@ +create or replace noneditionable type body ut_be_within as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected number) return self as result is + begin + self.init(ut_data_value_number(a_distance_from_expected), $$plsql_unit); + return; + end; + + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected dsinterval_unconstrained) return self as result is + begin + self.init(ut_data_value_dsinterval(a_distance_from_expected), $$plsql_unit); + return; + end; + + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected yminterval_unconstrained) return self as result is + begin + self.init(ut_data_value_yminterval(a_distance_from_expected), $$plsql_unit); + return; + end; + + member procedure of_(self in ut_be_within, a_expected date) is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_date(a_expected); + if l_result.is_negated_flag = 1 then + l_result.expectation.not_to(l_result); + else + l_result.expectation.to_(l_result); + end if; + end; + + member function of_(self in ut_be_within, a_expected date) return ut_be_within is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_date(a_expected); + return l_result; + end; + + member procedure of_(self in ut_be_within, a_expected timestamp_unconstrained) is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp(a_expected); + if l_result.is_negated_flag = 1 then + l_result.expectation.not_to(l_result); + else + l_result.expectation.to_(l_result); + end if; + end; + + member function of_(self in ut_be_within, a_expected timestamp_unconstrained) return ut_be_within is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp(a_expected); + return l_result; + end; + + member procedure of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp_tz(a_expected); + if l_result.is_negated_flag = 1 then + l_result.expectation.not_to(l_result); + else + l_result.expectation.to_(l_result); + end if; + end; + + member function of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) return ut_be_within is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp_tz(a_expected); + return l_result; + end; + + member procedure of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp_ltz(a_expected); + if l_result.is_negated_flag = 1 then + l_result.expectation.not_to(l_result); + else + l_result.expectation.to_(l_result); + end if; + end; + + member function of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) return ut_be_within is + l_result ut_be_within := self; + begin + l_result.expected := ut_data_value_timestamp_ltz(a_expected); + return l_result; + end; + + overriding member function run_matcher(self in out nocopy ut_be_within, a_actual ut_data_value) return boolean is + l_result boolean; + begin + if self.expected.data_type = a_actual.data_type + and ( + self.expected is of (ut_data_value_date, ut_data_value_timestamp, ut_data_value_timestamp_tz, ut_data_value_timestamp_ltz) + and self.distance_from_expected is of (ut_data_value_yminterval, ut_data_value_dsinterval) + or self.expected is of (ut_data_value_number) and self.distance_from_expected is of (ut_data_value_number) + ) + then + l_result := ut_be_within_helper.values_within_abs_distance( a_actual, self.expected, self.distance_from_expected) ; + else + l_result := (self as ut_matcher).run_matcher(a_actual); + end if; + return l_result; + end; + + overriding member function failure_message(a_actual ut_data_value) return varchar2 is + begin + return (self as ut_matcher).failure_message(a_actual) || ' '||self.distance_from_expected.to_string ||' of '|| expected.to_string_report(); + end; + + overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 is + begin + return (self as ut_matcher).failure_message_when_negated(a_actual) || ' '||self.distance_from_expected.to_string ||' of '|| expected.to_string_report(); + end; + +end; +/ diff --git a/source/expectations/matchers/ut_be_within.tps b/source/expectations/matchers/ut_be_within.tps new file mode 100644 index 000000000..276c529df --- /dev/null +++ b/source/expectations/matchers/ut_be_within.tps @@ -0,0 +1,36 @@ +create or replace noneditionable type ut_be_within force under ut_be_within_pct( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected number) return self as result, + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected dsinterval_unconstrained) return self as result, + constructor function ut_be_within(self in out nocopy ut_be_within, a_distance_from_expected yminterval_unconstrained) return self as result, + member procedure of_(self in ut_be_within, a_expected date), + member function of_(self in ut_be_within, a_expected date) return ut_be_within, + member procedure of_(self in ut_be_within, a_expected timestamp_unconstrained), + member function of_(self in ut_be_within, a_expected timestamp_unconstrained) return ut_be_within, + member procedure of_(self in ut_be_within, a_expected timestamp_tz_unconstrained ), + member function of_(self in ut_be_within, a_expected timestamp_tz_unconstrained) return ut_be_within, + member procedure of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained), + member function of_(self in ut_be_within, a_expected timestamp_ltz_unconstrained) return ut_be_within, + overriding member function run_matcher(self in out nocopy ut_be_within, a_actual ut_data_value) return boolean, + overriding member function failure_message(a_actual ut_data_value) return varchar2, + overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 +) +not final +/ diff --git a/source/expectations/matchers/ut_be_within_helper.pkb b/source/expectations/matchers/ut_be_within_helper.pkb new file mode 100644 index 000000000..635c1b111 --- /dev/null +++ b/source/expectations/matchers/ut_be_within_helper.pkb @@ -0,0 +1,55 @@ +create or replace noneditionable package body ut_be_within_helper as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + function values_within_abs_distance( + a_actual ut_data_value, a_expected ut_data_value, a_distance ut_data_value + ) return boolean is + l_result integer; + l_YM_conversion varchar2(50) := case when a_distance is of (ut_data_value_yminterval) then ' year to month ' end; + l_formula varchar2(4000); + l_code varchar2(4000); + begin + l_formula := + case + when a_actual is of (ut_data_value_date) + then '( cast(greatest(l_actual, l_expected) as timestamp) - cast(least(l_actual, l_expected) as timestamp) ) '||l_YM_conversion||' <= l_distance' + else '( greatest(l_actual, l_expected) - least(l_actual, l_expected) ) '||l_YM_conversion||' <= l_distance' + end; + l_code := + q'[ + declare + l_actual ]'||dbms_assert.simple_sql_name(a_actual.data_type_plsql)|| q'[ := treat(:a_actual as ]'||dbms_assert.simple_sql_name(a_actual.self_type)||q'[).data_value; + l_expected ]'||dbms_assert.simple_sql_name(a_expected.data_type_plsql)||q'[ := treat(:a_expected as ]'||dbms_assert.simple_sql_name(a_expected.self_type)||q'[).data_value; + l_distance ]'||dbms_assert.simple_sql_name(a_distance.data_type_plsql)||q'[ := treat(:a_distance as ]'||dbms_assert.simple_sql_name(a_distance.self_type)||q'[).data_value; + begin + :result := + case + when + ]'||l_formula||q'[ + then 1 + else 0 + end; + end; + ]'; + execute immediate l_code + using a_actual, a_expected, a_distance, out l_result; + return l_result > 0; + end; + +end; +/ diff --git a/source/expectations/matchers/ut_be_within_helper.pks b/source/expectations/matchers/ut_be_within_helper.pks new file mode 100644 index 000000000..5a59eea9e --- /dev/null +++ b/source/expectations/matchers/ut_be_within_helper.pks @@ -0,0 +1,24 @@ +create or replace noneditionable package ut_be_within_helper authid definer as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + function values_within_abs_distance( + a_actual ut_data_value, a_expected ut_data_value, a_distance ut_data_value + ) return boolean; + +end; +/ diff --git a/source/expectations/matchers/ut_be_within_pct.tpb b/source/expectations/matchers/ut_be_within_pct.tpb new file mode 100644 index 000000000..82ce8e84e --- /dev/null +++ b/source/expectations/matchers/ut_be_within_pct.tpb @@ -0,0 +1,84 @@ +create or replace noneditionable type body ut_be_within_pct as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_be_within_pct(self in out nocopy ut_be_within_pct, a_pct_of_expected number) return self as result is + begin + self.init(ut_data_value_number(a_pct_of_expected), $$plsql_unit); + return; + end; + + member procedure init(self in out nocopy ut_be_within_pct, a_distance_from_expected ut_data_value, self_type varchar2) is + begin + self.distance_from_expected := a_distance_from_expected; + self.self_type := self_type; + end; + + member procedure of_(self in ut_be_within_pct, a_expected number) is + l_result ut_be_within_pct := self; + begin + l_result.expected := ut_data_value_number(a_expected); + if l_result.is_negated_flag = 1 then + l_result.expectation.not_to(l_result); + else + l_result.expectation.to_(l_result); + end if; + end; + + member function of_(self in ut_be_within_pct, a_expected number) return ut_be_within_pct is + l_result ut_be_within_pct := self; + begin + l_result.expected := ut_data_value_number(a_expected); + return l_result; + end; + + overriding member function run_matcher(self in out nocopy ut_be_within_pct, a_actual ut_data_value) return boolean is + l_result boolean; + begin + if self.expected.data_type = a_actual.data_type then + if self.expected is of (ut_data_value_number) then + l_result := + abs(treat(self.distance_from_expected as ut_data_value_number).data_value) * treat(self.expected as ut_data_value_number).data_value + >= abs( ( treat(self.expected as ut_data_value_number).data_value - treat(a_actual as ut_data_value_number).data_value ) * 100 ); + end if; + else + l_result := (self as ut_matcher).run_matcher(a_actual); + end if; + return l_result; + end; + + overriding member function failure_message(a_actual ut_data_value) return varchar2 is + begin + return rtrim( (self as ut_matcher).failure_message(a_actual), 'pct' ) || self.distance_from_expected.to_string ||' % of '|| expected.to_string_report(); + end; + + overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2 is + begin + return rtrim( (self as ut_matcher).failure_message_when_negated(a_actual), 'pct' ) || self.distance_from_expected.to_string ||' % of '|| expected.to_string_report(); + end; + + overriding member function error_message(a_actual ut_data_value) return varchar2 is + l_result varchar2(32767); + begin + if ut_utils.int_to_boolean(self.is_errored) then + l_result := 'Matcher '''||self.name()||''' cannot be used to compare Actual ('||a_actual.data_type||') with Expected ('||expected.data_type||') using distance ('||self.distance_from_expected.data_type||').'; + end if; + return l_result; + end; + +end; +/ diff --git a/source/expectations/matchers/ut_be_within_pct.tps b/source/expectations/matchers/ut_be_within_pct.tps new file mode 100644 index 000000000..810eaa5b3 --- /dev/null +++ b/source/expectations/matchers/ut_be_within_pct.tps @@ -0,0 +1,34 @@ +create or replace noneditionable type ut_be_within_pct force under ut_comparison_matcher( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + /** + * Holds information about mather options + */ + distance_from_expected ut_data_value, + + constructor function ut_be_within_pct(self in out nocopy ut_be_within_pct, a_pct_of_expected number) return self as result, + member procedure init(self in out nocopy ut_be_within_pct, a_distance_from_expected ut_data_value, self_type varchar2), + member procedure of_(self in ut_be_within_pct, a_expected number), + member function of_(self in ut_be_within_pct, a_expected number) return ut_be_within_pct, + overriding member function run_matcher(self in out nocopy ut_be_within_pct, a_actual ut_data_value) return boolean, + overriding member function failure_message(a_actual ut_data_value) return varchar2, + overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2, + overriding member function error_message(a_actual ut_data_value) return varchar2 +) +not final +/ diff --git a/source/expectations/matchers/ut_comparison_matcher.tpb b/source/expectations/matchers/ut_comparison_matcher.tpb index 50f8d6050..d6c47855d 100644 --- a/source/expectations/matchers/ut_comparison_matcher.tpb +++ b/source/expectations/matchers/ut_comparison_matcher.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_comparison_matcher as +create or replace noneditionable type body ut_comparison_matcher as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_comparison_matcher.tps b/source/expectations/matchers/ut_comparison_matcher.tps index 76520a296..b1ed08a84 100644 --- a/source/expectations/matchers/ut_comparison_matcher.tps +++ b/source/expectations/matchers/ut_comparison_matcher.tps @@ -1,7 +1,7 @@ -create or replace type ut_comparison_matcher under ut_matcher( +create or replace noneditionable type ut_comparison_matcher under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_contain.tpb b/source/expectations/matchers/ut_contain.tpb index c0c4f481a..bcc3806b7 100644 --- a/source/expectations/matchers/ut_contain.tpb +++ b/source/expectations/matchers/ut_contain.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_contain as +create or replace noneditionable type body ut_contain as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ create or replace type body ut_contain as overriding member function run_matcher_negated(self in out nocopy ut_contain, a_actual ut_data_value) return boolean is begin + self.negated(); return run_matcher(a_actual); end; diff --git a/source/expectations/matchers/ut_contain.tps b/source/expectations/matchers/ut_contain.tps index 518352573..bd67113c9 100644 --- a/source/expectations/matchers/ut_contain.tps +++ b/source/expectations/matchers/ut_contain.tps @@ -1,7 +1,7 @@ -create or replace type ut_contain under ut_equal( +create or replace noneditionable type ut_contain under ut_equal( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_equal.tpb b/source/expectations/matchers/ut_equal.tpb index afa2148f3..f999995d1 100644 --- a/source/expectations/matchers/ut_equal.tpb +++ b/source/expectations/matchers/ut_equal.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_equal as +create or replace noneditionable type body ut_equal as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -158,6 +158,12 @@ create or replace type body ut_equal as return; end; + constructor function ut_equal(self in out nocopy ut_equal, a_expected json, a_nulls_are_equal boolean := null) return self as result is + begin + init(ut_data_value_json(a_expected), a_nulls_are_equal); + return; + end; + member function include(a_items varchar2) return ut_equal is l_result ut_equal := self; begin @@ -172,6 +178,18 @@ create or replace type body ut_equal as return l_result; end; + member procedure include(self in ut_equal, a_items varchar2) is + begin + include( ut_varchar2_list( a_items ) ); + end; + + member procedure include(self in ut_equal, a_items ut_varchar2_list) is + l_result ut_equal := self; + begin + l_result.options.include.add_items(a_items); + l_result.expectation.to_(l_result ); + end; + member function exclude(a_items varchar2) return ut_equal is l_result ut_equal := self; begin @@ -186,6 +204,18 @@ create or replace type body ut_equal as return l_result; end; + member procedure exclude(self in ut_equal, a_items varchar2) is + begin + exclude( ut_varchar2_list( a_items ) ); + end; + + member procedure exclude(self in ut_equal, a_items ut_varchar2_list) is + l_result ut_equal := self; + begin + l_result.options.exclude.add_items(a_items); + l_result.expectation.to_(l_result ); + end; + member function unordered return ut_equal is l_result ut_equal := self; begin @@ -193,6 +223,13 @@ create or replace type body ut_equal as return l_result; end; + member procedure unordered(self in ut_equal) is + l_result ut_equal := self; + begin + l_result.options.unordered(); + l_result.expectation.to_(l_result ); + end; + member function join_by(a_columns varchar2) return ut_equal is l_result ut_equal := self; begin @@ -209,18 +246,43 @@ create or replace type body ut_equal as return l_result; end; - member function uc return ut_equal is + member procedure join_by(self in ut_equal, a_columns varchar2) is begin - return unordered_columns; + join_by( ut_varchar2_list( a_columns ) ); end; + member procedure join_by(self in ut_equal, a_columns ut_varchar2_list) is + l_result ut_equal := self; + begin + l_result.options.unordered(); + l_result.options.join_by.add_items(a_columns); + l_result.expectation.to_(l_result ); + end; + member function unordered_columns return ut_equal is l_result ut_equal := self; begin l_result.options.unordered_columns(); return l_result; end; + + member procedure unordered_columns(self in ut_equal) is + l_result ut_equal := self; + begin + l_result.options.unordered_columns(); + l_result.expectation.to_(l_result ); + end; + + member function uc return ut_equal is + begin + return unordered_columns; + end; + member procedure uc(self in ut_equal) is + begin + unordered_columns; + end; + overriding member function run_matcher(self in out nocopy ut_equal, a_actual ut_data_value) return boolean is l_result boolean; begin diff --git a/source/expectations/matchers/ut_equal.tps b/source/expectations/matchers/ut_equal.tps index 1f42324d7..eeb715a58 100644 --- a/source/expectations/matchers/ut_equal.tps +++ b/source/expectations/matchers/ut_equal.tps @@ -1,7 +1,7 @@ -create or replace type ut_equal force under ut_comparison_matcher( +create or replace noneditionable type ut_equal force under ut_comparison_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -42,18 +42,28 @@ create or replace type ut_equal force under ut_comparison_matcher( constructor function ut_equal(self in out nocopy ut_equal, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null) return self as result, constructor function ut_equal(self in out nocopy ut_equal, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null) return self as result, constructor function ut_equal(self in out nocopy ut_equal, a_expected json_element_t, a_nulls_are_equal boolean := null) return self as result, + constructor function ut_equal(self in out nocopy ut_equal, a_expected json, a_nulls_are_equal boolean := null) return self as result, member function include(a_items varchar2) return ut_equal, - member function include(a_items ut_varchar2_list) return ut_equal, + member function include(a_items ut_varchar2_list) return ut_equal, + member procedure include(self in ut_equal, a_items varchar2), + member procedure include(self in ut_equal, a_items ut_varchar2_list), member function exclude(a_items varchar2) return ut_equal, member function exclude(a_items ut_varchar2_list) return ut_equal, + member procedure exclude(self in ut_equal, a_items varchar2), + member procedure exclude(self in ut_equal, a_items ut_varchar2_list), member function unordered return ut_equal, + member procedure unordered(self in ut_equal), member function join_by(a_columns varchar2) return ut_equal, member function join_by(a_columns ut_varchar2_list) return ut_equal, + member procedure join_by(self in ut_equal, a_columns varchar2), + member procedure join_by(self in ut_equal, a_columns ut_varchar2_list), overriding member function run_matcher(self in out nocopy ut_equal, a_actual ut_data_value) return boolean, overriding member function failure_message(a_actual ut_data_value) return varchar2, overriding member function failure_message_when_negated(a_actual ut_data_value) return varchar2, member function unordered_columns return ut_equal, - member function uc return ut_equal + member procedure unordered_columns(self in ut_equal), + member function uc return ut_equal, + member procedure uc(self in ut_equal) ) not final / diff --git a/source/expectations/matchers/ut_have_count.tpb b/source/expectations/matchers/ut_have_count.tpb index b0f4e06d4..f6d3d2682 100644 --- a/source/expectations/matchers/ut_have_count.tpb +++ b/source/expectations/matchers/ut_have_count.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_have_count as +create or replace noneditionable type body ut_have_count as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_have_count.tps b/source/expectations/matchers/ut_have_count.tps index fb0fc76fc..85cdf4393 100644 --- a/source/expectations/matchers/ut_have_count.tps +++ b/source/expectations/matchers/ut_have_count.tps @@ -1,7 +1,7 @@ -create or replace type ut_have_count under ut_matcher( +create or replace noneditionable type ut_have_count under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_match.tpb b/source/expectations/matchers/ut_match.tpb index 57fabbaec..1836298e7 100644 --- a/source/expectations/matchers/ut_match.tpb +++ b/source/expectations/matchers/ut_match.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_match as +create or replace noneditionable type body ut_match as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_match.tps b/source/expectations/matchers/ut_match.tps index 8d6b0f73a..3f840545f 100644 --- a/source/expectations/matchers/ut_match.tps +++ b/source/expectations/matchers/ut_match.tps @@ -1,7 +1,7 @@ -create or replace type ut_match under ut_matcher( +create or replace noneditionable type ut_match under ut_matcher( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_matcher.tpb b/source/expectations/matchers/ut_matcher.tpb index 11ff1b6b6..828e1ac51 100644 --- a/source/expectations/matchers/ut_matcher.tpb +++ b/source/expectations/matchers/ut_matcher.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_matcher as +create or replace noneditionable type body ut_matcher as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,11 +20,6 @@ create or replace type body ut_matcher as begin ut_utils.debug_log('Failure - ut_matcher.run_matcher'||'(a_actual '||a_actual.data_type||')'); self.is_errored := ut_utils.boolean_to_int(true); --- self.error_message := 'The matcher '''||name()||''' cannot be used'; --- if self.expected is not null then --- self.error_message := self.error_message ||' for comparison of data type ('||self.expected.data_type||')'; --- end if; --- self.error_message := self.error_message ||' with data type ('||a_actual.data_type||').'; return null; end; diff --git a/source/expectations/matchers/ut_matcher.tps b/source/expectations/matchers/ut_matcher.tps index 34e1742df..ee7f1b5dd 100644 --- a/source/expectations/matchers/ut_matcher.tps +++ b/source/expectations/matchers/ut_matcher.tps @@ -1,7 +1,7 @@ -create or replace type ut_matcher authid current_user as object( +create or replace noneditionable type ut_matcher under ut_matcher_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -15,10 +15,9 @@ create or replace type ut_matcher authid current_user as object( See the License for the specific language governing permissions and limitations under the License. */ - self_type varchar2(250), is_errored integer, is_negated_flag number(1,0), - + expectation ut_expectation_base, /* function: run_matcher diff --git a/source/reporters/ut_coveralls_reporter.tps b/source/expectations/matchers/ut_matcher_base.tps similarity index 51% rename from source/reporters/ut_coveralls_reporter.tps rename to source/expectations/matchers/ut_matcher_base.tps index 53e86338e..ba79a6aed 100644 --- a/source/reporters/ut_coveralls_reporter.tps +++ b/source/expectations/matchers/ut_matcher_base.tps @@ -1,7 +1,7 @@ -create or replace type ut_coveralls_reporter under ut_coverage_reporter_base( +create or replace noneditionable type ut_matcher_base force authid current_user as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -16,16 +16,7 @@ create or replace type ut_coveralls_reporter under ut_coverage_reporter_base( limitations under the License. */ - /** - * Builds a coverage XML report that follows rules described in - * https://docs.sonarqube.org/display/SONAR/Generic+Test+Data - */ - constructor function ut_coveralls_reporter( - self in out nocopy ut_coveralls_reporter - ) return self as result, - - overriding member procedure after_calling_run(self in out nocopy ut_coveralls_reporter, a_run in ut_run), - - overriding member function get_description return varchar2 + self_type varchar2(250) ) +not final not instantiable / diff --git a/source/expectations/matchers/ut_matcher_options.tpb b/source/expectations/matchers/ut_matcher_options.tpb index 6fec3dee6..57aca2617 100644 --- a/source/expectations/matchers/ut_matcher_options.tpb +++ b/source/expectations/matchers/ut_matcher_options.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_matcher_options as +create or replace noneditionable type body ut_matcher_options as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_matcher_options.tps b/source/expectations/matchers/ut_matcher_options.tps index dbdf985f0..db076fd99 100644 --- a/source/expectations/matchers/ut_matcher_options.tps +++ b/source/expectations/matchers/ut_matcher_options.tps @@ -1,7 +1,7 @@ -create or replace type ut_matcher_options authid current_user as object( +create or replace noneditionable type ut_matcher_options authid current_user as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_matcher_options_items.tpb b/source/expectations/matchers/ut_matcher_options_items.tpb index 2c3f51d34..6e4841870 100644 --- a/source/expectations/matchers/ut_matcher_options_items.tpb +++ b/source/expectations/matchers/ut_matcher_options_items.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_matcher_options_items is +create or replace noneditionable type body ut_matcher_options_items is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/matchers/ut_matcher_options_items.tps b/source/expectations/matchers/ut_matcher_options_items.tps index cc74650f3..045a7ece7 100644 --- a/source/expectations/matchers/ut_matcher_options_items.tps +++ b/source/expectations/matchers/ut_matcher_options_items.tps @@ -1,7 +1,7 @@ -create or replace type ut_matcher_options_items authid current_user as object( +create or replace noneditionable type ut_matcher_options_items authid current_user as object( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/ut_expectation.tpb b/source/expectations/ut_expectation.tpb index d8ed1f79a..5f7c9cce0 100644 --- a/source/expectations/ut_expectation.tpb +++ b/source/expectations/ut_expectation.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_expectation as +create or replace noneditionable type body ut_expectation as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -15,31 +15,6 @@ create or replace type body ut_expectation as See the License for the specific language governing permissions and limitations under the License. */ - member procedure to_(self in ut_expectation, a_matcher ut_matcher) is - l_expectation_result boolean; - l_matcher ut_matcher := a_matcher; - l_message varchar2(32767); - begin - if a_matcher.is_negated() then - self.not_to( a_matcher ); - else - l_expectation_result := l_matcher.run_matcher( self.actual_data ); - l_expectation_result := coalesce(l_expectation_result,false); - l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message( self.actual_data ) ); - ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) ); - end if; - end; - - member procedure not_to(self in ut_expectation, a_matcher ut_matcher) is - l_expectation_result boolean; - l_matcher ut_matcher := a_matcher; - l_message varchar2(32767); - begin - l_expectation_result := coalesce( l_matcher.run_matcher_negated( self.actual_data ), false ); - - l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message_when_negated( self.actual_data ) ); - ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) ); - end; member procedure to_be_null(self in ut_expectation) is begin @@ -201,6 +176,11 @@ create or replace type body ut_expectation as self.to_( ut_equal(a_expected, a_nulls_are_equal) ); end; + member procedure to_equal(self in ut_expectation, a_expected json, a_nulls_are_equal boolean := null) is + begin + self.to_( ut_equal(a_expected, a_nulls_are_equal) ); + end; + member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null) is begin self.not_to( ut_equal(a_expected, a_nulls_are_equal) ); @@ -706,7 +686,7 @@ create or replace type body ut_expectation as member procedure not_to_contain(self in ut_expectation, a_expected sys_refcursor) is begin - self.not_to( ut_contain(a_expected).negated() ); + self.not_to( ut_contain(a_expected)); end; member procedure to_contain(self in ut_expectation, a_expected anydata) is @@ -716,8 +696,73 @@ create or replace type body ut_expectation as member procedure not_to_contain(self in ut_expectation, a_expected anydata) is begin - self.not_to( ut_contain(a_expected).negated() ); + self.not_to( ut_contain(a_expected)); end; + member function to_be_within(a_dist number) return ut_be_within is + l_result ut_be_within; + begin + l_result := ut_be_within(a_dist); + l_result.expectation := self; + return l_result; + end; + + member function to_be_within(a_dist dsinterval_unconstrained) return ut_be_within is + l_result ut_be_within; + begin + l_result := ut_be_within(a_dist); + l_result.expectation := self; + return l_result; + end; + + member function to_be_within(a_dist yminterval_unconstrained) return ut_be_within is + l_result ut_be_within; + begin + l_result := ut_be_within(a_dist); + l_result.expectation := self; + return l_result; + end; + + member function to_be_within_pct(a_dist number) return ut_be_within_pct is + l_result ut_be_within_pct; + begin + l_result := ut_be_within_pct(a_dist); + l_result.expectation := self; + return l_result; + end; + + member function not_to_be_within(a_dist number) return ut_be_within is + l_result ut_be_within; + begin + l_result := treat( ut_be_within(a_dist).negated() as ut_be_within); + l_result.expectation := self; + return l_result; + end; + + member function not_to_be_within(a_dist dsinterval_unconstrained) return ut_be_within is + l_result ut_be_within; + begin + l_result := treat( ut_be_within(a_dist).negated() as ut_be_within); + l_result.expectation := self; + return l_result; + end; + + member function not_to_be_within(a_dist yminterval_unconstrained) return ut_be_within is + l_result ut_be_within; + begin + l_result := treat( ut_be_within(a_dist).negated() as ut_be_within); + l_result.expectation := self; + return l_result; + end; + + member function not_to_be_within_pct(a_dist number) return ut_be_within_pct is + l_result ut_be_within_pct; + begin + l_result := treat( ut_be_within_pct(a_dist).negated() as ut_be_within_pct); + l_result.expectation := self; + return l_result; + end; + end; / + diff --git a/source/expectations/ut_expectation.tps b/source/expectations/ut_expectation.tps index 37fefd085..a423c39a4 100644 --- a/source/expectations/ut_expectation.tps +++ b/source/expectations/ut_expectation.tps @@ -1,7 +1,7 @@ -create or replace type ut_expectation authid current_user as object( +create or replace noneditionable type ut_expectation force under ut_expectation_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -15,13 +15,7 @@ create or replace type ut_expectation authid current_user as object( See the License for the specific language governing permissions and limitations under the License. */ - actual_data ut_data_value, - description varchar2(4000 char), - - --base matcher executors - member procedure to_(self in ut_expectation, a_matcher ut_matcher), - member procedure not_to(self in ut_expectation, a_matcher ut_matcher), - + --shortcuts member procedure to_be_null(self in ut_expectation), member procedure to_be_not_null(self in ut_expectation), @@ -55,6 +49,7 @@ create or replace type ut_expectation authid current_user as object( member procedure to_equal(self in ut_expectation, a_expected yminterval_unconstrained, a_nulls_are_equal boolean := null), member procedure to_equal(self in ut_expectation, a_expected dsinterval_unconstrained, a_nulls_are_equal boolean := null), member procedure to_equal(self in ut_expectation, a_expected json_element_t, a_nulls_are_equal boolean := null), + member procedure to_equal(self in ut_expectation, a_expected json, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_nulls_are_equal boolean := null), member procedure not_to_equal(self in ut_expectation, a_expected anydata, a_exclude varchar2, a_nulls_are_equal boolean := null), @@ -168,8 +163,16 @@ create or replace type ut_expectation authid current_user as object( member procedure to_contain(self in ut_expectation, a_expected sys_refcursor), member procedure not_to_contain(self in ut_expectation, a_expected sys_refcursor), member procedure to_contain(self in ut_expectation, a_expected anydata), - member procedure not_to_contain(self in ut_expectation, a_expected anydata) + member procedure not_to_contain(self in ut_expectation, a_expected anydata), + member function to_be_within(a_dist number) return ut_be_within, + member function to_be_within(a_dist dsinterval_unconstrained) return ut_be_within, + member function to_be_within(a_dist yminterval_unconstrained) return ut_be_within, + member function to_be_within_pct(a_dist number) return ut_be_within_pct, + member function not_to_be_within(a_dist number) return ut_be_within, + member function not_to_be_within(a_dist dsinterval_unconstrained) return ut_be_within, + member function not_to_be_within(a_dist yminterval_unconstrained) return ut_be_within, + member function not_to_be_within_pct(a_dist number) return ut_be_within_pct ) not final / diff --git a/source/expectations/ut_expectation_base.tpb b/source/expectations/ut_expectation_base.tpb new file mode 100644 index 000000000..5ed630c63 --- /dev/null +++ b/source/expectations/ut_expectation_base.tpb @@ -0,0 +1,45 @@ +create or replace noneditionable type body ut_expectation_base as + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + member procedure to_(self in ut_expectation_base, a_matcher ut_matcher_base) is + l_expectation_result boolean; + l_matcher ut_matcher := treat(a_matcher as ut_matcher); + l_message varchar2(32767); + begin + if l_matcher.is_negated() then + self.not_to( a_matcher ); + else + l_expectation_result := l_matcher.run_matcher( self.actual_data ); + l_expectation_result := coalesce(l_expectation_result,false); + l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message( self.actual_data ) ); + ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) ); + end if; + end; + + member procedure not_to(self in ut_expectation_base, a_matcher ut_matcher_base) is + l_expectation_result boolean; + l_matcher ut_matcher := treat(a_matcher as ut_matcher); + l_message varchar2(32767); + begin + l_expectation_result := coalesce( l_matcher.run_matcher_negated( self.actual_data ), false ); + + l_message := coalesce( l_matcher.error_message( self.actual_data ), l_matcher.failure_message_when_negated( self.actual_data ) ); + ut_expectation_processor.add_expectation_result( ut_expectation_result( ut_utils.to_test_result( l_expectation_result ), self.description, l_message ) ); + end; +end; +/ diff --git a/source/expectations/ut_expectation_base.tps b/source/expectations/ut_expectation_base.tps new file mode 100644 index 000000000..86f274495 --- /dev/null +++ b/source/expectations/ut_expectation_base.tps @@ -0,0 +1,26 @@ +create or replace noneditionable type ut_expectation_base authid current_user as object( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + actual_data ut_data_value, + description varchar2(4000 char), + + --base matcher executors + member procedure to_(self in ut_expectation_base, a_matcher ut_matcher_base), + member procedure not_to(self in ut_expectation_base, a_matcher ut_matcher_base) +) not final not instantiable +/ diff --git a/source/expectations/ut_expectation_compound.tpb b/source/expectations/ut_expectation_compound.tpb index 5e7268bd9..d76010f80 100644 --- a/source/expectations/ut_expectation_compound.tpb +++ b/source/expectations/ut_expectation_compound.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_expectation_compound as +create or replace noneditionable type body ut_expectation_compound as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -34,162 +34,68 @@ create or replace type body ut_expectation_compound as end; - member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound is - l_result ut_expectation_compound := self; + member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal is + l_result ut_equal; begin - l_result.matcher := ut_equal(a_expected, a_nulls_are_equal); + l_result := ut_equal(a_expected, a_nulls_are_equal); + l_result.expectation := self; return l_result; end; - member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound is - l_result ut_expectation_compound := self; + member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal is + l_result ut_matcher; begin - l_result.matcher := ut_equal(a_expected, a_nulls_are_equal).negated(); - return l_result; - end; - - member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := ut_equal(a_expected, a_nulls_are_equal); - return l_result; - end; - - member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := ut_equal(a_expected, a_nulls_are_equal).negated(); - return l_result; - end; - - member function to_contain(a_expected sys_refcursor) return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := ut_contain(a_expected); - return l_result; - end; - - member function not_to_contain(a_expected sys_refcursor) return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := ut_contain(a_expected).negated(); - return l_result; - end; - - member function to_contain(a_expected anydata) return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := ut_contain(a_expected); - return l_result; + l_result := ut_equal(a_expected, a_nulls_are_equal).negated(); + l_result.expectation := self; + return treat(l_result as ut_equal); end; - member function not_to_contain(a_expected anydata) return ut_expectation_compound is - l_result ut_expectation_compound := self; + member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal is + l_result ut_equal; begin - l_result.matcher := ut_contain(a_expected).negated(); + l_result := ut_equal(a_expected, a_nulls_are_equal); + l_result.expectation := self; return l_result; end; - member function include(a_items varchar2) return ut_expectation_compound is + member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal is + l_result ut_matcher; begin - return include( ut_varchar2_list( a_items ) ); + l_result := ut_equal(a_expected, a_nulls_are_equal).negated(); + l_result.expectation := self; + return treat(l_result as ut_equal); end; - member function include(a_items ut_varchar2_list) return ut_expectation_compound is - l_result ut_expectation_compound := self; + member function to_contain(a_expected sys_refcursor) return ut_contain is + l_result ut_contain; begin - l_result.matcher := treat(l_result.matcher as ut_equal).include(a_items); + l_result := ut_contain(a_expected); + l_result.expectation := self; return l_result; end; - member procedure include(self in ut_expectation_compound, a_items varchar2) is - begin - include( ut_varchar2_list( a_items ) ); - end; - - member procedure include(self in ut_expectation_compound, a_items ut_varchar2_list) is + member function not_to_contain(a_expected sys_refcursor) return ut_contain is + l_result ut_matcher; begin - self.to_( treat(matcher as ut_equal).include(a_items) ); + l_result := ut_contain(a_expected).negated(); + l_result.expectation := self; + return treat(l_result as ut_contain); end; - - member function exclude(a_items varchar2) return ut_expectation_compound is - begin - return exclude( ut_varchar2_list( a_items ) ); - end; - - member function exclude(a_items ut_varchar2_list) return ut_expectation_compound is - l_result ut_expectation_compound := self; + + member function to_contain(a_expected anydata) return ut_contain is + l_result ut_contain; begin - l_result.matcher := treat(l_result.matcher as ut_equal).exclude(a_items); + l_result := ut_contain(a_expected); + l_result.expectation := self; return l_result; end; - member procedure exclude(self in ut_expectation_compound, a_items varchar2) is - begin - exclude( ut_varchar2_list( a_items ) ); - end; - - member procedure exclude(self in ut_expectation_compound, a_items ut_varchar2_list) is - begin - self.to_( treat(matcher as ut_equal).exclude(a_items) ); - end; - - member function unordered return ut_expectation_compound is - l_result ut_expectation_compound := self; - begin - l_result.matcher := treat(l_result.matcher as ut_equal).unordered(); - return l_result; - end; - - member procedure unordered(self in ut_expectation_compound) is - begin - self.to_( treat(matcher as ut_equal).unordered() ); - end; - - member function join_by(a_columns varchar2) return ut_expectation_compound is - begin - return join_by( ut_varchar2_list( a_columns ) ); - end; - - member function join_by(a_columns ut_varchar2_list) return ut_expectation_compound is - l_result ut_expectation_compound; - begin - l_result := self; - l_result.matcher := treat(l_result.matcher as ut_equal).join_by(a_columns); - return l_result; - end; - - member procedure join_by(self in ut_expectation_compound, a_columns varchar2) is - begin - join_by( ut_varchar2_list( a_columns ) ); - end; - - member procedure join_by(self in ut_expectation_compound, a_columns ut_varchar2_list) is - begin - self.to_( treat(matcher as ut_equal).join_by(a_columns) ); - end; - - member function unordered_columns return ut_expectation_compound is - l_result ut_expectation_compound; - begin - l_result := self; - l_result.matcher := treat(l_result.matcher as ut_equal).unordered_columns; - return l_result; - end; - - member procedure unordered_columns(self in ut_expectation_compound) is - begin - self.to_( treat(matcher as ut_equal).unordered_columns ); - end; - - member function uc return ut_expectation_compound is - begin - return unordered_columns; - end; - - member procedure uc(self in ut_expectation_compound) is + member function not_to_contain(a_expected anydata) return ut_contain is + l_result ut_matcher; begin - unordered_columns; + l_result := ut_contain(a_expected).negated(); + l_result.expectation := self; + return treat(l_result as ut_contain); end; end; diff --git a/source/expectations/ut_expectation_compound.tps b/source/expectations/ut_expectation_compound.tps index fc413e3fd..54de48dd4 100644 --- a/source/expectations/ut_expectation_compound.tps +++ b/source/expectations/ut_expectation_compound.tps @@ -1,7 +1,7 @@ -create or replace type ut_expectation_compound force under ut_expectation( +create or replace noneditionable type ut_expectation_compound under ut_expectation( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,33 +22,15 @@ create or replace type ut_expectation_compound force under ut_expectation( member procedure to_have_count(self in ut_expectation_compound, a_expected integer), member procedure not_to_have_count(self in ut_expectation_compound, a_expected integer), - member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound, - member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_expectation_compound, - member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound, - member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_expectation_compound, - member function to_contain(a_expected sys_refcursor) return ut_expectation_compound, - member function not_to_contain(a_expected sys_refcursor) return ut_expectation_compound, - member function to_contain(a_expected anydata) return ut_expectation_compound, - member function not_to_contain(a_expected anydata) return ut_expectation_compound, - - member function include(a_items varchar2) return ut_expectation_compound, - member function include(a_items ut_varchar2_list) return ut_expectation_compound, - member procedure include(self in ut_expectation_compound, a_items varchar2), - member procedure include(self in ut_expectation_compound, a_items ut_varchar2_list), - member function exclude(a_items varchar2) return ut_expectation_compound, - member function exclude(a_items ut_varchar2_list) return ut_expectation_compound, - member procedure exclude(self in ut_expectation_compound, a_items varchar2), - member procedure exclude(self in ut_expectation_compound, a_items ut_varchar2_list), - member function unordered return ut_expectation_compound, - member procedure unordered(self in ut_expectation_compound), - member function join_by(a_columns varchar2) return ut_expectation_compound, - member function join_by(a_columns ut_varchar2_list) return ut_expectation_compound, - member procedure join_by(self in ut_expectation_compound, a_columns varchar2), - member procedure join_by(self in ut_expectation_compound, a_columns ut_varchar2_list), - - member function unordered_columns return ut_expectation_compound, - member procedure unordered_columns(self in ut_expectation_compound), - member function uc return ut_expectation_compound, - member procedure uc(self in ut_expectation_compound) + member function to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal, + member function not_to_equal(a_expected anydata, a_nulls_are_equal boolean := null) return ut_equal, + member function to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal, + member function not_to_equal(a_expected sys_refcursor, a_nulls_are_equal boolean := null) return ut_equal, + member function to_contain(a_expected sys_refcursor) return ut_contain, + member function not_to_contain(a_expected sys_refcursor) return ut_contain, + member function to_contain(a_expected anydata) return ut_contain, + member function not_to_contain(a_expected anydata) return ut_contain ) / + + diff --git a/source/expectations/ut_expectation_json.tpb b/source/expectations/ut_expectation_json.tpb index 49c0c2e1f..68d4085ef 100644 --- a/source/expectations/ut_expectation_json.tpb +++ b/source/expectations/ut_expectation_json.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_expectation_json as +create or replace noneditionable type body ut_expectation_json as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/expectations/ut_expectation_json.tps b/source/expectations/ut_expectation_json.tps index dd3aa5b25..c75154bc8 100644 --- a/source/expectations/ut_expectation_json.tps +++ b/source/expectations/ut_expectation_json.tps @@ -1,7 +1,7 @@ -create or replace type ut_expectation_json under ut_expectation( +create or replace noneditionable type ut_expectation_json under ut_expectation( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -24,4 +24,4 @@ create or replace type ut_expectation_json under ut_expectation( member procedure to_have_count(self in ut_expectation_json, a_expected integer), member procedure not_to_have_count(self in ut_expectation_json, a_expected integer) ) -/ +/ \ No newline at end of file diff --git a/source/install.sql b/source/install.sql index 3249e3e49..9af0a4f66 100644 --- a/source/install.sql +++ b/source/install.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -50,6 +50,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/types/ut_key_value_pairs.tps' @@install_component.sql 'core/types/ut_reporter_info.tps' @@install_component.sql 'core/types/ut_reporters_info.tps' +@@install_component.sql 'core/types/ut_stack.tps' +@@install_component.sql 'core/types/ut_stack.tpb' @@install_component.sql 'core/ut_utils.pks' @@install_component.sql 'core/ut_metadata.pks' @@install_component.sql 'core/ut_savepoint_seq.sql' @@ -89,7 +91,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/types/ut_run.tps' @@install_component.sql 'core/types/ut_reporter_base.tps' @@install_component.sql 'core/types/ut_reporters.tps' - +@@install_component.sql 'core/types/ut_path_item.tps' +@@install_component.sql 'core/types/ut_path_items.tps' @@install_component.sql 'expectations/json_objects_specs.sql' @@install_component.sql 'expectations/matchers/ut_matcher_options_items.tps' @@ -119,6 +122,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/output_buffers/ut_output_table_buffer.tpb' @@install_component.sql 'core/output_buffers/ut_output_clob_table_buffer.tps' @@install_component.sql 'core/output_buffers/ut_output_clob_table_buffer.tpb' +@@install_component.sql 'core/output_buffers/ut_output_bulk_buffer.tps' +@@install_component.sql 'core/output_buffers/ut_output_bulk_buffer.tpb' @@install_component.sql 'core/types/ut_output_reporter_base.tps' @@ -150,6 +155,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/ut_suite_cache_seq.sql' @@install_component.sql 'core/ut_suite_cache.sql' +@@install_component.sql 'core/ut_suite_tag_filter.pks' +@@install_component.sql 'core/ut_suite_tag_filter.pkb' @@install_component.sql 'core/ut_suite_cache_manager.pks' @@install_component.sql 'core/ut_suite_cache_manager.pkb' @@install_component.sql 'core/ut_suite_builder.pks' @@ -165,9 +172,6 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex prompt Installing PLSQL profiler objects into &&ut3_owner schema @@core/coverage/proftab.sql -prompt Installing PLSQL profiler objects into &&ut3_owner schema -@@core/coverage/proftab.sql - prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@core/coverage/dbms_plssqlcode.sql @@ -210,6 +214,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'core/types/ut_executable_test.tpb' @@install_component.sql 'core/types/ut_console_reporter_base.tps' @@install_component.sql 'core/types/ut_console_reporter_base.tpb' +@@install_component.sql 'core/types/ut_path_item.tpb' --expectations and matchers @@install_component.sql 'expectations/data_values/ut_compound_data_tmp.sql' @@ -238,8 +243,14 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/data_values/ut_data_value_xmltype.tps' @@install_component.sql 'expectations/data_values/ut_compound_data_helper.pks' @@install_component.sql 'expectations/data_values/ut_data_value_json.tps' +@@install_component.sql 'expectations/matchers/ut_matcher_base.tps' +@@install_component.sql 'expectations/ut_expectation_base.tps' @@install_component.sql 'expectations/matchers/ut_matcher.tps' @@install_component.sql 'expectations/matchers/ut_comparison_matcher.tps' +@@install_component.sql 'expectations/matchers/ut_be_within_pct.tps' +@@install_component.sql 'expectations/matchers/ut_be_within.tps' +@@install_component.sql 'expectations/matchers/ut_be_within_helper.pks' +@@install_component.sql 'expectations/ut_expectation.tps' @@install_component.sql 'expectations/matchers/ut_be_false.tps' @@install_component.sql 'expectations/matchers/ut_be_greater_or_equal.tps' @@install_component.sql 'expectations/matchers/ut_be_greater_than.tps' @@ -255,7 +266,6 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/matchers/ut_be_between.tps' @@install_component.sql 'expectations/matchers/ut_be_empty.tps' @@install_component.sql 'expectations/matchers/ut_match.tps' -@@install_component.sql 'expectations/ut_expectation.tps' @@install_component.sql 'expectations/data_values/ut_json_leaf.tpb' @@install_component.sql 'expectations/data_values/ut_json_tree_details.tpb' @@install_component.sql 'expectations/data_values/ut_cursor_column.tpb' @@ -295,11 +305,15 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'expectations/matchers/ut_be_null.tpb' @@install_component.sql 'expectations/matchers/ut_be_true.tpb' @@install_component.sql 'expectations/matchers/ut_equal.tpb' +@@install_component.sql 'expectations/matchers/ut_be_within_pct.tpb' +@@install_component.sql 'expectations/matchers/ut_be_within.tpb' +@@install_component.sql 'expectations/matchers/ut_be_within_helper.pkb' @@install_component.sql 'expectations/matchers/ut_contain.tpb' @@install_component.sql 'expectations/matchers/ut_have_count.tpb' @@install_component.sql 'expectations/matchers/ut_be_between.tpb' @@install_component.sql 'expectations/matchers/ut_be_empty.tpb' @@install_component.sql 'expectations/matchers/ut_match.tpb' +@@install_component.sql 'expectations/ut_expectation_base.tpb' @@install_component.sql 'expectations/ut_expectation.tpb' @@install_component.sql 'expectations/ut_expectation_compound.tpb' @@install_component.sql 'expectations/ut_expectation_json.tpb' @@ -332,6 +346,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'reporters/ut_xunit_reporter.tpb' @@install_component.sql 'reporters/ut_sonar_test_reporter.tps' @@install_component.sql 'reporters/ut_sonar_test_reporter.tpb' +@@install_component.sql 'reporters/ut_tap_reporter.tps' +@@install_component.sql 'reporters/ut_tap_reporter.tpb' @@install_component.sql 'reporters/ut_coverage_html_reporter.tps' @@install_component.sql 'reporters/ut_coverage_report_html_helper.pks' @@ -339,8 +355,6 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'reporters/ut_coverage_html_reporter.tpb' @@install_component.sql 'reporters/ut_coverage_sonar_reporter.tps' @@install_component.sql 'reporters/ut_coverage_sonar_reporter.tpb' -@@install_component.sql 'reporters/ut_coveralls_reporter.tps' -@@install_component.sql 'reporters/ut_coveralls_reporter.tpb' @@install_component.sql 'reporters/ut_coverage_cobertura_reporter.tps' @@install_component.sql 'reporters/ut_coverage_cobertura_reporter.tpb' @@install_component.sql 'reporters/ut_realtime_reporter.tps' @@ -357,6 +371,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'api/be_not_null.syn' @@install_component.sql 'api/be_null.syn' @@install_component.sql 'api/be_true.syn' +@@install_component.sql 'api/be_within_pct.syn' +@@install_component.sql 'api/be_within.syn' @@install_component.sql 'api/equal.syn' @@install_component.sql 'api/have_count.syn' @@install_component.sql 'api/match.syn' diff --git a/source/install_above_12_1.sql b/source/install_above_12_1.sql index 69e47ce9a..5a1943358 100644 --- a/source/install_above_12_1.sql +++ b/source/install_above_12_1.sql @@ -1,3 +1,20 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + def FILE_NAME = '&&1' column SCRIPT_NAME new_value SCRIPT_NAME noprint diff --git a/source/install_component.sql b/source/install_component.sql index 857c1771c..1189df496 100644 --- a/source/install_component.sql +++ b/source/install_component.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/install_ddl_trigger.sql b/source/install_ddl_trigger.sql index 843ee0c07..b23b4bc63 100644 --- a/source/install_ddl_trigger.sql +++ b/source/install_ddl_trigger.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/install_headless.sql b/source/install_headless.sql index e8cf00705..be1e5becf 100644 --- a/source/install_headless.sql +++ b/source/install_headless.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/install_headless_with_trigger.sql b/source/install_headless_with_trigger.sql index 16d81f3c6..2c05403c5 100644 --- a/source/install_headless_with_trigger.sql +++ b/source/install_headless_with_trigger.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_ansiconsole_helper.pkb b/source/reporters/ut_ansiconsole_helper.pkb index b015e50be..e8ee34531 100644 --- a/source/reporters/ut_ansiconsole_helper.pkb +++ b/source/reporters/ut_ansiconsole_helper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_ansiconsole_helper as +create or replace noneditionable package body ut_ansiconsole_helper as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_ansiconsole_helper.pks b/source/reporters/ut_ansiconsole_helper.pks index 7c83ca73b..e882de293 100644 --- a/source/reporters/ut_ansiconsole_helper.pks +++ b/source/reporters/ut_ansiconsole_helper.pks @@ -1,7 +1,7 @@ -create or replace package ut_ansiconsole_helper as +create or replace noneditionable package ut_ansiconsole_helper as /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_coverage_cobertura_reporter.tpb b/source/reporters/ut_coverage_cobertura_reporter.tpb index d8aae7f7a..45c1935b1 100644 --- a/source/reporters/ut_coverage_cobertura_reporter.tpb +++ b/source/reporters/ut_coverage_cobertura_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_coverage_cobertura_reporter is +create or replace noneditionable type body ut_coverage_cobertura_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ create or replace type body ut_coverage_cobertura_reporter is self in out nocopy ut_coverage_cobertura_reporter ) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; @@ -29,11 +29,19 @@ create or replace type body ut_coverage_cobertura_reporter is l_report_lines ut_varchar2_list; l_coverage_data ut_coverage.t_coverage; - function get_lines_xml(a_unit_coverage ut_coverage.t_unit_coverage) return clob is + function get_line_rate(a_lines_covered in integer, a_lines_hit in integer) return varchar2 is + begin + return to_char(round((case a_lines_covered when 0 then 0 else a_lines_covered/a_lines_hit end), 17), rpad('FM0.0',20,'9') , 'NLS_NUMERIC_CHARACTERS=''. '''); + end; + + procedure get_lines_xml(a_unit_coverage ut_coverage.t_unit_coverage, + a_lines_result in out nocopy clob, a_lines_hits out number, a_lines_total out number) is l_file_part varchar2(32767); l_result clob; l_line_no binary_integer; l_pct integer; + l_lines_hits integer := 0; + l_lines_total integer := 0; begin dbms_lob.createtemporary(l_result, true); l_line_no := a_unit_coverage.lines.first; @@ -41,11 +49,14 @@ create or replace type body ut_coverage_cobertura_reporter is for i in 1 .. a_unit_coverage.total_lines loop ut_utils.append_to_clob(l_result, ''||chr(10)); end loop; + l_lines_hits:=0; + l_lines_total:= a_unit_coverage.total_lines; else while l_line_no is not null loop if a_unit_coverage.lines(l_line_no).executions = 0 then l_file_part := ''||chr(10); else + l_lines_hits:= l_lines_hits+1; l_file_part := '' ); @@ -113,9 +134,12 @@ create or replace type body ut_coverage_cobertura_reporter is while l_unit is not null loop l_obj_name := a_coverage_data.objects(l_unit).name; + dbms_lob.createtemporary(l_lines_xml, true); + get_lines_xml(a_coverage_data.objects(l_unit),l_lines_xml,l_line_hits,l_line_total); + ut_utils.append_to_list( l_result, - '' + '' ); ut_utils.append_to_list( @@ -126,13 +150,13 @@ create or replace type body ut_coverage_cobertura_reporter is ut_utils.append_to_list( l_result, '' + ||dbms_xmlgen.convert(l_unit)||'" line-rate="'||get_line_rate(l_line_hits,l_line_total)||'" branch-rate="0.0" complexity="0.0">' ); ut_utils.append_to_list(l_result, ''); - - ut_utils.append_to_list( l_result, get_lines_xml(a_coverage_data.objects(l_unit)) ); - + ut_utils.append_to_list( l_result,l_lines_xml); + dbms_lob.freetemporary(l_lines_xml); + ut_utils.append_to_list(l_result, c_lines_footer); ut_utils.append_to_list(l_result, c_class_footer); ut_utils.append_to_list(l_result, c_classes_footer); diff --git a/source/reporters/ut_coverage_cobertura_reporter.tps b/source/reporters/ut_coverage_cobertura_reporter.tps index ec5cef2cb..799840a55 100644 --- a/source/reporters/ut_coverage_cobertura_reporter.tps +++ b/source/reporters/ut_coverage_cobertura_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_coverage_cobertura_reporter under ut_coverage_reporter_base( +create or replace noneditionable type ut_coverage_cobertura_reporter under ut_coverage_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_coverage_html_reporter.tpb b/source/reporters/ut_coverage_html_reporter.tpb index 9e51b5cc2..90419371a 100644 --- a/source/reporters/ut_coverage_html_reporter.tpb +++ b/source/reporters/ut_coverage_html_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_coverage_html_reporter is +create or replace noneditionable type body ut_coverage_html_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ create or replace type body ut_coverage_html_reporter is a_html_report_assets_path varchar2 := null ) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); self.project_name := a_project_name; assets_path := nvl(a_html_report_assets_path, ut_coverage_report_html_helper.get_default_html_assets_path()); return; diff --git a/source/reporters/ut_coverage_html_reporter.tps b/source/reporters/ut_coverage_html_reporter.tps index f6bdcc249..23cf45c0b 100644 --- a/source/reporters/ut_coverage_html_reporter.tps +++ b/source/reporters/ut_coverage_html_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_coverage_html_reporter under ut_coverage_reporter_base( +create or replace noneditionable type ut_coverage_html_reporter under ut_coverage_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_coverage_report_html_helper.pkb b/source/reporters/ut_coverage_report_html_helper.pkb index f4b633c1f..99c36ab73 100644 --- a/source/reporters/ut_coverage_report_html_helper.pkb +++ b/source/reporters/ut_coverage_report_html_helper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_coverage_report_html_helper is +create or replace noneditionable package body ut_coverage_report_html_helper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -90,7 +90,7 @@ create or replace package body ut_coverage_report_html_helper is function object_id(a_object_full_name varchar2) return varchar2 is begin - return rawtohex(dbms_crypto.hash(src => utl_raw.cast_to_raw(a_object_full_name), typ => dbms_crypto.hash_md5)); + return rawtohex(dbms_crypto.hash(src => utl_raw.cast_to_raw(a_object_full_name), typ => dbms_crypto.HASH_SH256)); end; function link_to_source_file(a_object_full_name varchar2) return varchar2 is @@ -134,9 +134,11 @@ create or replace package body ut_coverage_report_html_helper is l_file_part varchar2(32767); l_result ut_varchar2_rows := ut_varchar2_rows(); l_coverage_pct number(5, 2); - l_coverage_block_pct number(5, 2); l_hits varchar2(30); l_blocks varchar2(30); + l_line_text varchar2(32767); + e_buffer_too_small exception; + pragma exception_init ( e_buffer_too_small, -19011 ); begin l_coverage_pct := coverage_pct(a_coverage_unit.covered_lines, a_coverage_unit.uncovered_lines); @@ -148,10 +150,16 @@ create or replace package body ut_coverage_report_html_helper is ut_utils.append_to_list(l_result, l_file_part); for line_no in 1 .. a_source_code.count loop + begin + l_line_text := dbms_xmlgen.convert(a_source_code(line_no)); + exception + when e_buffer_too_small then + l_line_text := dbms_xmlgen.convert(to_clob(a_source_code(line_no))); + end; if not a_coverage_unit.lines.exists(line_no) then l_file_part := '
  • - ' || (dbms_xmlgen.convert(a_source_code(line_no))) || + ' || l_line_text || '
  • '; else l_hits := to_char(a_coverage_unit.lines(line_no).executions); @@ -188,7 +196,7 @@ create or replace package body ut_coverage_report_html_helper is ''; end if; l_file_part := l_file_part || ' - ' || (dbms_xmlgen.convert(a_source_code(line_no))) || + ' || l_line_text || ''; end if; ut_utils.append_to_list(l_result, l_file_part); @@ -222,9 +230,8 @@ create or replace package body ut_coverage_report_html_helper is l_file_part varchar2(32767); l_title varchar2(100) := 'All files'; l_coverage_pct number(5, 2); - l_coverage_block_pct number(5, 2); l_result ut_varchar2_rows; - l_id varchar2(50) := object_id(a_title); + l_id varchar2(100) := object_id(a_title); l_unit_coverage ut_coverage.t_unit_coverage; l_unit ut_coverage.t_object_name; begin diff --git a/source/reporters/ut_coverage_report_html_helper.pks b/source/reporters/ut_coverage_report_html_helper.pks index 48dd76acf..8463e3e89 100644 --- a/source/reporters/ut_coverage_report_html_helper.pks +++ b/source/reporters/ut_coverage_report_html_helper.pks @@ -1,7 +1,7 @@ -create or replace package ut_coverage_report_html_helper authid current_user is +create or replace noneditionable package ut_coverage_report_html_helper authid current_user is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_coverage_sonar_reporter.tpb b/source/reporters/ut_coverage_sonar_reporter.tpb index 68f4a7989..a070572b7 100644 --- a/source/reporters/ut_coverage_sonar_reporter.tpb +++ b/source/reporters/ut_coverage_sonar_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_coverage_sonar_reporter is +create or replace noneditionable type body ut_coverage_sonar_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ create or replace type body ut_coverage_sonar_reporter is self in out nocopy ut_coverage_sonar_reporter ) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; @@ -84,6 +84,12 @@ create or replace type body ut_coverage_sonar_reporter is end; begin +-- execute immediate 'alter session set statistics_level=all'; +-- dbms_hprof.start_profiling( +-- location => 'PLSHPROF_DIR' +-- , filename => 'profiler_utPLSQL_run_on_'||$$plsql_unit||'_'||rawtohex(self.id)||'.txt' +-- ); +-- ut_coverage.coverage_stop(); self.print_text_lines( @@ -92,13 +98,14 @@ create or replace type body ut_coverage_sonar_reporter is a_run ) ); +-- dbms_hprof.stop_profiling; end; overriding member function get_description return varchar2 as begin - return 'Generates a JSON coverage report providing information on code coverage with line numbers.' || chr(10) || - 'Designed for [SonarQube](https://about.sonarqube.com/) to report coverage.' || chr(10) || - 'JSON format returned conforms with the Sonar specification: https://docs.sonarqube.org/display/SONAR/Generic+Test+Data'; + return 'Generates a XML coverage report providing information on code coverage with line numbers.' || chr(10) || + 'Designed for [SonarQube](https://www.sonarqube.org/) to report coverage.' || chr(10) || + 'XML format returned conforms with the Sonar specification: https://docs.sonarqube.org/latest/analysis/generic-test/'; end; end; diff --git a/source/reporters/ut_coverage_sonar_reporter.tps b/source/reporters/ut_coverage_sonar_reporter.tps index aa21cafe3..fd160bdb5 100644 --- a/source/reporters/ut_coverage_sonar_reporter.tps +++ b/source/reporters/ut_coverage_sonar_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_coverage_sonar_reporter under ut_coverage_reporter_base( +create or replace noneditionable type ut_coverage_sonar_reporter under ut_coverage_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ create or replace type ut_coverage_sonar_reporter under ut_coverage_reporter_bas /** * Builds a coverage XML report that follows rules described in - * https://docs.sonarqube.org/display/SONAR/Generic+Test+Data + * https://docs.sonarqube.org/latest/analysis/generic-test/ */ constructor function ut_coverage_sonar_reporter( self in out nocopy ut_coverage_sonar_reporter diff --git a/source/reporters/ut_coveralls_reporter.tpb b/source/reporters/ut_coveralls_reporter.tpb deleted file mode 100644 index f2cb73abe..000000000 --- a/source/reporters/ut_coveralls_reporter.tpb +++ /dev/null @@ -1,104 +0,0 @@ -create or replace type body ut_coveralls_reporter is - /* - utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - constructor function ut_coveralls_reporter( - self in out nocopy ut_coveralls_reporter - ) return self as result is - begin - self.init($$plsql_unit); - return; - end; - - overriding member procedure after_calling_run(self in out nocopy ut_coveralls_reporter, a_run in ut_run) as - - function get_lines_json(a_unit_coverage ut_coverage.t_unit_coverage) return ut_varchar2_rows is - l_file_part varchar2(32767); - l_result ut_varchar2_rows := ut_varchar2_rows(); - l_last_line_no binary_integer; - c_coverage_header constant varchar2(30) := '"coverage": ['; - c_null constant varchar2(4) := 'null'; - begin - ut_utils.append_to_list(l_result, c_coverage_header); - - l_last_line_no := a_unit_coverage.lines.last; - if l_last_line_no is null then - ut_utils.append_to_list( - l_result - , rpad( to_clob( '0' ), ( a_unit_coverage.total_lines * 3 ) - 2, ','||chr(10)||'0' ) - ); - else - for line_no in 1 .. l_last_line_no loop - if a_unit_coverage.lines.exists(line_no) then - l_file_part := to_char(a_unit_coverage.lines(line_no).executions); - else - l_file_part := c_null; - end if; - if line_no < l_last_line_no then - l_file_part := l_file_part ||','; - end if; - ut_utils.append_to_list(l_result, l_file_part); - end loop; - end if; - ut_utils.append_to_list(l_result, ']'); - return l_result; - end; - - function get_coverage_json( - a_coverage_data ut_coverage.t_coverage - ) return ut_varchar2_rows is - l_result ut_varchar2_rows := ut_varchar2_rows(); - l_unit ut_coverage.t_object_name; - c_coverage_header constant varchar2(30) := '{"source_files":['; - c_coverage_footer constant varchar2(30) := ']}'||chr(10)||' '; - begin - ut_utils.append_to_list(l_result, c_coverage_header); - l_unit := a_coverage_data.objects.first; - while l_unit is not null loop - ut_utils.append_to_list(l_result, '{ "name": "'||l_unit||'",'); - - ut_utils.append_to_list(l_result,get_lines_json(a_coverage_data.objects(l_unit))); - - ut_utils.append_to_list(l_result, '}'); - - l_unit := a_coverage_data.objects.next(l_unit); - if l_unit is not null then - ut_utils.append_to_list(l_result, ','); - end if; - end loop; - ut_utils.append_to_list(l_result, c_coverage_footer); - return l_result; - end; - begin - ut_coverage.coverage_stop(); - - self.print_text_lines( - get_coverage_json( - ut_coverage.get_coverage_data(a_run.coverage_options) - ) - ); - end; - - overriding member function get_description return varchar2 as - begin - return 'Generates a JSON coverage report providing information on code coverage with line numbers.' || chr(10) || - 'Designed for [Coveralls](https://coveralls.io/).' || chr(10) || - 'JSON format conforms with specification: https://docs.coveralls.io/api-introduction'; - end; - -end; -/ diff --git a/source/reporters/ut_debug_reporter.tpb b/source/reporters/ut_debug_reporter.tpb index 365f77755..547437cde 100644 --- a/source/reporters/ut_debug_reporter.tpb +++ b/source/reporters/ut_debug_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_debug_reporter is +create or replace noneditionable type body ut_debug_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -71,7 +71,7 @@ create or replace type body ut_debug_reporter is member function event_item_to_clob(a_event_item ut_event_item) return clob is l_clob clob; begin - select xmlserialize( content deletexml(xmltype(a_event_item),'/*/ITEMS|/*/ALL_EXPECTATIONS|/*/FAILED_EXPECTATIONS') as clob indent size = 2 ) into l_clob from dual; + select /*+ no_parallel */ xmlserialize( content deletexml(xmltype(a_event_item),'/*/ITEMS|/*/ALL_EXPECTATIONS|/*/FAILED_EXPECTATIONS') as clob indent size = 2 ) into l_clob from dual; return l_clob; end; diff --git a/source/reporters/ut_debug_reporter.tps b/source/reporters/ut_debug_reporter.tps index 5fbab61fb..edbf1cdc6 100644 --- a/source/reporters/ut_debug_reporter.tps +++ b/source/reporters/ut_debug_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_debug_reporter under ut_output_reporter_base( +create or replace noneditionable type ut_debug_reporter under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_documentation_reporter.tpb b/source/reporters/ut_documentation_reporter.tpb index 7210e1cb6..83152ea1c 100644 --- a/source/reporters/ut_documentation_reporter.tpb +++ b/source/reporters/ut_documentation_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_documentation_reporter is +create or replace noneditionable type body ut_documentation_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -68,7 +68,11 @@ create or replace type body ut_documentation_reporter is l_message := coalesce(a_test.description, a_test.name)||' ['||round(a_test.execution_time,3)||' sec]'; --if test failed, then add it to the failures list, print failure with number if a_test.result = ut_utils.gc_disabled then - self.print_yellow_text(l_message || ' (DISABLED)'); + self.print_yellow_text(l_message || ' (DISABLED'|| + case when a_test.disabled_reason is not null + then ' - '||a_test.disabled_reason + else null + end || ')'); elsif a_test.result = ut_utils.gc_success then self.print_green_text(l_message); elsif a_test.result > ut_utils.gc_success then @@ -198,7 +202,7 @@ create or replace type body ut_documentation_reporter is begin print_failures_details(a_run); print_warnings(a_run); - self.print_text('Finished in ' || a_run.execution_time || ' seconds'); + self.print_text('Finished in ' || ut_utils.interval_to_text(numtodsinterval(a_run.execution_time,'second')) ); l_summary_text := a_run.results_count.total_count || ' tests, ' diff --git a/source/reporters/ut_documentation_reporter.tps b/source/reporters/ut_documentation_reporter.tps index 08097e7d4..bab9082c6 100644 --- a/source/reporters/ut_documentation_reporter.tps +++ b/source/reporters/ut_documentation_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_documentation_reporter under ut_console_reporter_base( +create or replace noneditionable type ut_documentation_reporter under ut_console_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_junit_reporter.tpb b/source/reporters/ut_junit_reporter.tpb index aa6786f6f..bf62dc564 100644 --- a/source/reporters/ut_junit_reporter.tpb +++ b/source/reporters/ut_junit_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_junit_reporter is +create or replace noneditionable type body ut_junit_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ create or replace type body ut_junit_reporter is constructor function ut_junit_reporter(self in out nocopy ut_junit_reporter) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; @@ -44,7 +44,13 @@ create or replace type body ut_junit_reporter is ' status="' || ut_utils.test_result_to_char(a_test.result) || '"' end || '>' ); if a_test.result = ut_utils.gc_disabled then - ut_utils.append_to_list( l_results, '' ); + if a_test.disabled_reason is not null then + ut_utils.append_to_list( l_results, '' ); + ut_utils.append_to_list( l_results, ut_utils.to_cdata( a_test.disabled_reason ) ); + ut_utils.append_to_list( l_results, '' ); + else + ut_utils.append_to_list( l_results, '' ); + end if; end if; if a_test.result = ut_utils.gc_error then ut_utils.append_to_list( l_results, ''); diff --git a/source/reporters/ut_junit_reporter.tps b/source/reporters/ut_junit_reporter.tps index ca6cf505b..a20913980 100644 --- a/source/reporters/ut_junit_reporter.tps +++ b/source/reporters/ut_junit_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_junit_reporter force under ut_output_reporter_base( +create or replace noneditionable type ut_junit_reporter force under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_realtime_reporter.tpb b/source/reporters/ut_realtime_reporter.tpb index 443c7c4e7..a8b7c566e 100644 --- a/source/reporters/ut_realtime_reporter.tpb +++ b/source/reporters/ut_realtime_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_realtime_reporter is +create or replace noneditionable type body ut_realtime_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ create or replace type body ut_realtime_reporter is self.print_node('objectName', a_test.item.object_name); self.print_node('procedureName', a_test.item.procedure_name); self.print_node('disabled', case when a_test.get_disabled_flag() then 'true' else 'false' end); + self.print_node('disabledReason', a_test.disabled_reason); self.print_node('name', a_test.name); self.print_node('description', a_test.description); self.print_node('testNumber', to_char(total_number_of_tests)); diff --git a/source/reporters/ut_realtime_reporter.tps b/source/reporters/ut_realtime_reporter.tps index 99efed387..b8686685b 100644 --- a/source/reporters/ut_realtime_reporter.tps +++ b/source/reporters/ut_realtime_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_realtime_reporter force under ut_output_reporter_base( +create or replace noneditionable type ut_realtime_reporter force under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_sonar_test_reporter.tpb b/source/reporters/ut_sonar_test_reporter.tpb index 6d76b5d41..da29b629d 100644 --- a/source/reporters/ut_sonar_test_reporter.tpb +++ b/source/reporters/ut_sonar_test_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_sonar_test_reporter is +create or replace noneditionable type body ut_sonar_test_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ create or replace type body ut_sonar_test_reporter is self in out nocopy ut_sonar_test_reporter ) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; @@ -47,7 +47,7 @@ create or replace type body ut_sonar_test_reporter is begin ut_utils.append_to_list( l_results, ''); if a_test.result = ut_utils.gc_disabled then - ut_utils.append_to_list( l_results, ''); + ut_utils.append_to_list( l_results, ''); elsif a_test.result = ut_utils.gc_error then ut_utils.append_to_list( l_results, ''); ut_utils.append_to_list( l_results, ut_utils.to_cdata( ut_utils.convert_collection( a_test.get_error_stack_traces() ) ) ); @@ -106,9 +106,9 @@ create or replace type body ut_sonar_test_reporter is overriding member function get_description return varchar2 as begin - return 'Generates a JSON report providing detailed information on test execution.' || chr(10) || - 'Designed for [SonarQube](https://about.sonarqube.com/) to report test execution.' || chr(10) || - 'JSON format returned conforms with the Sonar specification: https://docs.sonarqube.org/display/SONAR/Generic+Test+Data'; + return 'Generates a XML report providing detailed information on test execution.' || chr(10) || + 'Designed for [SonarQube](https://www.sonarqube.org/) to report test execution.' || chr(10) || + 'XML format returned conforms with the Sonar specification: https://docs.sonarqube.org/latest/analysis/generic-test/'; end; end; diff --git a/source/reporters/ut_sonar_test_reporter.tps b/source/reporters/ut_sonar_test_reporter.tps index 8bdb067df..0884ad3d3 100644 --- a/source/reporters/ut_sonar_test_reporter.tps +++ b/source/reporters/ut_sonar_test_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_sonar_test_reporter under ut_output_reporter_base( +create or replace noneditionable type ut_sonar_test_reporter under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_tap_reporter.tpb b/source/reporters/ut_tap_reporter.tpb new file mode 100644 index 000000000..c807ab3e1 --- /dev/null +++ b/source/reporters/ut_tap_reporter.tpb @@ -0,0 +1,139 @@ +create or replace noneditionable type body ut_tap_reporter is + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_tap_reporter(self in out nocopy ut_tap_reporter) return self as result is + begin + self.init($$plsql_unit); + self.lvl := 0; + return; + end ut_tap_reporter; + + member procedure print_comment(self in out nocopy ut_tap_reporter, a_comment clob) as + begin + self.print_clob(regexp_replace(a_comment, '^(.+)', '# \1', 1, 0, 'm')); + end print_comment; + + member function escape_special_chars(self in out nocopy ut_tap_reporter, a_string_to_escape clob) return clob as + begin + return regexp_replace(a_string_to_escape, '([\\#])', '\\\1'); + end escape_special_chars; + + overriding member procedure before_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite) as + begin + self.print_text('# Subtest: ' || self.escape_special_chars(coalesce(a_suite.description, a_suite.name))); + lvl := lvl + 2; + self.print_text('1..' || a_suite.items.count); + end before_calling_suite; + + + overriding member procedure after_calling_test(self in out nocopy ut_tap_reporter, a_test ut_test) as + l_message varchar2(4000); + l_test_name varchar2(4000) := self.escape_special_chars(coalesce(a_test.description, a_test.name)); + + procedure print_failed_expectation(a_test ut_test) is + l_lines ut_varchar2_list; + l_failed boolean; + begin + if a_test.get_error_stack_traces().count = 0 then + -- If no error occurred, print failed expectation + l_lines := a_test.all_expectations(a_test.all_expectations.last).get_result_lines(); + l_failed := a_test.all_expectations(a_test.all_expectations.last).status >= ut_utils.gc_success; + if l_failed then + self.print_text('message: ''' || l_lines(1) || ''''); + self.print_text('severity: fail'); + end if; + else + -- Print multi-line YAML-String with implicit newline characters + self.print_text('message: |'); + self.lvl := self.lvl + 1; + self.print_text(ut_utils.table_to_clob(a_test.get_error_stack_traces())); + self.lvl := self.lvl - 1; + self.print_text('severity: error'); + end if; + end print_failed_expectation; + + begin + + if a_test.result = ut_utils.gc_disabled then + self.print_text('ok - ' || l_test_name || ' # SKIP'|| + case when a_test.disabled_reason is not null + then ': '|| self.escape_special_chars(a_test.disabled_reason) + else null + end ); + elsif a_test.result = ut_utils.gc_success then + self.print_text('ok - ' || l_test_name); + elsif a_test.result > ut_utils.gc_success then + if self.lvl = 0 then + self.print_text(ut_ansiconsole_helper.red('not ok') || ' - ' || l_test_name); + else + self.print_text('not ok - ' || l_test_name); + end if; + self.lvl := self.lvl + 1; + self.print_text('---'); + print_failed_expectation(a_test); + self.print_text('...'); + self.lvl := self.lvl - 1; + end if; + + self.print_comment(a_test.get_serveroutputs); + + end after_calling_test; + + overriding member procedure after_calling_before_all(self in out nocopy ut_tap_reporter, a_executable in ut_executable) is + begin + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_comment(a_executable.serveroutput); + end if; + end after_calling_before_all; + + overriding member procedure after_calling_after_all(self in out nocopy ut_tap_reporter, a_executable in ut_executable) is + begin + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_comment(a_executable.serveroutput); + end if; + end after_calling_after_all; + + overriding member procedure after_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite) as + l_suite_name varchar2(4000) := coalesce(a_suite.description, a_suite.name); + begin + lvl := lvl - 2; + if lvl = 0 then + if a_suite.result = ut_utils.gc_success or a_suite.result = ut_utils.gc_disabled then + self.print_text('ok - ' || self.escape_special_chars(l_suite_name)); + elsif a_suite.result > ut_utils.gc_success then + self.print_text(ut_ansiconsole_helper.red('not ok') || ' - ' || self.escape_special_chars(l_suite_name)); + end if; + + self.print_text(' '); + end if; + + end after_calling_suite; + + overriding member procedure before_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) as + begin + self.print_text('TAP version 14'); + self.print_text('1..' || a_run.items.count); + self.print_text(' '); + end before_calling_run; + + overriding member procedure after_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) as + begin + self.lvl := 0; + end; +end; +/ diff --git a/source/reporters/ut_tap_reporter.tps b/source/reporters/ut_tap_reporter.tps new file mode 100644 index 000000000..150486648 --- /dev/null +++ b/source/reporters/ut_tap_reporter.tps @@ -0,0 +1,34 @@ +create or replace noneditionable type ut_tap_reporter under ut_documentation_reporter( + /* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + constructor function ut_tap_reporter(self in out nocopy ut_tap_reporter) return self as result, + member procedure print_comment(self in out nocopy ut_tap_reporter, a_comment clob), + member function escape_special_chars(self in out nocopy ut_tap_reporter, a_string_to_escape clob) return clob, + overriding member procedure before_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite), + + overriding member procedure after_calling_test(self in out nocopy ut_tap_reporter, a_test ut_test), + + overriding member procedure after_calling_before_all (self in out nocopy ut_tap_reporter, a_executable in ut_executable), + overriding member procedure after_calling_after_all (self in out nocopy ut_tap_reporter, a_executable in ut_executable), + overriding member procedure before_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run), + overriding member procedure after_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite), + overriding member procedure after_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) + +) +not final +/ diff --git a/source/reporters/ut_teamcity_reporter.tpb b/source/reporters/ut_teamcity_reporter.tpb index 9475c0a90..94d5497d5 100644 --- a/source/reporters/ut_teamcity_reporter.tpb +++ b/source/reporters/ut_teamcity_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_teamcity_reporter is +create or replace noneditionable type body ut_teamcity_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ create or replace type body ut_teamcity_reporter is lower(a_test.item.procedure_name); if a_test.result = ut_utils.gc_disabled then - ut_utils.append_to_list( l_results, ut_teamcity_reporter_helper.test_disabled(l_test_full_name)); + ut_utils.append_to_list( l_results, ut_teamcity_reporter_helper.test_disabled(l_test_full_name,a_test.disabled_reason)); else ut_utils.append_to_list( l_results, a_test.get_serveroutputs()); @@ -108,23 +108,28 @@ create or replace type body ut_teamcity_reporter is ut_teamcity_reporter_helper.test_failed( a_test_name => l_test_full_name, a_msg => 'Error occured', - a_details => - trim(l_std_err_msg) - || case when a_test.failed_expectations is not null - and a_test.failed_expectations.count>0 - then a_test.failed_expectations(1).message end + a_details => trim(l_std_err_msg) ) ); + for i in 1 .. a_test.failed_expectations.count loop + ut_utils.append_to_list( + l_results, + ut_teamcity_reporter_helper.test_failed( + a_test_name => l_test_full_name, + a_msg => a_test.failed_expectations(i).description, + a_details => a_test.failed_expectations(i).message ) + ); + end loop; elsif a_test.failed_expectations is not null and a_test.failed_expectations.count > 0 then - -- Teamcity supports only a single failure message - - ut_utils.append_to_list( - l_results, - ut_teamcity_reporter_helper.test_failed( - a_test_name => l_test_full_name, - a_msg => a_test.failed_expectations(a_test.failed_expectations.first).description, - a_details => a_test.failed_expectations(a_test.failed_expectations.first).message ) - ); + for i in 1 .. a_test.failed_expectations.count loop + ut_utils.append_to_list( + l_results, + ut_teamcity_reporter_helper.test_failed( + a_test_name => l_test_full_name, + a_msg => a_test.failed_expectations(i).description, + a_details => a_test.failed_expectations(i).message ) + ); + end loop; elsif a_test.result = ut_utils.gc_failure then ut_utils.append_to_list( l_results, diff --git a/source/reporters/ut_teamcity_reporter.tps b/source/reporters/ut_teamcity_reporter.tps index 2738ec00e..2def91c35 100644 --- a/source/reporters/ut_teamcity_reporter.tps +++ b/source/reporters/ut_teamcity_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_teamcity_reporter under ut_output_reporter_base( +create or replace noneditionable type ut_teamcity_reporter under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_teamcity_reporter_helper.pkb b/source/reporters/ut_teamcity_reporter_helper.pkb index 2d165d7a3..1d33c43ae 100644 --- a/source/reporters/ut_teamcity_reporter_helper.pkb +++ b/source/reporters/ut_teamcity_reporter_helper.pkb @@ -1,7 +1,7 @@ -create or replace package body ut_teamcity_reporter_helper is +create or replace noneditionable package body ut_teamcity_reporter_helper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -73,8 +73,6 @@ create or replace package body ut_teamcity_reporter_helper is 'true' when false then 'false' - else - null end; l_props('flowId') := a_flow_id; return message('testStarted', l_props); @@ -89,10 +87,13 @@ create or replace package body ut_teamcity_reporter_helper is return message('testFinished', l_props); end; - function test_disabled(a_test_name varchar2, a_flow_id varchar2 default null) return varchar2 is + function test_disabled(a_test_name varchar2,a_msg varchar2 default null, a_flow_id varchar2 default null) return varchar2 is l_props t_props; begin l_props('name') := a_test_name; + if a_msg is not null then + l_props('message') := a_msg; + end if; l_props('flowId') := a_flow_id; return message('testIgnored', l_props); end; diff --git a/source/reporters/ut_teamcity_reporter_helper.pks b/source/reporters/ut_teamcity_reporter_helper.pks index 7258999e1..bb9acef35 100644 --- a/source/reporters/ut_teamcity_reporter_helper.pks +++ b/source/reporters/ut_teamcity_reporter_helper.pks @@ -1,7 +1,7 @@ -create or replace package ut_teamcity_reporter_helper is +create or replace noneditionable package ut_teamcity_reporter_helper is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ create or replace package ut_teamcity_reporter_helper is function test_started(a_test_name varchar2, a_capture_standard_output boolean default null, a_flow_id varchar2 default null) return varchar2; function test_finished(a_test_name varchar2, a_test_duration_milisec number default null, a_flow_id varchar2 default null) return varchar2; - function test_disabled(a_test_name varchar2, a_flow_id varchar2 default null) return varchar2; + function test_disabled(a_test_name varchar2,a_msg varchar2 default null, a_flow_id varchar2 default null) return varchar2; function test_failed(a_test_name varchar2, a_msg in varchar2 default null, a_details varchar2 default null, a_flow_id varchar2 default null, a_actual varchar2 default null, a_expected varchar2 default null) return varchar2; function test_std_err(a_test_name varchar2, a_out in varchar2, a_flow_id in varchar2 default null) return varchar2; diff --git a/source/reporters/ut_tfs_junit_reporter.tpb b/source/reporters/ut_tfs_junit_reporter.tpb index 5262ac687..5e24b2dca 100644 --- a/source/reporters/ut_tfs_junit_reporter.tpb +++ b/source/reporters/ut_tfs_junit_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_tfs_junit_reporter is +create or replace noneditionable type body ut_tfs_junit_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ create or replace type body ut_tfs_junit_reporter is constructor function ut_tfs_junit_reporter(self in out nocopy ut_tfs_junit_reporter) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; @@ -51,11 +51,12 @@ create or replace type body ut_tfs_junit_reporter is return regexp_substr(a_path_with_name, '(.*)\.' ||a_name||'$',subexpression=>1); end; - procedure print_test_results(a_test ut_test) is + function add_test_results(a_test ut_test) return ut_varchar2_rows is l_results ut_varchar2_rows := ut_varchar2_rows(); begin - self.print_text(''); + /* According to specs : - A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. @@ -69,6 +70,12 @@ create or replace type body ut_tfs_junit_reporter is ut_utils.append_to_list( l_results, ut_utils.to_cdata( ut_utils.convert_collection( a_test.get_error_stack_traces() ) ) ); ut_utils.append_to_list( l_results, ''); -- Do not count error as failure + elsif a_test.result = ut_utils.gc_disabled then + if a_test.disabled_reason is not null then + ut_utils.append_to_list( l_results, ''); + else + ut_utils.append_to_list( l_results, '' ); + end if; elsif a_test.result = ut_utils.gc_failure then ut_utils.append_to_list( l_results, ''); ut_utils.append_to_list( l_results, ut_utils.to_cdata( a_test.get_failed_expectation_lines() ) ); @@ -77,64 +84,87 @@ create or replace type body ut_tfs_junit_reporter is ut_utils.append_to_list( l_results, ''); - self.print_text_lines(l_results); + return l_results; end; - procedure print_suite_results(a_suite ut_logical_suite, a_suite_id in out nocopy integer) is + procedure print_suite_results(a_suite ut_logical_suite, a_suite_id in out nocopy integer, a_nested_tests in out nocopy ut_varchar2_rows) is l_tests_count integer := a_suite.results_count.disabled_count + a_suite.results_count.success_count + a_suite.results_count.failure_count + a_suite.results_count.errored_count; l_results ut_varchar2_rows := ut_varchar2_rows(); l_suite ut_suite; l_outputs clob; l_errors ut_varchar2_list; - begin - + l_tests ut_varchar2_list; + begin for i in 1 .. a_suite.items.count loop - if a_suite.items(i) is of(ut_logical_suite) then - print_suite_results(treat(a_suite.items(i) as ut_logical_suite), a_suite_id); + if a_suite.items(i) is of(ut_suite_context) then + print_suite_results(treat(a_suite.items(i) as ut_suite_context), a_suite_id, a_nested_tests); + elsif a_suite.items(i) is of(ut_suite) then + print_suite_results(treat(a_suite.items(i) as ut_suite), a_suite_id, a_nested_tests); + elsif a_suite.items(i) is of(ut_logical_suite) then + print_suite_results(treat(a_suite.items(i) as ut_logical_suite), a_suite_id, a_nested_tests); end if; end loop; - - if a_suite is of(ut_suite) then - a_suite_id := a_suite_id + 1; - self.print_text(''); - self.print_text(''); + --Due to fact tha TFS and junit5 accepts only flat structure we have to report in suite level only. + if a_suite is of(ut_suite_context) then for i in 1 .. a_suite.items.count loop if a_suite.items(i) is of(ut_test) then - print_test_results(treat(a_suite.items(i) as ut_test)); + ut_utils.append_to_list( a_nested_tests,(add_test_results(treat(a_suite.items(i) as ut_test)))); end if; end loop; - l_suite := treat(a_suite as ut_suite); - l_outputs := l_suite.get_serveroutputs(); - if l_outputs is not null and l_outputs != empty_clob() then - ut_utils.append_to_list( l_results, ''); - ut_utils.append_to_list( l_results, ut_utils.to_cdata( l_suite.get_serveroutputs() ) ); - ut_utils.append_to_list( l_results, ''); - else - ut_utils.append_to_list( l_results, ''); - end if; - - l_errors := l_suite.get_error_stack_traces(); - if l_errors is not empty then - ut_utils.append_to_list( l_results, ''); - ut_utils.append_to_list( l_results, ut_utils.to_cdata( ut_utils.convert_collection( l_errors ) ) ); - ut_utils.append_to_list( l_results, ''); - else - ut_utils.append_to_list( l_results, ''); - end if; - ut_utils.append_to_list( l_results, ''); - - self.print_text_lines(l_results); + elsif a_suite is of(ut_suite) then + for i in 1 .. a_suite.items.count loop + if a_suite.items(i) is of(ut_test) then + ut_utils.append_to_list( a_nested_tests,(add_test_results(treat(a_suite.items(i) as ut_test)))); + end if; + end loop; + --TFS doesnt report on empty test suites, however all we want to make sure is that we dont pring parents suites + -- showing test count but not tests. + if (a_nested_tests.count > 0 and l_tests_count > 0) or (a_nested_tests.count = 0 and l_tests_count = 0) then + a_suite_id := a_suite_id + 1; + ut_utils.append_to_list( l_results,''); + ut_utils.append_to_list( l_results,''); + ut_utils.append_to_list(l_results,a_nested_tests); + l_suite := treat(a_suite as ut_suite); + l_outputs := l_suite.get_serveroutputs(); + if l_outputs is not null and l_outputs != empty_clob() then + ut_utils.append_to_list( l_results, ''); + ut_utils.append_to_list( l_results, ut_utils.to_cdata( l_suite.get_serveroutputs() ) ); + ut_utils.append_to_list( l_results, ''); + else + ut_utils.append_to_list( l_results, ''); + end if; + + l_errors := l_suite.get_error_stack_traces(); + if l_errors is not empty then + ut_utils.append_to_list( l_results, ''); + ut_utils.append_to_list( l_results, ut_utils.to_cdata( ut_utils.convert_collection( l_errors ) ) ); + ut_utils.append_to_list( l_results, ''); + else + ut_utils.append_to_list( l_results, ''); + end if; + ut_utils.append_to_list( l_results, ''); + + self.print_text_lines(l_results); + --We have resolved a context and we now reset value. + a_nested_tests := ut_varchar2_rows(); + end if; end if; end; + + procedure get_suite_results(a_suite ut_logical_suite, a_suite_id in out nocopy integer) is + l_nested_tests ut_varchar2_rows:= ut_varchar2_rows(); + begin + print_suite_results(a_suite, l_suite_id,l_nested_tests); + end; begin l_suite_id := 0; self.print_text(ut_utils.get_xml_header(a_run.client_character_set)); self.print_text(''); for i in 1 .. a_run.items.count loop - print_suite_results(treat(a_run.items(i) as ut_logical_suite), l_suite_id); + get_suite_results(treat(a_run.items(i) as ut_logical_suite), l_suite_id); end loop; self.print_text(''); end; diff --git a/source/reporters/ut_tfs_junit_reporter.tps b/source/reporters/ut_tfs_junit_reporter.tps index ecf422bd3..560bb10dd 100644 --- a/source/reporters/ut_tfs_junit_reporter.tps +++ b/source/reporters/ut_tfs_junit_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_tfs_junit_reporter under ut_output_reporter_base( +create or replace noneditionable type ut_tfs_junit_reporter under ut_output_reporter_base( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/reporters/ut_xunit_reporter.tpb b/source/reporters/ut_xunit_reporter.tpb index b06c20810..74fc57ac0 100644 --- a/source/reporters/ut_xunit_reporter.tpb +++ b/source/reporters/ut_xunit_reporter.tpb @@ -1,7 +1,7 @@ -create or replace type body ut_xunit_reporter is +create or replace noneditionable type body ut_xunit_reporter is /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ create or replace type body ut_xunit_reporter is constructor function ut_xunit_reporter(self in out nocopy ut_xunit_reporter) return self as result is begin - self.init($$plsql_unit); + self.init($$plsql_unit,ut_output_bulk_buffer()); return; end; diff --git a/source/reporters/ut_xunit_reporter.tps b/source/reporters/ut_xunit_reporter.tps index b09205171..d701d8ab0 100644 --- a/source/reporters/ut_xunit_reporter.tps +++ b/source/reporters/ut_xunit_reporter.tps @@ -1,7 +1,7 @@ -create or replace type ut_xunit_reporter under ut_junit_reporter( +create or replace noneditionable type ut_xunit_reporter under ut_junit_reporter( /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/set_install_params.sql b/source/set_install_params.sql index 4dc33c367..b559b8023 100644 --- a/source/set_install_params.sql +++ b/source/set_install_params.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/uninstall.sql b/source/uninstall.sql index 760bf89b7..712845fb7 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/uninstall_all.sql b/source/uninstall_all.sql index 32180d762..d3d3f3fbe 100644 --- a/source/uninstall_all.sql +++ b/source/uninstall_all.sql @@ -1,6 +1,6 @@ /* utPLSQL - Version 3 - Copyright 2016 - 2019 utPLSQL Project + Copyright 2016 - 2026 utPLSQL Project Licensed under the Apache License, Version 2.0 (the "License"): you may not use this file except in compliance with the License. diff --git a/source/uninstall_coverage_tables.sql b/source/uninstall_coverage_tables.sql index 890aa7090..df3c19627 100644 --- a/source/uninstall_coverage_tables.sql +++ b/source/uninstall_coverage_tables.sql @@ -1,3 +1,20 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + set echo off set feedback off begin diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index 230e394ee..fcc8dfa52 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -1,5 +1,21 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + set echo off -set serverout on declare procedure drop_if_exists(a_object_type varchar2, a_object_name varchar2) is l_count integer; @@ -54,7 +70,9 @@ drop synonym be_true; drop synonym equal; -drop type ut_coveralls_reporter force; +drop synonym be_within; + +drop synonym be_within_pct; drop type ut_coverage_sonar_reporter force; @@ -84,10 +102,16 @@ drop type ut_suite_items_info force; drop type ut_suite_item_info force; +drop type ut_path_items force; + +drop type ut_path_item force; + drop package ut_suite_manager; drop package ut_suite_builder; +drop package ut_suite_tag_filter; + drop package ut_suite_cache_manager; drop table ut_suite_cache purge; @@ -142,10 +166,20 @@ drop type ut_be_less_than force; drop type ut_be_false force; +drop type ut_be_within_pct force; + +drop type ut_be_within force; + +drop package ut_be_within_helper; + drop type ut_comparison_matcher force; drop type ut_matcher force; +drop type ut_expectation_base force; + +drop type ut_matcher_base force; + drop type ut_data_value_yminterval force; drop type ut_data_value_varchar2 force; @@ -235,18 +269,27 @@ drop package ut_metadata; drop package ut_ansiconsole_helper; begin + null; + $if dbms_db_version.version < 21 $then + begin execute immediate 'drop type json force'; exception when others then null; end; + $end $if dbms_db_version.version = 12 and dbms_db_version.release = 1 or dbms_db_version.version < 12 $then - execute immediate 'drop type json_element_t force'; - $else - dbms_output.put_line('Nothing to drop'); + begin execute immediate 'drop type json_element_t force'; exception when others then null; end; + begin execute immediate 'drop type json_object_t force'; exception when others then null; end; + begin execute immediate 'drop type json_array_t force'; exception when others then null; end; + begin execute immediate 'drop type json_key_list force'; exception when others then null; end; $end end; / drop package ut_utils; +drop type ut_stack force; + drop sequence ut_savepoint_seq; +drop type ut_tap_reporter force; + drop type ut_documentation_reporter force; drop type ut_debug_reporter force; @@ -301,12 +344,18 @@ drop type ut_output_table_buffer force; drop type ut_output_clob_table_buffer force; +drop type ut_output_bulk_buffer force; + drop type ut_output_buffer_base force; drop table ut_output_buffer_tmp purge; +drop sequence ut_output_buffer_tmp_seq; + drop table ut_output_clob_buffer_tmp purge; +drop sequence ut_output_clob_buffer_tmp_seq; + drop table ut_output_buffer_info_tmp purge; drop package ut_session_context; diff --git a/source/uninstall_synonyms.sql b/source/uninstall_synonyms.sql index 569e5057d..4a12340f6 100644 --- a/source/uninstall_synonyms.sql +++ b/source/uninstall_synonyms.sql @@ -1,3 +1,20 @@ +/* + utPLSQL - Version 3 + Copyright 2016 - 2026 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + set echo off set feedback off declare @@ -15,10 +32,10 @@ begin and not exists (select 1 from all_objects o where o.owner = s.table_owner and o.object_name = s.table_name) ) loop - i := i + 1; begin execute immediate 'drop '||syn.syn_name; dbms_output.put_line('Dropped '||syn.syn_name||' for object '||syn.for_object); + i := i + 1; exception when others then dbms_output.put_line('FAILED to drop '||syn.syn_name||' for object '||syn.for_object); diff --git a/test/install_and_run_tests.sh b/test/install_and_run_tests.sh index 04f51fb3c..0c17b08b7 100755 --- a/test/install_and_run_tests.sh +++ b/test/install_and_run_tests.sh @@ -3,7 +3,8 @@ set -ev #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" +. ./development/env.sh cd test -time . ./${DIR}/install_tests.sh -time . ./${DIR}/run_tests.sh +time . ./install_tests.sh +time . ./run_tests.sh diff --git a/test/install_tests.sh b/test/install_tests.sh index e07b4d2b3..1b97ec33d 100755 --- a/test/install_tests.sh +++ b/test/install_tests.sh @@ -1,13 +1,13 @@ #!/bin/bash set -ev -#goto git root directory -git rev-parse && cd "$(git rev-parse --show-cdup)" -cd test +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd ${SCRIPT_DIR} -"$SQLCLI" ${UT3_TESTER_HELPER}/${UT3_TESTER_HELPER_PASSWORD}@//${CONNECTION_STR} @install_ut3_tester_helper.sql -"$SQLCLI" ${UT3_USER}/${UT3_USER_PASSWORD}@//${CONNECTION_STR} @install_ut3_user_tests.sql +"$SQLCLI" UT3_TESTER_HELPER/ut3@//${CONNECTION_STR} @install_ut3_tester_helper.sql -"$SQLCLI" ${UT3_TESTER}/${UT3_TESTER_PASSWORD}@//${CONNECTION_STR} @install_ut3_tester_tests.sql +"$SQLCLI" UT3_USER/ut3@//${CONNECTION_STR} @install_ut3_user_tests.sql + +"$SQLCLI" UT3_TESTER/ut3@//${CONNECTION_STR} @install_ut3_tester_tests.sql diff --git a/test/install_ut3_tester_helper.sql b/test/install_ut3_tester_helper.sql index 9841252a0..b78221627 100644 --- a/test/install_ut3_tester_helper.sql +++ b/test/install_ut3_tester_helper.sql @@ -6,9 +6,15 @@ alter session set plsql_optimize_level=0; --Install ut3_tester_helper @@ut3_tester_helper/test_dummy_object.tps @@ut3_tester_helper/other_dummy_object.tps +@@ut3_tester_helper/test_dummy_nested_object.tps +@@ut3_tester_helper/test_dummy_double_nested_object.tps @@ut3_tester_helper/test_dummy_object_list.tps +@@ut3_tester_helper/test_dummy_nested_object_list.tps +@@ut3_tester_helper/test_dummy_double_nested_list.tps +@@ut3_tester_helper/test_dummy_dble_nest_lst_obj.tps @@ut3_tester_helper/test_tab_varchar2.tps @@ut3_tester_helper/test_tab_varray.tps +@@ut3_tester_helper/test_nested_tab_varray.tps @@ut3_tester_helper/test_dummy_number.tps @@ut3_tester_helper/ut_test_table.sql @@ut3_tester_helper/test_event_object.tps @@ -29,7 +35,7 @@ alter session set plsql_optimize_level=0; @@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; -create or replace synonym ut3$user#.coverage_helper for ut3_tester_helper.coverage_helper; +create or replace synonym ut3_user.coverage_helper for ut3_tester_helper.coverage_helper; set linesize 200 set define on diff --git a/test/install_ut3_tester_tests.sql b/test/install_ut3_tester_tests.sql index 9548d5148..cc96b2b64 100644 --- a/test/install_ut3_tester_tests.sql +++ b/test/install_ut3_tester_tests.sql @@ -14,8 +14,10 @@ alter session set plsql_optimize_level=0; @@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/annotations/test_annot_disabled_reason.pks @@ut3_tester/core/expectations/test_expectation_processor.pks @@ut3_tester/core/test_ut_utils.pks +@@ut3_tester/core/test_ut_suite_tag_filter.pks @@ut3_tester/core/test_ut_test.pks @@ut3_tester/core/test_ut_suite.pks @@ut3_tester/core/test_ut_executable.pks @@ -31,8 +33,10 @@ alter session set plsql_optimize_level=0; @@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/annotations/test_annot_disabled_reason.pkb @@ut3_tester/core/expectations/test_expectation_processor.pkb @@ut3_tester/core/test_ut_utils.pkb +@@ut3_tester/core/test_ut_suite_tag_filter.pkb @@ut3_tester/core/test_ut_test.pkb @@ut3_tester/core/test_ut_suite.pkb @@ut3_tester/core/test_ut_executable.pkb diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index e790293c8..b74c5d7ae 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -25,6 +25,8 @@ prompt Install user tests @@ut3_user/expectations/binary/test_be_less_or_equal.pks @@ut3_user/expectations/binary/test_be_greater_or_equal.pks @@ut3_user/expectations/binary/test_be_greater_than.pks +@@ut3_user/expectations/binary/test_to_be_within.pks +@@ut3_user/expectations/binary/test_to_be_within_pct.pks @@ut3_user/expectations/test_matchers.pks @@ut3_user/expectations/test_expectation_anydata.pks @@ut3_user/expectations/test_expectations_cursor.pks @@ -41,13 +43,13 @@ set define off @@ut3_user/reporters/test_documentation_reporter.pks @@ut3_user/reporters/test_debug_reporter.pks @@ut3_user/reporters/test_realtime_reporter.pks +@@ut3_user/reporters/test_tap_reporter.pks @@ut3_user/reporters/test_coverage.pks @@ut3_user/reporters/test_coverage/test_coverage_standalone.pks set define on @@ut3_user/reporters/test_coverage/test_extended_coverage.pks @@ut3_user/reporters/test_coverage/test_html_coverage_reporter.pks set define off -@@ut3_user/reporters/test_coverage/test_coveralls_reporter.pks @@ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pks @@ut3_user/reporters/test_coverage/test_coverage_sonar_reporter.pks set define on @@ -66,6 +68,8 @@ set define off @@ut3_user/expectations/binary/test_be_less_or_equal.pkb @@ut3_user/expectations/binary/test_be_greater_or_equal.pkb @@ut3_user/expectations/binary/test_be_greater_than.pkb +@@ut3_user/expectations/binary/test_to_be_within.pkb +@@ut3_user/expectations/binary/test_to_be_within_pct.pkb @@ut3_user/expectations/test_matchers.pkb @@ut3_user/expectations/test_expectation_anydata.pkb @@ut3_user/expectations/test_expectations_cursor.pkb @@ -82,12 +86,12 @@ set define off @@ut3_user/reporters/test_documentation_reporter.pkb @@ut3_user/reporters/test_debug_reporter.pkb @@ut3_user/reporters/test_realtime_reporter.pkb +@@ut3_user/reporters/test_tap_reporter.pkb @@ut3_user/reporters/test_coverage/test_coverage_standalone.pkb set define on @@ut3_user/reporters/test_coverage/test_extended_coverage.pkb @@ut3_user/reporters/test_coverage/test_html_coverage_reporter.pkb set define off -@@ut3_user/reporters/test_coverage/test_coveralls_reporter.pkb @@ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pkb @@ut3_user/reporters/test_coverage/test_coverage_sonar_reporter.pkb set define on diff --git a/test/run_tests.sh b/test/run_tests.sh index 0edc4d96d..078d011d6 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -4,14 +4,19 @@ set -ev #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" -time utPLSQL-cli/bin/utplsql run ${UT3_TESTER_HELPER}/${UT3_TESTER_HELPER_PASSWORD}@${CONNECTION_STR} \ +export NLS_LANG=AMERICAN_AMERICA.AL32UTF8 + +time utPLSQL-cli/bin/utplsql run UT3_TESTER_HELPER/ut3@//${CONNECTION_STR} -D \ -source_path=source -owner=ut3_develop \ --p='ut3_tester,ut3$user#' \ +-p='ut3_tester,ut3_user' \ -test_path=test -c \ + -regex_expression="test/(\w+)(/(\w+))*/(\w+)\.(.{3})$" \ + -owner_subexpression=1 \ + -type_subexpression=5 \ + -name_subexpression=4 \ -f=ut_coverage_sonar_reporter -o=coverage.xml \ -f=ut_coverage_cobertura_reporter -o=cobertura.xml \ -f=ut_coverage_html_reporter -o=coverage.html \ --f=ut_coveralls_reporter -o=coverage.json \ -f=ut_sonar_test_reporter -o=test_results.xml \ -f=ut_junit_reporter -o=junit_test_results.xml \ -f=ut_tfs_junit_reporter -o=tfs_test_results.xml \ diff --git a/test/ut3_tester/core/annotations/test_annot_disabled_reason.pkb b/test/ut3_tester/core/annotations/test_annot_disabled_reason.pkb new file mode 100644 index 000000000..7f1e068a8 --- /dev/null +++ b/test/ut3_tester/core/annotations/test_annot_disabled_reason.pkb @@ -0,0 +1,484 @@ +create or replace package body test_annot_disabled_reason +is + + + procedure compile_dummy_packages is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package test_package_1 is + + --%suite + --%displayname(disable_test_suite_level) + --%suitepath(tests) + --%rollback(manual) + + --%disabled( Tests are disabled on suite level ) + + --%context( First context ) + + --%test(Test1 from test package 1) + procedure test1; + + --%test(Test2 from test package 1) + procedure test2; + + --%endcontext + + --%context( Second context ) + + --%test(Test3 from test package 1) + procedure test3; + + --%test(Test4 from test package 1) + procedure test4; + + --%endcontext + +end test_package_1;]'; + + execute immediate q'[create or replace package body test_package_1 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + + procedure test3 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test4 is + begin + ut.expect(2).to_equal(2); + end; + +end test_package_1;]'; + + execute immediate q'[create or replace package test_package_2 is + + --%suite + --%displayname(Disable on context level) + --%suitepath(tests) + --%rollback(manual) + + --%context( First context ) + + --%disabled( Tests and disabled on first context level ) + + --%test(Test1 from test package 2) + procedure test1; + + --%test(Test2 from test package 2) + procedure test2; + + --%endcontext + + --%context( Second context ) + + --%test(Test3 from test package 2) + procedure test3; + + --%test(Test4 from test package 3) + procedure test4; + + --%endcontext + +end test_package_2;]'; + + execute immediate q'[create or replace package body test_package_2 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + + procedure test3 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test4 is + begin + ut.expect(2).to_equal(2); + end; + +end test_package_2;]'; + + execute immediate q'[create or replace package testing_package_3 is + + --%suite + --%displayname(Disable tests on test level) + --%suitepath(tests) + --%rollback(manual) + + --%context( First context ) + + --%test(Test1 from test package 3) + --%disabled( Test1 disabled from first context ) + procedure test1; + + --%test(Test2 from test package 3) + procedure test2; + + --%endcontext + + --%context( Second context ) + + --%test(Test3 from test package 3) + procedure test3; + + --%test(Test4 from test package 3) + --%disabled( Test4 disabled from second context ) + procedure test4; + + --%endcontext + +end testing_package_3;]'; + + execute immediate q'[create or replace package body testing_package_3 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + + procedure test3 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test4 is + begin + ut.expect(2).to_equal(2); + end; + +end testing_package_3;]'; + + execute immediate q'[create or replace package test_package_4 is + + --%suite + --%displayname(Disable reason is very long or have special characters) + --%suitepath(tests) + --%rollback(manual) + + + --%test(Test1 from test package 4) + --%disabled( $#?!%*&-/\^ ) + procedure test1; + + --%test(Test2 from test package 4) + --%disabled(verylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtext) + procedure test2; + +end test_package_4;]'; + + execute immediate q'[create or replace package body test_package_4 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + +end test_package_4;]'; + + execute immediate q'[create or replace package test_package_5 is + + --%suite + --%displayname(Disable tests on suite level overriding rest) + --%suitepath(tests) + --%rollback(manual) + + --%disabled( Disable on suite level ) + + --%context( First context ) + + --%disabled( Disable on 1st context level ) + + --%test(Test1 from test package 3) + --%disabled( Disable on 1st test level ) + procedure test1; + + --%test(Test2 from test package 3) + --%disabled( Disable on 2nd test level ) + procedure test2; + + --%endcontext + + --%context( Second context ) + + --%disabled( Disable on 2nd context level ) + + --%test(Test3 from test package 3) + --%disabled( Disable on 3rd test level ) + procedure test3; + + --%test(Test4 from test package 3) + --%disabled( Disable on 4th test level ) + procedure test4; + + --%endcontext + +end test_package_5;]'; + + execute immediate q'[create or replace package body test_package_5 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + + procedure test3 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test4 is + begin + ut.expect(2).to_equal(2); + end; + +end test_package_5;]'; + + execute immediate q'[create or replace package test_package_6 is + + --%suite + --%displayname(Disable tests on each of ctx level overriding rest) + --%suitepath(tests) + --%rollback(manual) + + --%context( First context ) + + --%disabled( Disable on 1st context level ) + + --%test(Test1 from test package 3) + --%disabled( Disable on 1st test level ) + procedure test1; + + --%test(Test2 from test package 3) + --%disabled( Disable on 2nd test level ) + procedure test2; + + --%endcontext + + --%context( Second context ) + + --%disabled( Disable on 2nd context level ) + + --%test(Test3 from test package 3) + --%disabled( Disable on 3rd test level ) + procedure test3; + + --%test(Test4 from test package 3) + --%disabled( Disable on 4th test level ) + procedure test4; + + --%endcontext + +end test_package_6;]'; + + execute immediate q'[create or replace package body test_package_6 is + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(2).to_equal(2); + end; + + procedure test3 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test4 is + begin + ut.expect(2).to_equal(2); + end; + +end test_package_6;]'; + + end; + + + procedure drop_dummy_packages is + pragma autonomous_transaction; + begin + execute immediate 'drop package test_package_1'; + execute immediate 'drop package test_package_2'; + execute immediate 'drop package testing_package_3'; + execute immediate 'drop package test_package_4'; + execute immediate 'drop package test_package_5'; + execute immediate 'drop package test_package_6'; + end; + + procedure test_disable_on_suite_level is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.test_package_1'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%disable_test_suite_level +%First context +%Test1 from test package 1 [0 sec] (DISABLED - Tests are disabled on suite level) +%Test2 from test package 1 [0 sec] (DISABLED - Tests are disabled on suite level) +%Second context +%Test3 from test package 1 [0 sec] (DISABLED - Tests are disabled on suite level) +%Test4 from test package 1 [0 sec] (DISABLED - Tests are disabled on suite level)%]'; + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + + procedure test_dis_on_1st_ctx_level is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.test_package_2'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%Disable on context level +%First context +%Test1 from test package 2 [0 sec] (DISABLED - Tests and disabled on first context level) +%Test2 from test package 2 [0 sec] (DISABLED - Tests and disabled on first context level) +%Second context +%Test3 from test package 2 [% sec] +%Test4 from test package 3 [% sec]%]'; + + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + + procedure test_disable_tests_level is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.testing_package_3'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%Disable tests on test level +%First context +%Test1 from test package 3 [0 sec] (DISABLED - Test1 disabled from first context) +%Test2 from test package 3 [% sec] +%Second context +%Test3 from test package 3 [% sec] +%Test4 from test package 3 [0 sec] (DISABLED - Test4 disabled from second context)%]'; + + ut.expect(l_actual_message).to_be_like(l_expected_message); + + + end; + + procedure test_long_text_spec_chr is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.test_package_4'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%Disable reason is very long or have special characters +%Test1 from test package 4 [0 sec] (DISABLED - $#?!%*&-/\^) +%Test2 from test package 4 [0 sec] (DISABLED - verylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtextverylongtext)%]'; + + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure test_disable_suite_ctx_tst is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.test_package_5'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%Disable tests on suite level overriding rest +%First context +%Test1 from test package 3 [0 sec] (DISABLED - Disable on suite level) +%Test2 from test package 3 [0 sec] (DISABLED - Disable on suite level) +%Second context +%Test3 from test package 3 [0 sec] (DISABLED - Disable on suite level) +%Test4 from test package 3 [0 sec] (DISABLED - Disable on suite level)%]'; + + ut.expect(l_actual_message).to_be_like(l_expected_message); + + + end; + + procedure test_disable_ctx_tst is + l_test_results ut3_develop.ut_varchar2_list; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Act + + select * bulk collect into l_test_results from table(ut3_develop.ut.run((sys_context('USERENV', 'CURRENT_USER')||'.test_package_6'))); + + l_actual_message := ut3_develop.ut_utils.table_to_clob(l_test_results); + + l_expected_message := q'[%tests +%Disable tests on each of ctx level overriding rest +%First context +%Test1 from test package 3 [0 sec] (DISABLED - Disable on 1st context level) +%Test2 from test package 3 [0 sec] (DISABLED - Disable on 1st context level) +%Second context +%Test3 from test package 3 [0 sec] (DISABLED - Disable on 2nd context level) +%Test4 from test package 3 [0 sec] (DISABLED - Disable on 2nd context level)%]'; + + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + +end; +/ diff --git a/test/ut3_tester/core/annotations/test_annot_disabled_reason.pks b/test/ut3_tester/core/annotations/test_annot_disabled_reason.pks new file mode 100644 index 000000000..cae5da1ee --- /dev/null +++ b/test/ut3_tester/core/annotations/test_annot_disabled_reason.pks @@ -0,0 +1,31 @@ +create or replace package test_annot_disabled_reason +is + --%suite(annotations - disabled) + --%suitepath(utplsql.ut3_tester.core.annotations) + + --%beforeall + procedure compile_dummy_packages; + + --%afterall + procedure drop_dummy_packages; + + --%test(Disable all tests under the suite displaying suite level reason) + procedure test_disable_on_suite_level; + + --%test(Disable all tests under one of two contexts displaying context level reason) + procedure test_dis_on_1st_ctx_level; + + --%test(Disable a single tests from each of the contexts displaying test level reason) + procedure test_disable_tests_level; + + --%test(Disable tests with reason using special characters and long reason) + procedure test_long_text_spec_chr; + + --%test(Disable tests on suite , context and test level and display suite level reason) + procedure test_disable_suite_ctx_tst; + + --%test(Disable tests on context and test level and display context level reason) + procedure test_disable_ctx_tst; + +end; +/ 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 ac51d3a49..761377765 100644 --- a/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb +++ b/test/ut3_tester/core/annotations/test_annot_throws_exception.pkb @@ -1,6 +1,7 @@ create or replace package body test_annot_throws_exception is g_tests_results clob; + g_results_other_schema clob; procedure recollect_tests_results is pragma autonomous_transaction; @@ -25,10 +26,18 @@ is e_some_exception exception; pragma exception_init(e_some_exception, -20207); - end;]'; + end; + ]'; - l_package_spec := ' - create package annotated_package_with_throws is + l_package_spec := q'[ + create or replace package annotated_package_with_throws is + + c_local_error_no constant number := -20211; + e_some_local_exception exception; + pragma exception_init(e_some_local_exception, -20212); + + e_uninitialized_exception_variable exception; + --%suite(Dummy package to test annotation throws) --%test(Throws same annotated exception) @@ -95,6 +104,22 @@ is --%throws(exc_pkg.c_e_mix_missin,utter_rubbish) procedure mixed_list_notexi; + --%test(Success referencing an exception variable when running from another schema) + --%throws(exc_pkg.e_some_exception) + procedure named_exc_pragma_run_from_another_schema; + + --%test(Success referencing a numeric exception variable when running from another schema) + --%throws(exc_pkg.c_e_list_1) + procedure exc_number_var_run_from_another_schema; + + --%test(Success referencing an exception variable without package name when running from another schema) + --%throws(e_some_local_exception) + procedure named_exc_pragma_run_from_another_schema_no_package_name; + + --%test(Success referencing a numeric exception variable without package name when running from another schema) + --%throws(c_local_error_no) + procedure exc_number_var_run_from_another_schema_no_package_name; + --%test(Success resolve and match named exception defined in pragma exception init) --%throws(exc_pkg.e_some_exception) procedure named_exc_pragma; @@ -122,11 +147,20 @@ is --%test(Bad exception constant) --%throws(exc_pkg.c_e_dummy); procedure bad_exc_const; + + --%test(Uninitialized exception variable can be used successfully in a test) + --%throws(e_uninitialized_exception_variable) + procedure referencing_uninitialized_exception; + + --%test(Failure report shows all expected exceptions) + --%throws(c_local_error_no,e_some_local_exception,e_uninitialized_exception_variable) + procedure not_throwing_expected_exceptions; + end; - '; + ]'; l_package_body := ' - create package body annotated_package_with_throws is + create or replace package body annotated_package_with_throws is procedure raised_same_exception is begin raise_application_error(-20145, ''Test error''); @@ -208,6 +242,26 @@ is raise_application_error(exc_pkg.c_e_mix_missin,''Test''); end; + procedure named_exc_pragma_run_from_another_schema is + begin + raise exc_pkg.e_some_exception; + end; + + procedure exc_number_var_run_from_another_schema is + begin + raise_application_error(exc_pkg.c_e_list_1,''Test''); + end; + + procedure named_exc_pragma_run_from_another_schema_no_package_name is + begin + raise e_some_local_exception; + end; + + procedure exc_number_var_run_from_another_schema_no_package_name is + begin + raise_application_error(c_local_error_no,''Test''); + end; + procedure named_exc_pragma is begin raise exc_pkg.e_some_exception; @@ -243,6 +297,15 @@ is raise_application_error(-20143, ''Test error''); end; + procedure referencing_uninitialized_exception is + begin + raise e_uninitialized_exception_variable; + end; + + procedure not_throwing_expected_exceptions is + begin + raise_application_error(-20143, ''Test error''); + end; end; '; @@ -256,6 +319,14 @@ is g_tests_results := ut3_develop.ut_utils.table_to_clob(l_test_results); end; + procedure drop_test_package is + pragma autonomous_transaction; + begin + execute immediate 'drop package annotated_package_with_throws'; + execute immediate 'drop package exc_pkg'; + end; + + procedure throws_same_annotated_except is begin ut.expect(g_tests_results).to_match('^\s*Throws same annotated exception \[[,\.0-9]+ sec\]\s*$','m'); @@ -312,7 +383,7 @@ is 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).to_match('^\s*Detects a valid exception number within many invalid ones \[[,\.0-9]+ sec\]\s*$','m'); ut.expect(g_tests_results).to_match('one_valid_exception_number\s*Invalid parameter value ".*" for "--%throws" annotation. Parameter ignored.','m'); end; @@ -357,7 +428,37 @@ is 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('named_exc_pragma'); end; - + + procedure run_from_another_schema is + begin + ut3_tester_helper.run_helper.run(('ut3_tester.annotated_package_with_throws')); + g_results_other_schema := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + end; + + procedure named_exc_pragma_run_from_another_schema is + begin + ut.expect(g_results_other_schema).to_match('^\s*Success referencing an exception variable when running from another schema \[[,\.0-9]+ sec\]\s*$', 'm'); + ut.expect(g_results_other_schema).not_to_match('named_exc_pragma_run_from_another_schema'); + end; + + procedure named_exc_pragma_run_from_another_schema_no_package_name is + begin + ut.expect(g_results_other_schema).to_match('^\s*Success referencing an exception variable without package name when running from another schema \[[,\.0-9]+ sec\]\s*$', 'm'); + ut.expect(g_results_other_schema).not_to_match('named_exc_pragma_run_from_another_schema_no_package_name'); + end; + + procedure exc_number_var_run_from_another_schema_no_package_name is + begin + ut.expect(g_results_other_schema).to_match('^\s*Success referencing a numeric exception variable without package name when running from another schema \[[,\.0-9]+ sec\]\s*$', 'm'); + ut.expect(g_results_other_schema).not_to_match('exc_number_var_run_from_another_schema_no_package_name'); + end; + + procedure exc_number_var_run_from_another_schema is + begin + ut.expect(g_results_other_schema).to_match('^\s*Success referencing a numeric exception variable when running from another schema \[[,\.0-9]+ sec\]\s*$', 'm'); + ut.expect(g_results_other_schema).not_to_match('exc_number_var_run_from_another_schema'); + end; + procedure named_exc_ora is begin ut.expect(g_tests_results).to_match('^\s*Success resolve and match oracle named exception \[[,\.0-9]+ sec\]\s*$','m'); @@ -392,13 +493,23 @@ is begin ut.expect(g_tests_results).to_match('^\s*Bad exception constant \[[,\.0-9]+ sec\] \(FAILED - [0-9]+\)\s*$','m'); ut.expect(g_tests_results).to_match('bad_exc_const\s*ORA-20143: Test error\s*ORA-06512: at "UT3_TESTER.ANNOTATED_PACKAGE_WITH_THROWS"'); - end; - - procedure drop_test_package is - pragma autonomous_transaction; + end; + + procedure referencing_uninitialized_exception is + l_test_results ut3_develop.ut_varchar2_list; + l_actual clob; begin - execute immediate 'drop package annotated_package_with_throws'; - execute immediate 'drop package exc_pkg'; + select * bulk collect into l_test_results from table(ut3_develop.ut.run(('annotated_package_with_throws.referencing_uninitialized_exception'))); + + l_actual := ut3_develop.ut_utils.table_to_clob(l_test_results); + ut.expect(l_actual).to_match('^\s*Uninitialized exception variable can be used successfully in a test \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect(l_actual).not_to_match('referencing_uninitialized_exception'); + end; + + procedure not_throwing_expected_exceptions is + begin + ut.expect(g_tests_results).to_match('not_throwing_expected_exceptions'); + ut.expect(g_tests_results).to_match('Actual: -20143 was expected to be one of: \(annotated_package_with_throws.e_some_local_exception, annotated_package_with_throws.e_uninitialized_exception_variable, -20211\)'); end; end; 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 a9d20183d..a41c18d2b 100644 --- a/test/ut3_tester/core/annotations/test_annot_throws_exception.pks +++ b/test/ut3_tester/core/annotations/test_annot_throws_exception.pks @@ -5,7 +5,11 @@ is --%beforeall procedure recollect_tests_results; - + + --%afterall + procedure drop_test_package; + + --%test(Gives success when annotated number exception is thrown) procedure throws_same_annotated_except; @@ -18,7 +22,7 @@ is --%test(Gives failure when the raised exception is different that the annotated one) procedure throws_diff_annotated_except; - --%test(Ignores when the annotation throws is empty) + --%test(Ignores the throws annotation when it is empty) procedure throws_empty; --%test(Ignores when only bad parameters are passed, the test raise a exception and it shows errored test) @@ -36,7 +40,7 @@ is --%test(Detects a valid exception number within many invalid ones) procedure one_valid_exception_number; - --%test(Gives failure when a exception is expected and nothing is thrown) + --%test(Gives failure when an exception is expected and nothing is thrown) procedure nothing_thrown; --%test(Single exception defined as a constant number in package) @@ -45,19 +49,19 @@ is --%test(Gives success when one of annotated exception using constant is thrown) procedure list_of_exc_constant; - --%test(Gives failure when the raised exception is different that the annotated one using variable) + --%test(Gives failure when the raised exception is different than the annotated one using variable) procedure fail_not_match_exc; - --%test(Success when one of exception from mixed list of number and constant is thrown) + --%test(Success when one of exception from a mixed list of number and constant is thrown) procedure mixed_exc_list; - --%test(Success when match exception even if other variable on list dont exists) + --%test(Success when match exception even if another variable on list doesn't exists) procedure mixed_list_notexi; --%test(Success resolve and match named exception defined in pragma exception init) procedure named_exc_pragma; - - --%test(Success resolve and match oracle named exception no data) + + --%test(Success resolve and match oracle named exception no data) procedure named_exc_ora; --%test(Success resolve and match oracle named exception dup val index) @@ -73,10 +77,32 @@ is procedure non_existing_const; --%test(Bad exception constant) - procedure bad_exc_const; + procedure bad_exc_const; + + --%test(Uninitialized exception variable can be used successfully in a test) + procedure referencing_uninitialized_exception; + + --%test(a Failure report shows all expected exceptions) + procedure not_throwing_expected_exceptions; - --%afterall - procedure drop_test_package; + --%context(referencing exceptions when running from another schema) + + --%beforeall + procedure run_from_another_schema; + + --%test(Success referencing an exception variable when running from another schema) + procedure named_exc_pragma_run_from_another_schema; + + --%test(Success referencing a numeric exception variable when running from another schema) + procedure exc_number_var_run_from_another_schema; + + --%test(Success referencing an exception variable without a package name when running from another schema) + procedure named_exc_pragma_run_from_another_schema_no_package_name; + + --%test(Success referencing a numeric exception variable without a package name when running from another schema) + procedure exc_number_var_run_from_another_schema_no_package_name; + + --%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 a5503f841..8eb3c2f6d 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pkb +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pkb @@ -71,19 +71,19 @@ create or replace package body test_annotation_manager is exec_autonomous(q'[alter package dummy_test_package compile]'); end; - procedure create_parse_proc_as_ut3$user# is + procedure create_parse_proc_as_ut3_user is begin - ut3_tester_helper.main_helper.create_parse_proc_as_ut3$user#(); + ut3_tester_helper.main_helper.create_parse_proc_as_ut3_user(); end; - procedure parse_dummy_test_as_ut3$user# is + procedure parse_dummy_test_as_ut3_user is begin - ut3_tester_helper.main_helper.parse_dummy_test_as_ut3$user#(); + ut3_tester_helper.main_helper.parse_dummy_test_as_ut3_user(); end; - procedure drop_parse_proc_as_ut3$user# is + procedure drop_parse_proc_as_ut3_user is begin - ut3_tester_helper.main_helper.drop_parse_proc_as_ut3$user#(); + ut3_tester_helper.main_helper.drop_parse_proc_as_ut3_user(); exception when others then null; @@ -400,10 +400,10 @@ create or replace package body test_annotation_manager is l_start_date date; begin l_start_date := sysdate; - parse_dummy_test_as_ut3$user#(); + parse_dummy_test_as_ut3_user(); drop_dummy_test_package(); --Act - parse_dummy_test_as_ut3$user#(); + parse_dummy_test_as_ut3_user(); --Assert select count(1) into l_cache_count diff --git a/test/ut3_tester/core/annotations/test_annotation_manager.pks b/test/ut3_tester/core/annotations/test_annotation_manager.pks index 5d7464fd2..7207917f3 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pks +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pks @@ -14,9 +14,9 @@ create or replace package test_annotation_manager is procedure create_dummy_test_package; - procedure create_parse_proc_as_ut3$user#; + procedure create_parse_proc_as_ut3_user; - procedure drop_parse_proc_as_ut3$user#; + procedure drop_parse_proc_as_ut3_user; procedure drop_dummy_test_package; @@ -85,8 +85,8 @@ create or replace package test_annotation_manager is procedure update_modified_test_package; --%test(Keeps annotations in cache when object was removed but user can't see whole schema) - --%beforetest(create_dummy_test_package,create_parse_proc_as_ut3$user#) - --%aftertest(drop_parse_proc_as_ut3$user#) + --%beforetest(create_dummy_test_package,create_parse_proc_as_ut3_user) + --%aftertest(drop_parse_proc_as_ut3_user) procedure keep_dropped_data_in_cache; --%test(Does not return data for dropped object) diff --git a/test/ut3_tester/core/annotations/test_annotation_parser.pkb b/test/ut3_tester/core/annotations/test_annotation_parser.pkb index bc789c377..beb526863 100644 --- a/test/ut3_tester/core/annotations/test_annotation_parser.pkb +++ b/test/ut3_tester/core/annotations/test_annotation_parser.pkb @@ -1,27 +1,28 @@ create or replace package body test_annotation_parser is procedure test_proc_comments is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; - begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - -- %ann1(Name of suite) - -- wrong line - -- %ann2(some_value) - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' -- %ann1(Name of suite)' || chr(10), + ' -- wrong line' || chr(10), + ' -- %ann2(some_value)' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert - l_expected := ut3_develop.ut_annotations( ut3_develop.ut_annotation(2,'suite',null, null), ut3_develop.ut_annotation(3,'displayname','Name of suite',null), @@ -34,30 +35,33 @@ create or replace package body test_annotation_parser is end; procedure include_floating_annotations is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - -- %ann1(Name of suite) - -- %ann2(all.globaltests) - - --%test - procedure foo; - - -- %ann3(Name of suite) - -- %ann4(all.globaltests) - - --%test - procedure bar; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' -- %ann1(Name of suite)' || chr(10), + ' -- %ann2(all.globaltests)' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' procedure foo;' || chr(10), + '' || chr(10), + ' -- %ann3(Name of suite)' || chr(10), + ' -- %ann4(all.globaltests)' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' procedure bar;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -77,40 +81,41 @@ create or replace package body test_annotation_parser is end; procedure parse_complex_with_functions is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure foo; - - - --%beforeeach - procedure foo2; - - --test comment - -- wrong comment - - - /* - describtion of the procedure - */ - --%beforeeach(key=testval) - PROCEDURE foo3(a_value number default null); - - --%all - function foo4(a_val number default null - , a_par varchar2 default := ''asdf''); - END;'; + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' procedure foo;' || chr(10), + '' || chr(10), + '' || chr(10), + ' --%beforeeach' || chr(10), + ' procedure foo2;' || chr(10), + '' || chr(10), + ' --test comment' || chr(10), + ' -- wrong comment' || chr(10), + '' || chr(10), + '' || chr(10), + '/*' || chr(10), + ' describtion of the procedure' || chr(10), + ' */' || chr(10), + ' --%beforeeach(key=testval)' || chr(10), + ' PROCEDURE foo3(a_value number default null);' || chr(10), + '' || chr(10), + ' --%all' || chr(10), + ' function foo4(a_val number default null' || chr(10), + ' , a_par varchar2 default := ''asdf'');' || chr(10), + 'END;')); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -128,21 +133,24 @@ create or replace package body test_annotation_parser is end; procedure no_procedure_annotation is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -156,21 +164,24 @@ create or replace package body test_annotation_parser is end; procedure parse_accessible_by is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt accessible by (foo) AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt accessible by (foo) AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -184,24 +195,27 @@ create or replace package body test_annotation_parser is end; procedure complex_package_declaration is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt - ACCESSIBLE BY (calling_proc) - authid current_user - AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt' || chr(10), + ' ACCESSIBLE BY (calling_proc)' || chr(10), + ' authid current_user' || chr(10), + ' AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -215,21 +229,24 @@ create or replace package body test_annotation_parser is end; procedure complex_text is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite - --%displayname(name = Name of suite) - -- %suitepath(key=all.globaltests,key2=foo,"--%some text") - - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' --%displayname(name = Name of suite)' || chr(10), + ' -- %suitepath(key=all.globaltests,key2=foo,"--%some text")' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -243,26 +260,29 @@ create or replace package body test_annotation_parser is end; procedure ignore_annotations_in_comments is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - /* - Some comment - -- inlined - -- %ignored - */ - -- %suite - --%displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' /*' || chr(10), + ' Some comment' || chr(10), + ' -- inlined' || chr(10), + ' -- %ignored' || chr(10), + ' */' || chr(10), + ' -- %suite' || chr(10), + ' --%displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -311,17 +331,19 @@ v58yvbLAXLi9gYHwoIvAgccti+Cmpg0DKLY= end; procedure brackets_in_desc is - - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite(Name of suite (including some brackets) and some more text) -END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite(Name of suite (including some brackets) and some more text)' || chr(10), + 'END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -332,24 +354,27 @@ END;'; end; procedure test_space_before_annot_params is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; - l_expected ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - /* - Some comment - -- inlined - */ - -- %suite - -- %suitepath (all.globaltests) - - procedure foo; -END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' /*' || chr(10), + ' Some comment' || chr(10), + ' -- inlined' || chr(10), + ' */' || chr(10), + ' -- %suite' || chr(10), + ' -- %suitepath (all.globaltests)'|| chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + 'END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -362,18 +387,21 @@ END;'; procedure test_windows_newline as - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite)' || chr(13) || chr(10) - || ' -- %suitepath(all.globaltests) - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(13) || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -387,21 +415,24 @@ END;'; procedure test_annot_very_long_name as - l_source clob; - l_actual ut3_develop.ut_annotations; - l_expected ut3_develop.ut_annotations; + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_dit_it; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(Name of suite)' || chr(10), + ' -- %suitepath(all.globaltests)' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' procedure very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_dit_it;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -415,30 +446,33 @@ END;'; end; procedure test_upper_annot is - l_source clob; + l_source dbms_preprocessor.source_lines_t; l_actual ut3_develop.ut_annotations; l_expected ut3_develop.ut_annotations; begin - l_source := 'PACKAGE test_tt AS - -- %SUITE - -- %DISPLAYNAME(Name of suite) - -- %SUITEPATH(all.globaltests) - - -- %ANN1(Name of suite) - -- %ANN2(all.globaltests) - - --%TEST - procedure foo; - - -- %ANN3(Name of suite) - -- %ANN4(all.globaltests) - - --%TEST - procedure bar; - END;'; + --Arrange + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %SUITE' || chr(10), + ' -- %DISPLAYNAME(Name of suite)' || chr(10), + ' -- %SUITEPATH(all.globaltests)' || chr(10), + '' || chr(10), + ' -- %ANN1(Name of suite)' || chr(10), + ' -- %ANN2(all.globaltests)' || chr(10), + '' || chr(10), + ' --%TEST' || chr(10), + ' procedure foo;' || chr(10), + '' || chr(10), + ' -- %ANN3(Name of suite)' || chr(10), + ' -- %ANN4(all.globaltests)' || chr(10), + '' || chr(10), + ' --%TEST' || chr(10), + ' procedure bar;' || chr(10), + ' END;' || chr(10) + )); --Act - l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source); + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); --Assert l_expected := ut3_develop.ut_annotations( @@ -457,5 +491,414 @@ END;'; end; + ------------------------------------------------------------ + -- replace_multiline_comments coverage tests + ------------------------------------------------------------ + + procedure test_rmc_empty_source is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result.count).to_equal(0); + end; + + procedure test_rmc_no_ml_comment_marker is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'procedure foo is' || chr(10), + 'begin' || chr(10), + ' null;' || chr(10), + 'end;' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(ut3_tester_helper.main_helper.lines_to_str(l_result)).to_equal(ut3_tester_helper.main_helper.lines_to_str(l_input)); + end; + + procedure test_rmc_line_inside_ml_comment is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'x := 1; /* start' || chr(10), + 'this whole line is comment' || chr(10), + 'end comment */ x := 2;' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('x := 1; ' || chr(10)); + ut.expect(l_result(2)).to_equal(''); + ut.expect(l_result(3)).to_equal(' x := 2;' || chr(10)); + end; + + procedure test_rmc_ml_comment_closed_midline is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + '/* open' || chr(10), + 'still inside' || chr(10), + '*/ code /* remove this too */ kept' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(2)).to_equal(''); + ut.expect(l_result(3)).to_equal(' code kept' || chr(10)); + end; + + procedure test_rmc_fast_path_no_tokens is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + -- line 2 has none of / - ' so hits fast path B; line 1 needed to pass pre-scan + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + '/* comment */' || chr(10), + 'begin' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(2)).to_equal('begin' || chr(10)); + end; + + procedure test_rmc_ml_comment_single_line is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'x := /* inline comment */ 42;' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('x := 42;' || chr(10)); + end; + + procedure test_rmc_single_line_comment_preserved is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + '/* marker */' || chr(10), + ' -- %test annotation' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(2)).to_equal(' -- %test annotation' || chr(10)); + end; + + procedure test_rmc_string_literal_protects_markers is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'v := ''val /* not a comment */ here'';' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('v := ''val /* not a comment */ here'';' || chr(10)); + end; + + procedure test_rmc_string_literal_escaped_quotes is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'v := ''it''''s a /* test */'';' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('v := ''it''''s a /* test */'';' || chr(10)); + end; + + procedure test_rmc_q_quoted_string_literal is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'v := q''[/* not a comment */]'';' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('v := q''[/* not a comment */]'';' || chr(10)); + end; + + procedure test_rmc_unclosed_string_literal is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'v := ''hello /* inside unclosed' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('v := ''hello /* inside unclosed' || chr(10)); + end; + + procedure test_rmc_unclosed_q_string is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + c_line constant varchar2(100) := q'(v := q'[/* unclosed q-string)' || chr(10); + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list(c_line)); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal(c_line); + end; + + procedure test_rmc_multiple_ml_comments_one_line is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'a /* one */ := /* two */ 1;' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('a := 1;' || chr(10)); + end; + + procedure test_rmc_comment_after_string_with_slash is + l_input dbms_preprocessor.source_lines_t; + l_result dbms_preprocessor.source_lines_t; + begin + --Arrange + l_input := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'v := ''a/b''; -- this is /* not */ a ml comment' || chr(10) + )); + + --Act + l_result := ut3_develop.ut_utils.replace_multiline_comments(l_input); + + --Assert + ut.expect(l_result(1)).to_equal('v := ''a/b''; -- this is /* not */ a ml comment' || chr(10)); + end; + + procedure test_multiline_proc_header_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- procedure header spans multiple lines before terminating ; + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' procedure foo(' || chr(10), + ' a_param1 varchar2' || chr(10), + ' ,a_param2 number default null' || chr(10), + ' );' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ), + ut3_develop.ut_annotation( 4, 'test', null, 'foo' ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_non_comment_line_resets_block_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- a non-comment non-proc line between annotation and procedure breaks the association + -- %test becomes a floating top-level annotation with no subobject + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' pragma serially_reusable;' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + -- %test is NOT associated with foo — accumulator was reset by pragma line + -- it surfaces as a top-level annotation with no subobject_name + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ), + ut3_develop.ut_annotation( 4, 'test', null, null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_annotation_not_at_line_start_ignored_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- code before -- means it is not at start of line and must be ignored + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' x := 1; -- %not_an_annotation' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_whitespace_line_between_comment_and_proc_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- space-only line between annotation comment and procedure declaration is tolerated + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + '' || chr(10), + ' --%test' || chr(10), + ' ' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ), + ut3_develop.ut_annotation( 4, 'test', null, 'foo' ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_space_between_dashes_and_qualifier_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- spaces between -- and % are skipped and annotation is still recognised + -- annotations are not adjacent to a procedure so they are top-level + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- %displayname(my suite)' || chr(10), + '' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ), + ut3_develop.ut_annotation( 3, 'displayname', 'my suite', null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_percent_not_after_dashes_ignored_lines is + l_source dbms_preprocessor.source_lines_t; + l_actual ut3_develop.ut_annotations; + l_expected ut3_develop.ut_annotations; + begin + --Arrange + -- -- present and % present on line but % is not immediately after -- + -- e.g. a regular comment that happens to mention 100% + l_source := ut3_tester_helper.main_helper.make_source(ut3_develop.ut_varchar2_list( + 'PACKAGE test_tt AS' || chr(10), + ' -- %suite' || chr(10), + ' -- coverage is 100% on this module' || chr(10), + ' procedure foo;' || chr(10), + ' END;' || chr(10) + )); + + --Act + l_actual := ut3_develop.ut_annotation_parser.parse_object_annotations(l_source, 'PACKAGE'); + + --Assert + l_expected := ut3_develop.ut_annotations( + ut3_develop.ut_annotation( 2, 'suite', null, null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + end test_annotation_parser; -/ +/ \ No newline at end of file diff --git a/test/ut3_tester/core/annotations/test_annotation_parser.pks b/test/ut3_tester/core/annotations/test_annotation_parser.pks index a4fe3ed70..c61c14d3a 100644 --- a/test/ut3_tester/core/annotations/test_annotation_parser.pks +++ b/test/ut3_tester/core/annotations/test_annotation_parser.pks @@ -19,24 +19,57 @@ create or replace package test_annotation_parser is procedure complex_text; --%test(Ignores content of multi-line comments) procedure ignore_annotations_in_comments; - - --%test(Ignores wrapped package and does not raise exception) + -- %test(ignore wrapped package source) procedure ignore_wrapped_package; - --%test(Parses package level annotations with annotation params containing brackets) procedure brackets_in_desc; - --%test(Parses annotation text even with spaces before brackets) procedure test_space_before_annot_params; - -- %test(Parses source-code with Windows-style newline) procedure test_windows_newline; - - -- %test(Parses annotations with very long object names) + -- %test(parse annotations with very long procedure name) procedure test_annot_very_long_name; - -- %test(Parses upper case annotations) procedure test_upper_annot; + -- %test(empty source returns immediately without processing) + procedure test_rmc_empty_source; + -- %test(source with no multiline comment markers returned unchanged) + procedure test_rmc_no_ml_comment_marker; + -- %test(line entirely inside multiline comment is blanked) + procedure test_rmc_line_inside_ml_comment; + -- %test(closing delimiter mid-line falls through to scan remainder) + procedure test_rmc_ml_comment_closed_midline; + -- %test(fast path line with no slash dash or quote copied unchanged) + procedure test_rmc_fast_path_no_tokens; + -- %test(single-line block comment removed on same line) + procedure test_rmc_ml_comment_single_line; + -- %test(single-line comment preserved intact) + procedure test_rmc_single_line_comment_preserved; + -- %test(string literal containing comment markers not stripped) + procedure test_rmc_string_literal_protects_markers; + -- %test(string literal with escaped quotes handled correctly) + procedure test_rmc_string_literal_escaped_quotes; + -- %test(q-quoted string literal protects interior comment markers) + procedure test_rmc_q_quoted_string_literal; + -- %test(unclosed string literal copies remainder and exits scan) + procedure test_rmc_unclosed_string_literal; + -- %test(unclosed q-quoted string copies remainder and exits scan) + procedure test_rmc_unclosed_q_string; + -- %test(multiple block comments on single line all removed) + procedure test_rmc_multiple_ml_comments_one_line; + -- %test(comment markers after string with slash handled correctly) + procedure test_rmc_comment_after_string_with_slash; + --%test(Procedure header spanning multiple lines is handled correctly) + procedure test_multiline_proc_header_lines; + --%test(Non-comment line between annotation and procedure resets association) + procedure test_non_comment_line_resets_block_lines; + --%test(Comment with non-whitespace before dashes is not treated as annotation) + procedure test_annotation_not_at_line_start_ignored_lines; + --%test(Spaces between dashes and annotation qualifier are skipped correctly) + procedure test_space_between_dashes_and_qualifier_lines; + --%test(Percent sign not immediately after dashes is not treated as annotation) + procedure test_percent_not_after_dashes_ignored_lines; + end test_annotation_parser; -/ +/ \ No newline at end of file diff --git a/test/ut3_tester/core/test_output_buffer.pkb b/test/ut3_tester/core/test_output_buffer.pkb index 2e8b3337c..55670b8b8 100644 --- a/test/ut3_tester/core/test_output_buffer.pkb +++ b/test/ut3_tester/core/test_output_buffer.pkb @@ -1,5 +1,13 @@ create or replace package body test_output_buffer is + function long_text return varchar2 is + l_txt varchar2(32767); + begin + for i in 1 .. 100 loop + l_txt := l_txt||lpad('a text', 310, ',a text'); + end loop; + return l_txt; + end; procedure test_receive is l_actual_text clob; l_actual_item_type varchar2(1000); @@ -10,18 +18,19 @@ create or replace package body test_output_buffer is begin --Arrange l_buffer := ut3_develop.ut_output_clob_table_buffer(); - l_expected_text := to_clob(lpad('a text', 31000, ',a text')) - || chr(10) || to_clob(lpad('a text', 31000, ',a text')) - || chr(13) || to_clob(lpad('a text', 31000, ',a text')) - || chr(13) || chr(10) || to_clob(lpad('a text', 31000, ',a text')) || to_clob(lpad('a text', 31000, ',a text')); + l_expected_text := to_clob('a text') ||long_text() + || chr(10) || long_text() + || chr(13) || long_text() + || chr(13) || chr(10) || long_text() || long_text(); l_expected_item_type := lpad('some item type',1000,'-'); --Act + l_buffer.lock_buffer(); l_buffer.send_clob(l_expected_text, l_expected_item_type); l_buffer.close(); select text, item_type into l_actual_text, l_actual_item_type - from table(l_buffer.get_lines(0,0)); + from table(l_buffer.get_lines(0.1,0.1)); --Assert ut.expect(l_actual_text).to_equal(l_expected_text); @@ -32,7 +41,14 @@ create or replace package body test_output_buffer is ut.expect(l_remaining).to_equal(0); end; - + + procedure test_wait_for_producer is + l_buffer ut3_develop.ut_output_buffer_base; + begin + l_buffer := ut3_develop.ut_output_clob_table_buffer(); + ut.expect( l_buffer.get_lines_cursor(0.1) ).to_be_empty(); + end; + procedure test_doesnt_send_on_null_text is l_cur sys_refcursor; l_result integer; @@ -48,8 +64,8 @@ create or replace package body test_output_buffer is procedure test_doesnt_send_on_null_elem is - l_cur sys_refcursor; - l_result integer; + l_actual sys_refcursor; + l_expected sys_refcursor; l_buffer ut3_develop.ut_output_buffer_base := ut3_develop.ut_output_table_buffer(); l_message_id varchar2(255); l_text varchar2(4000); @@ -59,9 +75,12 @@ create or replace package body test_output_buffer is l_buffer.send_lines(ut3_develop.ut_varchar2_rows(null)); l_buffer.send_lines(ut3_develop.ut_varchar2_rows('test')); - select message_id, text into l_message_id, l_text from table(ut3_tester_helper.run_helper.ut_output_buffer_tmp); - ut.expect(l_message_id).to_equal('1'); - ut.expect(l_text).to_equal('test'); + open l_actual for + select text from table(ut3_tester_helper.run_helper.ut_output_buffer_tmp); + open l_expected for + select 'test' as text from dual; + + ut.expect(l_actual).to_equal(l_expected); end; procedure test_send_line is @@ -86,11 +105,12 @@ create or replace package body test_output_buffer is begin --Arrange l_expected := 'a text'; + l_buffer.lock_buffer(); l_buffer.send_line(l_expected); l_start := localtimestamp; --Act begin - select text into l_result from table(l_buffer.get_lines(1,1)); + select text into l_result from table(l_buffer.get_lines(0,0.3)); ut.fail('Expected a timeout exception but nothing was raised'); exception when others then @@ -101,7 +121,7 @@ create or replace package body test_output_buffer is --Throws a timeout exception ut.expect(dbms_utility.format_error_stack()).to_match('ORA'||ut3_develop.ut_utils.gc_out_buffer_timeout); --Waited for one second - ut.expect(l_duration).to_be_greater_than(interval '0.99' second); + ut.expect(l_duration).to_be_greater_or_equal(interval '0.3' second); end; select count(1) into l_remaining from table(ut3_tester_helper.run_helper.ut_output_buffer_tmp) where output_id = l_buffer.output_id; @@ -116,13 +136,15 @@ create or replace package body test_output_buffer is l_buffer ut3_develop.ut_output_buffer_base; begin --Arrange - l_stale_buffer.start_date := sysdate - 2; + l_stale_buffer.start_date := sysdate - 10; --initialize with new start date l_stale_buffer.init(); + l_stale_buffer.lock_buffer(); l_stale_buffer.send_line('some text'); l_stale_buffer.close(); l_fresh_buffer := ut3_develop.ut_output_table_buffer(); + l_fresh_buffer.lock_buffer(); l_fresh_buffer.send_line('some text'); l_fresh_buffer.close(); @@ -131,9 +153,9 @@ create or replace package body test_output_buffer is --Assert -- Data in "fresh" buffer remains - ut.expect( l_fresh_buffer.get_lines_cursor(0,0), l_buffer.self_type ).to_have_count(1); + ut.expect( l_fresh_buffer.get_lines_cursor(0,0), l_fresh_buffer.self_type ).to_have_count(1); -- Data in "stale" buffer is purged and so the call to get_lines_cursor throws ORA-20218 - ut.expect( l_stale_buffer.get_lines_cursor(0,0), l_buffer.self_type ).to_be_empty(); + ut.expect( l_stale_buffer.get_lines_cursor(0,0), l_stale_buffer.self_type ).to_be_empty(); end; procedure test_purge_text_buffer is @@ -146,5 +168,50 @@ create or replace package body test_output_buffer is test_purge(ut3_develop.ut_output_clob_table_buffer()); end; + procedure text_buffer_send_multibyte is + l_input varchar2(32767); + l_max_len integer := ut3_develop.ut_utils.gc_max_storage_varchar2_len; + l_buffer ut3_develop.ut_output_buffer_base := ut3_develop.ut_output_table_buffer(); + l_text varchar2(4000); + begin + --Arrange + ut3_tester_helper.run_helper.delete_buffer(); + l_input := rpad( '❤', l_max_len, 'a' ); + ut.expect( lengthb( l_input ) ).to_be_greater_than(l_max_len); + + --Act + l_buffer.send_line(l_input); + --Assert + select text into l_text from table(ut3_tester_helper.run_helper.ut_output_buffer_tmp); + ut.expect(lengthb(l_text)).to_be_less_or_equal(l_max_len); + end; + + procedure text_buffer_send_clob_multib is + l_input clob; + l_max_len integer := ut3_develop.ut_utils.gc_max_storage_varchar2_len; + l_buffer ut3_develop.ut_output_buffer_base := ut3_develop.ut_output_table_buffer(); + l_text varchar2(4000); + begin + --Arrange + ut3_tester_helper.run_helper.delete_buffer(); + l_input := rpad( '❤', l_max_len, 'a' ); + ut.expect( ut3_develop.ut_utils.lengthb_clob( l_input ) ).to_be_greater_than(l_max_len); + + --Act + l_buffer.send_clob(l_input); + --Assert + select text into l_text from table(ut3_tester_helper.run_helper.ut_output_buffer_tmp); + ut.expect(lengthb(l_text)).to_be_less_or_equal(l_max_len); + end; + + procedure concurrent_calls_do_not_cause_exception is + l_consumer_buffer_instance ut3_develop.ut_output_buffer_base; + l_shared_output_id raw(32) := sys_guid(); + begin + ut3_tester_helper.run_helper.run_as_job('declare x ut3_develop.ut_output_buffer_base; begin x := ut3_develop.ut_output_table_buffer(a_output_id => '''||l_shared_output_id||'''); end;'); + dbms_session.sleep(0.5); + l_consumer_buffer_instance := ut3_develop.ut_output_table_buffer(a_output_id => l_shared_output_id); + end; + end test_output_buffer; / diff --git a/test/ut3_tester/core/test_output_buffer.pks b/test/ut3_tester/core/test_output_buffer.pks index 24c2c01eb..aa5c830d3 100644 --- a/test/ut3_tester/core/test_output_buffer.pks +++ b/test/ut3_tester/core/test_output_buffer.pks @@ -2,10 +2,33 @@ create or replace package test_output_buffer is --%suite(output_buffer) --%suitepath(utplsql.ut3_tester.core) + + + --%context(Read and write within the same session) + + + --%endcontext + --%context(Buffer is read in a different session than buffer write) + + --reader will wait for a_initial_timeout seconds for the writer process to start and then it will finish with error + + --reader will wait forever (beyond a_initial_timeout) if the writer process is started and end of data row was not received from the buffer + + --reader stops after reading the end of data signal from the buffer + + --reader stops when writer process ends and all data was read from the buffer + + + --%endcontext + --%test(Receives a line from buffer table and deletes) procedure test_receive; + --%test(Waits specified time for producer to lock the buffer ) + --%throws(-20218) + procedure test_wait_for_producer; + --%test(Does not send line if null text given) procedure test_doesnt_send_on_null_text; @@ -19,12 +42,21 @@ create or replace package test_output_buffer is procedure test_waiting_for_data; --%test(Purges text buffer data older than one day and leaves the rest) - --%throws(-20218) procedure test_purge_text_buffer; --%test(Purges clob buffer data older than one day and leaves the rest) - --%throws(-20218) procedure test_purge_clob_buffer; + --%test(Successfully sends multibyte long line into text buffer) + procedure text_buffer_send_multibyte; + + --%test(Successfully sends multibyte long clob line into text buffer) + procedure text_buffer_send_clob_multib; + + --%beforetest(ut3_tester_helper.run_helper.slow_down_insert_into_table_buffer) + --%aftertest(ut3_tester_helper.run_helper.cleanup_slow_down_insert_into_table_buffer) + --%test(concurrent calls to init() procedure do not cause errors - Issue #985) + procedure concurrent_calls_do_not_cause_exception; + end test_output_buffer; / diff --git a/test/ut3_tester/core/test_suite_builder.pkb b/test/ut3_tester/core/test_suite_builder.pkb index 570d86a0d..9a86772c7 100644 --- a/test/ut3_tester/core/test_suite_builder.pkb +++ b/test/ut3_tester/core/test_suite_builder.pkb @@ -1370,6 +1370,54 @@ create or replace package body test_suite_builder is ); end; + procedure context_name_case_insensitive is + l_actual clob; + l_annotations ut3_develop.ut_annotations; + begin + --Arrange + l_annotations := ut3_develop.ut_annotations( + ut3_develop.ut_annotation(1, 'suite','Cool', null), + ut3_develop.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'), + ut3_develop.ut_annotation(3, 'test','In suite', 'suite_level_test'), + ut3_develop.ut_annotation(4, 'context','A context', null), + ut3_develop.ut_annotation(5, 'name','a_Context', null), + ut3_develop.ut_annotation(6, 'beforeall',null, 'context_setup'), + ut3_develop.ut_annotation(7, 'test', 'In context', 'test_in_a_context'), + ut3_develop.ut_annotation(8, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + ''|| + '' || + '%' || + '%' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '%a_contextA contextsome_package.a_context' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagecontext_setup' || + '%' || + '' || + '' || + '' || + '' || + '%some_packagesuite_level_beforeall' || + '%' || + '' || + ''|| + '' + ); + end; + procedure throws_value_empty is l_actual clob; l_annotations ut3_develop.ut_annotations; diff --git a/test/ut3_tester/core/test_suite_builder.pks b/test/ut3_tester/core/test_suite_builder.pks index 4cbfcad74..09ff3c0b3 100644 --- a/test/ut3_tester/core/test_suite_builder.pks +++ b/test/ut3_tester/core/test_suite_builder.pks @@ -142,7 +142,7 @@ create or replace package test_suite_builder is --%test(Falls back to default context name and gives warning when name contains spaces) procedure name_with_spaces_invalid; - --%test(Raises warning when more than one name annotation used ) + --%test(Raises warning when more than one name annotation used) procedure duplicate_name_annotation; --%test(Is ignored when used outside of context - no warning given) @@ -154,6 +154,9 @@ create or replace package test_suite_builder is --%test(Is applied to corresponding context when multiple contexts used) procedure multiple_contexts; + --%test(Context name is case insensitive) + procedure context_name_case_insensitive; + --%endcontext --%context(--%throws annotation) diff --git a/test/ut3_tester/core/test_suite_manager.pkb b/test/ut3_tester/core/test_suite_manager.pkb index 11cc7acfe..c818ad212 100644 --- a/test/ut3_tester/core/test_suite_manager.pkb +++ b/test/ut3_tester/core/test_suite_manager.pkb @@ -278,8 +278,167 @@ end test_package_with_ctx;]'; end; end test_package_with_ctx;]'; + + execute immediate q'[create or replace package test1_frontwildcard is + + --%suite + --%displayname(test1_frontwildcard) + --%suitepath(front_wildcard) + --%rollback(manual) + + --%test + --%displayname(Test1 from test test1_frontwildcard) + procedure first_test; + + --%test + --%displayname(Test2 from test test1_frontwildcard) + procedure second_test; + +end test1_frontwildcard;]'; + + execute immediate q'[create or replace package body test1_frontwildcard is + + procedure first_test is + begin + ut.expect(1).to_equal(1); + end; + + procedure second_test is + begin + ut.expect(1).to_equal(2); + end; + +end test1_frontwildcard;]'; + + execute immediate q'[create or replace package test2_frontwildcard is + + --%suite + --%displayname(test2_frontwildcard) + --%suitepath(front_wildcard) + --%rollback(manual) + + --%test + --%displayname(Test1 from test package test2_frontwildcard) + procedure first_test; + +end test2_frontwildcard;]'; + + execute immediate q'[create or replace package body test2_frontwildcard is + + procedure first_test is + begin + ut.expect(1).to_equal(1); + end; + +end test2_frontwildcard;]'; + + execute immediate q'[create or replace package middle_test1_wildcard is + + --%suite + --%displayname(middle_test1_wildcard) + --%suitepath(wild_middle_card) + --%rollback(manual) + + --%test + --%displayname(Test1 from test middle_test1_wildcard) + procedure middle_first_test; + + --%test + --%displayname(Test2 from test middle_test1_wildcard) + procedure middle_second_test; + +end middle_test1_wildcard;]'; + + execute immediate q'[create or replace package body middle_test1_wildcard is + + procedure middle_first_test is + begin + ut.expect(1).to_equal(1); + end; + + procedure middle_second_test is + begin + ut.expect(1).to_equal(2); + end; + +end middle_test1_wildcard;]'; + + execute immediate q'[create or replace package middle_test2_wildcard is + + --%suite + --%displayname(middle_test2_wildcard) + --%suitepath(wild_middle_card) + --%rollback(manual) + + --%test + --%displayname(Test1 from test package middle_test2_wildcard) + procedure middle_first_test; + +end middle_test2_wildcard;]'; + + execute immediate q'[create or replace package body middle_test2_wildcard is + + procedure middle_first_test is + begin + ut.expect(1).to_equal(1); end; +end middle_test2_wildcard;]'; + + execute immediate q'[create or replace package test1_multi_wildcard is + + --%suite + --%displayname(test1_multi_wildcard) + --%suitepath(wildcard_multi_asterisks) + --%rollback(manual) + + --%test + --%displayname(Test1 from test test1_multi_wildcard) + procedure first_multi_test1; + + --%test + --%displayname(Test2 from test test1_multi_wildcard) + procedure second_multi_test2; + +end test1_multi_wildcard;]'; + + execute immediate q'[create or replace package body test1_multi_wildcard is + + procedure first_multi_test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure second_multi_test2 is + begin + ut.expect(1).to_equal(2); + end; + +end test1_multi_wildcard;]'; + + execute immediate q'[create or replace package test2_multi_wildcard is + + --%suite + --%displayname(test2_multi_wildcard) + --%suitepath(wildcard_multi_asterisks) + --%rollback(manual) + + --%test + --%displayname(Test1 from test package test2_multi_wildcard) + procedure first_multi_test1; + +end test2_multi_wildcard;]'; + + execute immediate q'[create or replace package body test2_multi_wildcard is + + procedure first_multi_test1 is + begin + ut.expect(1).to_equal(1); + end; + +end test2_multi_wildcard;]'; + + end; procedure drop_dummy_packages is pragma autonomous_transaction; @@ -288,6 +447,12 @@ end test_package_with_ctx;]'; execute immediate 'drop package test_package_2'; execute immediate 'drop package test_package_3'; execute immediate 'drop package test_package_with_ctx'; + execute immediate 'drop package test1_frontwildcard'; + execute immediate 'drop package test2_frontwildcard'; + execute immediate 'drop package middle_test1_wildcard'; + execute immediate 'drop package middle_test2_wildcard'; + execute immediate 'drop package test1_multi_wildcard'; + execute immediate 'drop package test2_multi_wildcard'; end; procedure test_schema_run is @@ -945,6 +1110,36 @@ end;]'; ut.expect(sqlerrm).to_be_like('%failing_non_existing%'); end; + procedure test_search_nonex_pck_wild is + l_objects_to_run ut3_develop.ut_suite_items; + begin + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list('ut3_develop.failing_non_*')); + ut.fail('Non existing package did not raise exception'); + exception + when others then + ut.expect(sqlerrm).to_be_like('%failing_non_*%'); + end; + + procedure test_search_nonex_prc_wild is + l_objects_to_run ut3_develop.ut_suite_items; + begin + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list('ut3_tester.test_package_1.nonexist*')); + ut.fail('Non existing package did not raise exception'); + exception + when others then + ut.expect(sqlerrm).to_be_like('%nonexist*%'); + end; + + procedure test_search_nonex_path_wild is + l_objects_to_run ut3_develop.ut_suite_items; + begin + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list('ut3_develop:failing_non_*')); + ut.fail('Non existing path did not raise exception'); + exception + when others then + ut.expect(sqlerrm).to_be_like('%:failing_non_*%'); + end; + procedure test_search_nonexist_sch_pck is l_objects_to_run ut3_develop.ut_suite_items; begin @@ -1466,5 +1661,578 @@ end;]'; ut.expect(SQLCODE).to_equal(ut3_develop.ut_utils.gc_value_too_large); end; + procedure setup_remove_annot_test is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package test_removing_annotation as + --%suite + + --%test + procedure test1; + + --%test + procedure test2; + +end;]'; + end; + + procedure remove_annot_from_test is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package test_removing_annotation as + + procedure test1; + + procedure test2; + +end;]'; + end; + + procedure rem_one_annot_test is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package test_removing_annotation as + --%suite + + procedure test1; + + --%test + procedure test2; + +end;]'; + execute immediate q'[create or replace package body test_removing_annotation as + + procedure test1 is + begin + ut.expect(1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(1).to_equal(1); + end; + +end;]'; + end; + + procedure clean_remove_annot_test is + pragma autonomous_transaction; + begin + execute immediate 'drop package test_removing_annotation'; + end; + + procedure test_rem_cache_on_create is + l_test_report ut3_develop.ut_varchar2_list; + begin + + select * bulk collect into l_test_report from table(ut3_develop.ut.run(sys_context('USERENV', 'CURRENT_USER')||'.test_removing_annotation')); + + -- drop all tests + remove_annot_from_test; + + begin + select * bulk collect into l_test_report from table(ut3_develop.ut.run(sys_context('USERENV', 'CURRENT_USER') || '.test_removing_annotation')); + exception + when others then + ut.expect(sqlerrm).to_be_like('%ORA-20204: Suite package ut3_tester.test_removing_annotation does not exist%'); + end; + + end; + + procedure test_rem_cache_on_crt_anno is + l_test_report ut3_develop.ut_varchar2_list; + l_results clob; + begin + + select * bulk collect into l_test_report from table(ut3_develop.ut.run(sys_context('USERENV', 'CURRENT_USER')||'.test_removing_annotation')); + + -- drop single test + rem_one_annot_test; + ut3_develop.ut.run(sys_context('USERENV', 'CURRENT_USER')|| '.test_removing_annotation',a_reporter => ut3_develop.ut_documentation_reporter() ); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).to_be_like( '%1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s)%' ); + + end; + + procedure test_wild_card_obj_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.test_package_*'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test2_suite ut3_develop.ut_logical_suite; + l_ctx_suite ut3_develop.ut_logical_suite; + l_test_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(3); + + + for i in 1 .. 3 loop + l_test_suite := treat(l_objects_to_run(i) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name in ('test_package_with_ctx','tests', 'tests2')).to_be_true; + + case l_test_suite.name + when 'test_package_with_ctx' then + ut.expect(l_test_suite.items.count).to_equal(1); + l_ctx_suite:= treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_ctx_suite.name).to_equal('some_context'); + ut.expect(l_ctx_suite.description).to_equal('Some context description'); + ut.expect(l_ctx_suite.items.count).to_equal(1); + l_test_proc := treat(l_ctx_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test_proc.name).to_equal('test1'); + when 'tests' then + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test_package_1'); + ut.expect(l_test1_suite.items.count).to_equal(3); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3_develop.ut_utils.gc_rollback_manual); + l_test2_suite := treat(l_test1_suite.items(1) as ut3_develop.ut_logical_suite); + + ut.expect(l_test2_suite.name).to_equal('test_package_2'); + ut.expect(l_test2_suite.items.count).to_equal(3); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3_develop.ut_utils.gc_rollback_manual); + when 'tests2' then + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test_package_3'); + ut.expect(l_test1_suite.items.count).to_equal(3); + end case; + + end loop; + + end; + + procedure test_wild_card_prc_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.test_package_1.test*'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('tests'); + + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test_package_1'); + ut.expect(l_test1_suite.items.count).to_equal(2); + + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('test1'); + + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('test2'); + end; + + procedure test_wild_card_path_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||':tests*'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test2_suite ut3_develop.ut_logical_suite; + l_ctx_suite ut3_develop.ut_logical_suite; + l_test_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(2); + + + for i in 1 .. 2 loop + l_test_suite := treat(l_objects_to_run(i) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name in ('tests', 'tests2')).to_be_true; + + case l_test_suite.name + when 'tests' then + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test_package_1'); + ut.expect(l_test1_suite.items.count).to_equal(3); + + for i in 1 ..3 loop + case l_test1_suite.items(i).self_type + when 'UT_SUITE' then + l_test2_suite := treat(l_test1_suite.items(i) as ut3_develop.ut_logical_suite); + ut.expect(l_test2_suite.name).to_equal('test_package_2'); + ut.expect(l_test2_suite.items.count).to_equal(3); + + l_test_proc := treat(l_test2_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test_proc.name in ('test1', 'test2','context_test')).to_be_true; + + l_test_proc := treat(l_test2_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test_proc.name in ('test1', 'test2','context_test')).to_be_true; + + l_test_proc := treat(l_test2_suite.items(3) as ut3_develop.ut_test); + ut.expect(l_test_proc.name in ('test1', 'test2','context_test')).to_be_true; + + when 'UT_TEST' then + l_test_proc := treat(l_test1_suite.items(i) as ut3_develop.ut_test); + ut.expect(l_test_proc.name in ('test1', 'test2')).to_be_true; + end case; + end loop; + when 'tests2' then + ut.expect(l_test_suite.items.count).to_equal(1); + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test_package_3'); + ut.expect(l_test1_suite.items.count).to_equal(3); + for i in 1 .. 3 loop + l_test_proc := treat(l_test1_suite.items(i) as ut3_develop.ut_test); + ut.expect(l_test_proc.name in ('test1', 'test2','disabled_test')).to_be_true; + end loop; + end case; + + end loop; + + end; + + procedure test_wild_card_front_obj_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.*_frontwildcard'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('front_wildcard'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'test1_frontwildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_test'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_test'); + when 'test2_frontwildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_test'); + end case; + + end loop; + end; + + procedure test_wild_card_front_prc_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.test1_frontwildcard.*_test'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('front_wildcard'); + + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test1_frontwildcard'); + ut.expect(l_test1_suite.items.count).to_equal(2); + + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_test'); + + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_test'); + end; + + procedure test_wild_card_front_path_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||':*_wildcard'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('front_wildcard'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'test1_frontwildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_test'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_test'); + when 'test2_frontwildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_test'); + end case; + + end loop; + end; + + procedure test_wild_card_mid_obj_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.middle_*_wildcard'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wild_middle_card'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'middle_test1_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('middle_first_test'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('middle_second_test'); + when 'middle_test2_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('middle_first_test'); + end case; + + end loop; + end; + + procedure test_wild_card_mid_prc_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.middle_test1_wildcard.middle_*_test'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wild_middle_card'); + + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('middle_test1_wildcard'); + ut.expect(l_test1_suite.items.count).to_equal(2); + + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('middle_first_test'); + + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('middle_second_test'); + end; + + procedure test_wild_card_mid_path_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||':wild_*card'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wild_middle_card'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'middle_test1_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('middle_first_test'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('middle_second_test'); + when 'middle_test2_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('middle_first_test'); + end case; + + end loop; + end; + + procedure test_wild_card_mul_obj_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.*_multi_*card'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wildcard_multi_asterisks'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'test1_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_multi_test2'); + when 'test2_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + end case; + + end loop; + end; + + procedure test_wild_card_mul_prc_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||'.test1_multi_wildcard.*_multi_*'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wildcard_multi_asterisks'); + + l_test1_suite := treat(l_test_suite.items(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test1_suite.name).to_equal('test1_multi_wildcard'); + ut.expect(l_test1_suite.items.count).to_equal(2); + + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_multi_test2'); + end; + + procedure test_wild_card_mul_path_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||':wild*_multi_*risks'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wildcard_multi_asterisks'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'test1_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(2); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + l_test2_proc := treat(l_test1_suite.items(2) as ut3_develop.ut_test); + ut.expect(l_test2_proc.name).to_equal('second_multi_test2'); + when 'test2_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + end case; + + end loop; + end; + + procedure tst_wild_card_mul_lv_path_name is + c_path varchar2(100) := sys_context('USERENV', 'CURRENT_USER')||':wild*_multi_*risks.*_multi_wildcard.*_multi_test1'; + l_objects_to_run ut3_develop.ut_suite_items; + l_test_suite ut3_develop.ut_logical_suite; + l_test1_suite ut3_develop.ut_logical_suite; + l_test1_proc ut3_develop.ut_test; + l_test2_proc ut3_develop.ut_test; + begin + --Act + l_objects_to_run := ut3_develop.ut_suite_manager.configure_execution_by_path(ut3_develop.ut_varchar2_list(c_path)); + + --Assert + ut.expect(l_objects_to_run.count).to_equal(1); + l_test_suite := treat(l_objects_to_run(1) as ut3_develop.ut_logical_suite); + ut.expect(l_test_suite.name).to_equal('wildcard_multi_asterisks'); + ut.expect(l_test_suite.items.count).to_equal(2); + + for i in 1 .. 2 loop + l_test1_suite := treat(l_test_suite.items(i) as ut3_develop.ut_logical_suite); + case l_test1_suite.name + when 'test1_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + when 'test2_multi_wildcard' then + ut.expect(l_test1_suite.items.count).to_equal(1); + l_test1_proc := treat(l_test1_suite.items(1) as ut3_develop.ut_test); + ut.expect(l_test1_proc.name).to_equal('first_multi_test1'); + end case; + + end loop; + end; + + --%test(Path validation does not fail on Estonian NLS_SORT - fix #1252) + procedure path_validate_nls_sort is + l_schema_names ut3_develop.ut_varchar2_rows; + begin + --Arrange + execute immediate q'[alter session set nls_sort='estonian']'; + --Act + l_schema_names := ut3_develop.ut_suite_manager.get_schema_names(ut3_develop.ut_varchar2_list('ut3')); + --Asseert + ut.expect(sqlcode).to_equal(0); + end; + + + procedure reset_nls_sort is + begin + execute immediate q'[alter session set nls_sort='binary']'; + end; end test_suite_manager; / diff --git a/test/ut3_tester/core/test_suite_manager.pks b/test/ut3_tester/core/test_suite_manager.pks index 90b589b9e..44f102716 100644 --- a/test/ut3_tester/core/test_suite_manager.pks +++ b/test/ut3_tester/core/test_suite_manager.pks @@ -75,8 +75,17 @@ create or replace package test_suite_manager is --%test(Prepare runner for nonexisting package with schema) procedure test_search_nonexisting_pck; - - --%test(Prepare runner for nonexisting package without schema) + + --%test(Prepare runner for nonexisting package using wildcard filter) + procedure test_search_nonex_pck_wild; + + --%test(Prepare runner for nonexisting procedure using wildcard filter) + procedure test_search_nonex_prc_wild; + + --%test(Prepare runner for nonexisting path using wildcard filter) + procedure test_search_nonex_path_wild; + + --%test(Prepare runner for nonexisting package without schema) procedure test_search_nonexist_sch_pck; --%test(Test description with comma) @@ -154,7 +163,7 @@ create or replace package test_suite_manager is --%context(get_schema_ut_packages) - --%test(returns list of all unit test packages in given schema) + --%test(returns list of all unit test packages in given schema excluding packages that are not suites) --%beforetest(create_ut3_suite) --%aftertest(drop_ut3_suite) procedure test_get_schema_ut_packages; @@ -170,5 +179,73 @@ create or replace package test_suite_manager is procedure create_dummy_long_test_package; procedure drop_dummy_long_test_package; + + procedure setup_remove_annot_test; + procedure clean_remove_annot_test; + + --%test(Remove cache on package create or replace when all annotations removed) + --%beforetest(setup_remove_annot_test) + --%aftertest(clean_remove_annot_test) + procedure test_rem_cache_on_create; + + + --%test(Remove cache on package create or replace when single annotation removed) + --%beforetest(setup_remove_annot_test) + --%aftertest(clean_remove_annot_test) + procedure test_rem_cache_on_crt_anno; + + --%context(wildcard_filters) + + --%test(Execute test_packages using a object_name with wildcard at the end ) + procedure test_wild_card_obj_name; + + --%test(Execute test_packages using a procedure name with wildcard at the end) + procedure test_wild_card_prc_name; + + --%test(Execute test_packages using a path name with wildcard at the end) + procedure test_wild_card_path_name; + + --%test(Execute test_packages using a object_name with wildcard in the front) + procedure test_wild_card_front_obj_name; + + --%test(Execute test_packages using a procedure name with wildcard in the front) + procedure test_wild_card_front_prc_name; + + -- %test(Execute test_packages using a path name with wildcard in the front) + procedure test_wild_card_front_path_name; + + --%test(Execute test_packages using a object_name with wildcard in the middle) + procedure test_wild_card_mid_obj_name; + + --%test(Execute test_packages using a procedure name with wildcard in the middle) + procedure test_wild_card_mid_prc_name; + + -- %test(Execute test_packages using a path name with wildcard in the middle) + procedure test_wild_card_mid_path_name; + + --%test(Execute test_packages using a object_name with multiple wildcards) + procedure test_wild_card_mul_obj_name; + + --%test(Execute test_packages using a procedure name with multiple wildcards) + procedure test_wild_card_mul_prc_name; + + -- %test(Execute test_packages using a path name with multiple wildcards) + procedure test_wild_card_mul_path_name; + + -- %test(Execute test_packages using a path name with multiple wildcards on different level) + procedure tst_wild_card_mul_lv_path_name; + + --%endcontext + + --%context(paths validation) + + --%test(Path validation does not fail on Estonian NLS_SORT - fix #1252) + --%aftertest(reset_nls_sort) + procedure path_validate_nls_sort; + + procedure reset_nls_sort; + + --%endcontext + end test_suite_manager; / diff --git a/test/ut3_tester/core/test_ut_suite_tag_filter.pkb b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb new file mode 100644 index 000000000..32610edf6 --- /dev/null +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb @@ -0,0 +1,123 @@ +create or replace package body test_ut_suite_tag_filter is + + procedure test_conversion_to_rpn is + l_postfix ut3_develop.ut_varchar2_list; + l_postfix_string varchar2(4000); + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('A'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('A'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('A|B'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('AB|'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('(a|b)|c&d'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('ab|cd&|'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('!a|b'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('a!b|'); + end; + + procedure test_conversion_opr_by_opr is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('A','B'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_oprd_by_opd is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('|','|'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_lb_by_oper is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('(','A','|','B',')','('); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_rb_by_oprd is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list(')','A'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure conversion_throws_when_lb_rb is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('(',')'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure conv_from_tag_to_sql_filter is + l_sql_filter varchar2(4000); + begin + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1'); + ut.expect(l_sql_filter).to_equal(q'['test1' member of tags]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1|test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags or 'test2' member of tags)]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1|!test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags or not('test2' member of tags))]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1&!test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags and not('test2' member of tags))]'); + end; + + procedure filtering_is_correctly_applied_to_all_items is + l_input_data ut3_develop.ut_suite_cache_rows; + l_expected sys_refcursor; + l_actual sys_refcursor; + begin + l_input_data := ut3_develop.ut_suite_cache_rows( + ut3_develop.ut_suite_cache_row( + 1, 'UT_SUITE', 'some.path.test_tags', 'TEST_USER', 'TEST_TAGS', 'TEST_TAGS', 3, timestamp '2026-05-06 10:23:38.461299', NULL, NULL, 0, NULL, null, null, null, null, null, null, null, null, + ut3_develop.ut_varchar2_rows('slow'), null), + ut3_develop.ut_suite_cache_row( + 2, 'UT_SUITE_CONTEXT', 'some.path.test_tags.nested_context_#1', 'TEST_USER', 'TEST_TAGS', 'NESTED_CONTEXT_#1', 9, timestamp '2026-05-06 10:23:38.461299', 'tags_are_applied_in_contexts', NULL, 0, NULL, null, null, null, null, null, null, null, null, + ut3_develop.ut_varchar2_rows('context_tag'), null), + ut3_develop.ut_suite_cache_row( + 3, 'UT_TEST', 'some.path.test_tags.nested_context_#1.create_new_mapping', 'TEST_USER', 'TEST_TAGS', 'CREATE_NEW_MAPPING', 11, timestamp '2026-05-06 10:23:38.461299', NULL, NULL, 0, NULL, null, null, null, null, null, null, null, null, + ut3_develop.ut_varchar2_rows('test_tag'), ut3_develop.ut_executable_test('UT_EXECUTABLE_TEST', 'test', 'TEST_USER', 'test_tags', 'create_new_mapping', NULL, NULL, NULL, NULL, NULL)) + ); + + ut.expect(ut3_develop.ut_suite_tag_filter.apply( l_input_data, 'slow').count).to_equal(3 ); + + open l_expected for select * from table( l_input_data ); + open l_actual for select * from table( ut3_develop.ut_suite_tag_filter.apply( l_input_data, 'slow') ); + ut.expect(l_actual).to_equal(l_expected).join_by('ID'); + + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, '!slow').count ).to_equal(0); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, 'bad').count ).to_equal(0); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, '!bad').count ).to_equal(3); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, '!context_tag').count ).to_equal(0); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, 'context_tag').count ).to_equal(3); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, '!test_tag').count ).to_equal(0); + ut.expect( ut3_develop.ut_suite_tag_filter.apply(l_input_data, 'test_tag').count ).to_equal(3); + end; + +end test_ut_suite_tag_filter; +/ diff --git a/test/ut3_tester/core/test_ut_suite_tag_filter.pks b/test/ut3_tester/core/test_ut_suite_tag_filter.pks new file mode 100644 index 000000000..e7b255d4b --- /dev/null +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pks @@ -0,0 +1,40 @@ +create or replace package test_ut_suite_tag_filter is + + --%suite(ut_suite_tag_filter) + --%suitepath(utplsql.ut3_tester.core) + + --%context( Conversion to Reverse Polish Notation) + + --%test( Test conversion of expression into Reverse Polish Notation) + procedure test_conversion_to_rpn; + + --%test( Operator is followed by operator) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_opr_by_opr; + + --%test( Operand is followed by operand) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_oprd_by_opd; + + --%test( Left Bracket is followed by operator) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_lb_by_oper; + + --%test( Right Bracket is followed by operand) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_rb_by_oprd; + + --%test(Conversion raises exception when empty brackets) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure conversion_throws_when_lb_rb; + + --%endcontext + + --%test( Test conversion of expression from tag into custom where filter for SQL) + procedure conv_from_tag_to_sql_filter; + + --%test( Regression for issue #1324 - corect filtering by tags with suitepath and contexts) + procedure filtering_is_correctly_applied_to_all_items; + +end test_ut_suite_tag_filter; +/ diff --git a/test/ut3_tester/core/test_ut_utils.pkb b/test/ut3_tester/core/test_ut_utils.pkb index a3ed605f9..4dc10492b 100644 --- a/test/ut3_tester/core/test_ut_utils.pkb +++ b/test/ut3_tester/core/test_ut_utils.pkb @@ -159,28 +159,28 @@ end;]' using p_expected_list; procedure to_string_date is l_value date := to_date('2016-12-31 23:59:59', 'yyyy-mm-dd hh24:mi:ss'); - l_expected varchar2(100) := '2016-12-31T23:59:59'; + l_expected varchar2(100) := ' 2016-12-31T23:59:59'; begin ut.expect(ut3_develop.ut_data_value_date(l_value).to_string()).to_equal(l_expected); end; procedure to_string_timestamp is l_value timestamp(9) := to_timestamp('2016-12-31 23:59:59.123456789', 'yyyy-mm-dd hh24:mi:ss.ff'); - l_expected varchar2(100) := '2016-12-31T23:59:59'||gc_delimiter||'123456789'; + l_expected varchar2(100) := ' 2016-12-31T23:59:59'||gc_delimiter||'123456789'; begin ut.expect(ut3_develop.ut_data_value_timestamp(l_value).to_string()).to_equal(l_expected); end; procedure to_string_timestamp_ltz is l_value timestamp(9) with local time zone := to_timestamp('2016-12-31 23:59:59.123456789', 'yyyy-mm-dd hh24:mi:ss.ff'); - l_expected varchar2(100) := '2016-12-31T23:59:59'||gc_delimiter||'123456789'; + l_expected varchar2(100) := ' 2016-12-31T23:59:59'||gc_delimiter||'123456789'; begin ut.expect(ut3_develop.ut_data_value_timestamp_ltz(l_value).to_string()).to_equal(l_expected); end; procedure to_string_timestamp_tz is l_value timestamp(9) with time zone := to_timestamp_tz('2016-12-31 23:59:59.123456789 -8:00', 'yyyy-mm-dd hh24:mi:ss.ff tzh:tzm'); - l_expected varchar2(100) := '2016-12-31T23:59:59'||gc_delimiter||'123456789 -08:00'; + l_expected varchar2(100) := ' 2016-12-31T23:59:59'||gc_delimiter||'123456789 -08:00'; begin ut.expect(ut3_develop.ut_data_value_timestamp_tz(l_value).to_string()).to_equal(l_expected); end; @@ -375,8 +375,8 @@ end;'; procedure replace_multiline_comments is l_source clob; - l_actual clob; - l_expected clob; + l_actual dbms_preprocessor.source_lines_t; + l_expected dbms_preprocessor.source_lines_t; begin --Arrange l_source := q'[ @@ -390,11 +390,11 @@ create or replace package dummy as gv_text2 varchar2(200) := '/* multi-line comment in a multi-line string*/'; - -- ignored start of multi-line comment with multi-byte text � /* - -- ignored end of multi-line comment with multi-byte text � */ + -- ignored start of multi-line comment with multi-byte text ☺ /* + -- ignored end of multi-line comment with multi-byte text ☺ */ /* multi-line comment with - multi-byte characters ��� + multi-byte characters ☺☺☺ in it */ gv_text3 varchar2(200) := 'some text'; /* multiline comment*/ --followed by single-line comment /* multi-line comment in one line*/ @@ -403,7 +403,14 @@ create or replace package dummy as string*/}'; end; ]'; - l_expected := q'[ + + --Act + l_actual := ut3_develop.ut_utils.replace_multiline_comments( + ut3_tester_helper.main_helper.make_source( ut3_develop.ut_utils.clob_to_table(l_source) ) + ); + + --Assert + l_expected := ut3_tester_helper.main_helper.make_source( ut3_develop.ut_utils.clob_to_table( q'[ create or replace package dummy as -- single line comment with disabled /* multi-line comment */ @@ -414,8 +421,8 @@ create or replace package dummy as gv_text2 varchar2(200) := '/* multi-line comment in a multi-line string*/'; - -- ignored start of multi-line comment with multi-byte text � /* - -- ignored end of multi-line comment with multi-byte text � */ + -- ignored start of multi-line comment with multi-byte text ☺ /* + -- ignored end of multi-line comment with multi-byte text ☺ */ ]'||q'[ @@ -426,11 +433,84 @@ create or replace package dummy as in escaped q'multi-line string*/}'; end; -]'; +]' ) ); --Act - l_actual := ut3_develop.ut_utils.replace_multiline_comments(l_source); - --Assert - ut.expect(l_actual).to_equal(l_expected); + ut.expect(ut3_tester_helper.main_helper.lines_to_str(l_actual)).to_equal(ut3_tester_helper.main_helper.lines_to_str(l_expected)); + end; + + procedure int_conv_ds_sec is + l_expected varchar2(100) := '1 second'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' second); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ds_minute is + l_expected varchar2(100) := '1 minute'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' minute); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ds_hour is + l_expected varchar2(100) := '1 hour'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' hour); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ds_day is + l_expected varchar2(100) := '1 day'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' day); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ds_date is + l_expected varchar2(100) := '2 days 3 hours 4 minutes 11.333 seconds'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(INTERVAL '2 3:04:11.333' DAY TO SECOND); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ym_year is + l_expected varchar2(100) := '1 year'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' year); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ym_month is + l_expected varchar2(100) := '1 month'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(interval '1' month); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure int_conv_ym_date is + l_expected varchar2(100) := '1 year 2 months'; + l_actual varchar2(200) := ut3_develop.ut_utils.interval_to_text(INTERVAL '1-2' YEAR TO MONTH); + begin + ut.expect(l_expected).to_equal(l_actual); + end; + + procedure convert_collection_multibyte is + l_input ut3_develop.ut_varchar2_list; + l_max_len integer := ut3_develop.ut_utils.gc_max_storage_varchar2_len; + begin + --Arrange + l_input := ut3_develop.ut_varchar2_list( rpad( '❤', l_max_len, 'a' ) ); + ut.expect( lengthb( l_input( 1 ) ) ).to_be_greater_than(l_max_len); + + --Act + ut.expect( lengthb( ut3_develop.ut_utils.convert_collection(l_input)(1) ) ).to_be_less_or_equal(l_max_len); + end; + + procedure lengthb_gives_length_in_bytes is + l_clob clob; + begin + l_clob := '❤'; + ut.expect(ut3_develop.ut_utils.lengthb_clob(l_clob)).to_be_greater_than(1); end; end test_ut_utils; / diff --git a/test/ut3_tester/core/test_ut_utils.pks b/test/ut3_tester/core/test_ut_utils.pks index d8fca3b60..ca2e7b304 100644 --- a/test/ut3_tester/core/test_ut_utils.pks +++ b/test/ut3_tester/core/test_ut_utils.pks @@ -128,5 +128,39 @@ create or replace package test_ut_utils is --%test(replace_multiline_comments - replaces multi-line comments with empty lines) procedure replace_multiline_comments; + --%context(interval_converter_to_strin) + + --%test(returns text representation of interval day to second for 1 second interval) + procedure int_conv_ds_sec; + + --%test(returns text representation of interval day to second for 1 minute interval) + procedure int_conv_ds_minute; + + --%test(returns text representation of interval day to second for 1 hour interval) + procedure int_conv_ds_hour; + + --%test(returns text representation of interval day to second for 1 day interval) + procedure int_conv_ds_day; + + --%test(returns text representation of interval day to second for combination interval) + procedure int_conv_ds_date; + + --%test(returns text representation of interval year to month for 1 year interval) + procedure int_conv_ym_year; + + --%test(returns text representation of interval year to month for 1 month interval) + procedure int_conv_ym_month; + + --%test(returns text representation of interval year to month for custom interval) + procedure int_conv_ym_date; + + --%endcontext + + --%test(convert_collection does not fail on multibyte strings - Issue #1245 ) + procedure convert_collection_multibyte; + + --%test(lengthb returns length of a CLOB in bytes ) + procedure lengthb_gives_length_in_bytes; + end test_ut_utils; / diff --git a/test/ut3_tester_helper/coverage_helper.pkb b/test/ut3_tester_helper/coverage_helper.pkb index 73dec720b..ef14c586f 100644 --- a/test/ut3_tester_helper/coverage_helper.pkb +++ b/test/ut3_tester_helper/coverage_helper.pkb @@ -48,6 +48,20 @@ create or replace package body coverage_helper is end; end;]'; + execute immediate q'[create or replace package ut3_develop.some_other_package is + procedure do_stuff(i_input in number); + end;]'; + + execute immediate q'[create or replace package body ut3_develop.some_other_package is + procedure do_stuff(i_input in number) is + begin + if i_input = 2 then dbms_output.put_line('should not get here'); elsif i_input = 1 then dbms_output.put_line('should get here'); + else + dbms_output.put_line('should not get here'); + end if; + end; + end;]'; + execute immediate q'[create or replace package ut3_develop.test_dummy_coverage is --%suite(dummy coverage test) --%suitepath(coverage_testing) @@ -77,6 +91,7 @@ create or replace package body coverage_helper is pragma autonomous_transaction; begin begin execute immediate q'[drop package ut3_develop.test_dummy_coverage]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_develop.some_other_package]'; exception when others then null; end; begin execute immediate q'[drop package ut3_develop.]'||covered_package_name; exception when others then null; end; end; @@ -123,6 +138,151 @@ create or replace package body coverage_helper is begin execute immediate q'[drop package ut3_develop.test_dummy_coverage_1]'; exception when others then null; end; end; + procedure create_cov_with_dbms_stats is + pragma autonomous_transaction; + begin + execute immediate q'[create table ut3_develop.table_to_test_stats as select * from user_objects]'; + + execute immediate q'[create or replace package ut3_develop.stats is + procedure gather; + end;]'; + + execute immediate q'[create or replace package body ut3_develop.stats is + procedure gather is + begin + dbms_Stats.gather_table_stats('UT3_DEVELOP','TABLE_TO_TEST_STATS'); + end; + end;]'; + + execute immediate q'[create or replace package ut3_develop.test_stats is + --%suite(stats gathering coverage test) + --%suitepath(coverage_testing) + + --%test + procedure test_stats_gather; + + end;]'; + + execute immediate q'[create or replace package body ut3_develop.test_stats is + procedure test_stats_gather is + begin + stats.gather; + ut.expect(1).to_equal(1); + end; + end;]'; + + end; + + procedure create_regex_dummy_for_schema(p_schema in varchar2) is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package ]'||p_schema||q'[.regex_dummy_cov is + procedure do_stuff(i_input in number); + end;]'; + + execute immediate q'[create or replace package body ]'||p_schema||q'[.regex_dummy_cov is + procedure do_stuff(i_input in number) is + begin + if i_input = 2 then dbms_output.put_line('should not get here'); elsif i_input = 1 then dbms_output.put_line('should get here'); + else + dbms_output.put_line('should not get here'); + end if; + end; + end;]'; + + execute immediate q'[create or replace package ]'||p_schema||q'[.test_regex_dummy_cov is + --%suite(dummy coverage test) + --%suitepath(coverage_testing) + + --%test + procedure test_do_stuff; + + --%test + procedure zero_coverage; + end;]'; + + execute immediate q'[create or replace package body ]'||p_schema||q'[.test_regex_dummy_cov is + procedure test_do_stuff is + begin + regex_dummy_cov.do_stuff(1); + ut.expect(1).to_equal(1); + end; + procedure zero_coverage is + begin + null; + end; + end;]'; + end; + + procedure create_regex_dummy_obj is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package ut3_develop.regex123_dummy_cov is + procedure do_stuff(i_input in number); + end;]'; + + execute immediate q'[create or replace package body ut3_develop.regex123_dummy_cov is + procedure do_stuff(i_input in number) is + begin + if i_input = 2 then dbms_output.put_line('should not get here'); elsif i_input = 1 then dbms_output.put_line('should get here'); + else + dbms_output.put_line('should not get here'); + end if; + end; + end;]'; + + execute immediate q'[create or replace package ut3_develop.test_regex123_dummy_cov is + --%suite(dummy coverage test) + --%suitepath(coverage_testing) + + --%test + procedure test_do_stuff; + + --%test + procedure zero_coverage; + end;]'; + + execute immediate q'[create or replace package body ut3_develop.test_regex123_dummy_cov is + procedure test_do_stuff is + begin + regex123_dummy_cov.do_stuff(1); + ut.expect(1).to_equal(1); + end; + procedure zero_coverage is + begin + null; + end; + end;]'; + end; + + procedure create_regex_dummy_cov is + begin + create_regex_dummy_for_schema('ut3_develop'); + create_regex_dummy_for_schema('ut3_tester_helper'); + create_regex_dummy_obj; + end; + + procedure drop_regex_dummy_cov is + pragma autonomous_transaction; + begin + begin execute immediate q'[drop package ut3_develop.regex_dummy_cov]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_develop.test_regex_dummy_cov]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_tester_helper.regex_dummy_cov]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_tester_helper.test_regex_dummy_cov]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_develop.regex123_dummy_cov]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_develop.test_regex123_dummy_cov]'; exception when others then null; end; + end; + + + procedure drop_cov_with_dbms_stats is + pragma autonomous_transaction; + begin + begin execute immediate q'[drop package ut3_develop.test_stats]'; exception when others then null; end; + begin execute immediate q'[drop package ut3_develop.stats]'; exception when others then null; end; + begin execute immediate q'[drop table ut3_develop.table_to_test_stats]'; exception when others then null; end; + end; + + procedure run_standalone_coverage(a_coverage_run_id raw, a_input integer) is begin ut3_develop.ut_runner.coverage_start(a_coverage_run_id); @@ -130,11 +290,11 @@ create or replace package body coverage_helper is ut3_develop.ut_runner.coverage_stop(); end; - function get_job_status(a_job_name varchar2, a_job_started_after timestamp with time zone) return varchar2 is - l_status varchar2(1000); + function get_job_status(a_job_name varchar2, a_job_started_after timestamp with time zone) return user_scheduler_job_run_details%rowtype is + l_result user_scheduler_job_run_details%rowtype; begin begin - select status into l_status + select * into l_result from user_scheduler_job_run_details where job_name = upper(a_job_name) and req_start_date >= a_job_started_after; @@ -142,20 +302,11 @@ create or replace package body coverage_helper is when no_data_found then null; end; - return l_status; - end; - - procedure sleep(a_time number) is - begin - $if dbms_db_version.version >= 18 $then - dbms_session.sleep(a_time); - $else - dbms_lock.sleep(a_time ); - $end + return l_result; end; procedure run_job_and_wait_for_finish(a_job_action varchar2) is - l_status varchar2(1000); + l_job_run_info user_scheduler_job_run_details%rowtype; l_job_name varchar2(30); l_timestamp timestamp with time zone := current_timestamp; i integer := 0; @@ -163,7 +314,7 @@ create or replace package body coverage_helper is begin g_job_no := g_job_no + 1; l_job_name := 'utPLSQL_selftest_job_'||g_job_no; - sleep(0.01); + dbms_lock.sleep(0.15); dbms_scheduler.create_job( job_name => l_job_name, job_type => 'PLSQL_BLOCK', @@ -173,14 +324,14 @@ create or replace package body coverage_helper is auto_drop => TRUE, comments => 'one-time-job' ); - while (l_status is null or l_status not in ('SUCCEEDED','FAILED')) and i < 30 loop - l_status := get_job_status( l_job_name, l_timestamp ); - sleep(0.1); + while (l_job_run_info.status is null or l_job_run_info.status not in ('SUCCEEDED','FAILED')) and i < 6000 loop + l_job_run_info := get_job_status( l_job_name, l_timestamp ); + dbms_lock.sleep(0.1); i := i + 1; end loop; commit; - if l_status = 'FAILED' then - raise_application_error(-20000, 'Running a scheduler job failed'); + if nvl(l_job_run_info.status,'null') <> 'SUCCEEDED' then + raise_application_error(-20000, 'Scheduler job '''||l_job_name||''', status='''||l_job_run_info.status||'''. Additional info: '||l_job_run_info.additional_info); end if; end; @@ -196,7 +347,7 @@ create or replace package body coverage_helper is e_exists exception; pragma exception_init ( e_exists, -955 ); begin - execute immediate 'create table test_results (text varchar2(4000))'; + execute immediate 'create table test_results (id integer, text varchar2(4000))'; exception when e_exists then null; @@ -218,14 +369,15 @@ create or replace package body coverage_helper is pragma autonomous_transaction; begin run_job_and_wait_for_finish( a_plsql_block ); - + dbms_lock.sleep(0.1); execute immediate q'[ declare l_results ut3_develop.ut_varchar2_list; begin - select * + select text bulk collect into l_results - from test_results; + from test_results + order by id; delete from test_results; commit; :clob_results := ut3_tester_helper.main_helper.table_to_clob(l_results); @@ -236,14 +388,136 @@ create or replace package body coverage_helper is return l_result_clob; end; + procedure copy_coverage_data_to_ut3(a_coverage_run_id raw) is + pragma autonomous_transaction; + l_current_coverage_run_id raw(32) := hextoraw(sys_context('UT3_INFO','COVERAGE_RUN_ID')); + begin + insert into ut3.ut_coverage_runs(coverage_run_id, line_coverage_id, block_coverage_id) + select l_current_coverage_run_id, -line_coverage_id, -block_coverage_id + from ut3_develop.ut_coverage_runs + where coverage_run_id = a_coverage_run_id; + + insert into ut3.plsql_profiler_runs(runid, related_run, run_owner, run_date, run_comment, run_total_time, run_system_info, run_comment1, spare1) + select -runid, related_run, run_owner, run_date, run_comment, run_total_time, run_system_info, run_comment1, spare1 + from ut3_develop.plsql_profiler_runs c + join ut3_develop.ut_coverage_runs r + on r.line_coverage_id = c.runid + where r.coverage_run_id = a_coverage_run_id; + + insert into ut3.plsql_profiler_units(runid, unit_number, unit_type, unit_owner, unit_name, unit_timestamp, total_time, spare1, spare2) + select -runid, unit_number, unit_type, unit_owner, unit_name, unit_timestamp, total_time, spare1, spare2 + from ut3_develop.plsql_profiler_units c + join ut3_develop.ut_coverage_runs r + on r.line_coverage_id = c.runid + where r.coverage_run_id = a_coverage_run_id; + + insert into ut3.plsql_profiler_data(runid, unit_number, line#, total_occur, total_time, min_time, max_time, spare1, spare2, spare3, spare4) + select -runid, unit_number, line#, total_occur, total_time, min_time, max_time, spare1, spare2, spare3, spare4 + from ut3_develop.plsql_profiler_data c + join ut3_develop.ut_coverage_runs r + on r.line_coverage_id = c.runid + where r.coverage_run_id = a_coverage_run_id; + + insert into ut3.dbmspcc_runs(run_id, run_comment, run_owner, run_timestamp) + select -run_id, run_comment, run_owner, run_timestamp + from ut3_develop.dbmspcc_runs c + join ut3_develop.ut_coverage_runs r + on r.block_coverage_id = c.run_id + where r.coverage_run_id = a_coverage_run_id; + + insert into ut3.dbmspcc_units(run_id, object_id, owner, name, type, last_ddl_time) + select -run_id, object_id, owner, name, type, last_ddl_time + from ut3_develop.dbmspcc_units c + join ut3_develop.ut_coverage_runs r + on r.block_coverage_id = c.run_id + where r.coverage_run_id = a_coverage_run_id; + + insert into ut3.dbmspcc_blocks(run_id, object_id, block, line, col, covered, not_feasible) + select -run_id, object_id, block, line, col, covered, not_feasible + from ut3_develop.dbmspcc_blocks c + join ut3_develop.ut_coverage_runs r + on r.block_coverage_id = c.run_id + where r.coverage_run_id = a_coverage_run_id; + + commit; + end; + + function gather_coverage_on_coverage( a_cov_options varchar2) return clob is + pragma autonomous_transaction; + l_plsql_block varchar2(32767); + l_result_clob clob; + l_coverage_id raw(32) := sys_guid(); + begin + l_plsql_block := q'[ + declare + l_coverage_options ut3_develop.ut_coverage_options; + l_coverage_run_id raw(32) := ']'||rawtohex(l_coverage_id)||q'['; + l_result ut3_develop.ut_coverage.t_coverage; + begin + ut3_develop.ut_runner.coverage_start(l_coverage_run_id); + ut3_develop.ut_coverage.set_develop_mode(a_develop_mode => true); + l_coverage_options := {a_cov_options}; + l_result := ut3_develop.ut_coverage.get_coverage_data(l_coverage_options); + ut3_develop.ut_coverage.set_develop_mode(a_develop_mode => false); + ut3_develop.ut_runner.coverage_stop(); + insert into test_results select rownum, owner||'.'||name from ut3_develop.ut_coverage_sources_tmp; + commit; + end;]'; + l_plsql_block := replace(l_plsql_block,'{a_cov_options}',a_cov_options); + run_job_and_wait_for_finish( l_plsql_block ); + execute immediate q'[ + declare + l_results ut3_develop.ut_varchar2_list; + begin + select text + bulk collect into l_results + from test_results + order by id; + delete from test_results; + commit; + :clob_results := ut3_tester_helper.main_helper.table_to_clob(l_results); + end; + ]' + using out l_result_clob; + copy_coverage_data_to_ut3(l_coverage_id); + return l_result_clob; + end; + function run_tests_as_job( a_run_command varchar2 ) return clob is l_plsql_block varchar2(32767); l_result_clob clob; - pragma autonomous_transaction; + l_coverage_id raw(32) := sys_guid(); begin - l_plsql_block := 'begin insert into test_results select * from table( {a_run_command} ); commit; end;'; + l_plsql_block := q'[ + declare + x dbms_output.chararr; + i integer := 100000; + begin +/* + execute immediate 'alter session set statistics_level=all'; + dbms_hprof.start_profiling( + location => 'PLSHPROF_DIR' + , filename => 'profiler_utPLSQL_run_]'||rawtohex(l_coverage_id)||q'[.txt' + ); +*/ + ut3_develop.ut_runner.coverage_start(']'||rawtohex(l_coverage_id)||q'['); + ut3_develop.ut_coverage.set_develop_mode(a_develop_mode => true); + --gather coverage on the command executed + begin {a_run_command}; end; + dbms_output.get_lines(x,i); + ut3_develop.ut_coverage.set_develop_mode(a_develop_mode => false); + ut3_develop.ut_runner.coverage_stop(); + --get the actual results of the command gathering the coverage + insert into test_results select rownum as id, x.* from table( {a_run_command} ) x; + commit; +/* + dbms_hprof.stop_profiling; +*/ + end;]'; l_plsql_block := replace(l_plsql_block,'{a_run_command}',a_run_command); - return run_code_as_job( l_plsql_block ); + l_result_clob := run_code_as_job( l_plsql_block ); + copy_coverage_data_to_ut3(l_coverage_id); + return l_result_clob; end; procedure create_dup_object_name is diff --git a/test/ut3_tester_helper/coverage_helper.pks b/test/ut3_tester_helper/coverage_helper.pks index 9a3bd1db6..8d2c1c150 100644 --- a/test/ut3_tester_helper/coverage_helper.pks +++ b/test/ut3_tester_helper/coverage_helper.pks @@ -14,9 +14,16 @@ create or replace package coverage_helper is procedure create_dummy_coverage_1; procedure drop_dummy_coverage_1; + procedure create_regex_dummy_cov; + procedure drop_regex_dummy_cov; + + procedure create_cov_with_dbms_stats; + procedure drop_cov_with_dbms_stats; + procedure run_standalone_coverage(a_coverage_run_id raw, a_input integer); procedure run_coverage_job(a_coverage_run_id raw, a_input integer); + function gather_coverage_on_coverage( a_cov_options varchar2) return clob; function run_tests_as_job( a_run_command varchar2 ) return clob; function run_code_as_job( a_plsql_block varchar2 ) return clob; procedure create_test_results_table; diff --git a/test/ut3_tester_helper/main_helper.pkb b/test/ut3_tester_helper/main_helper.pkb index 76245ea12..f60ce796c 100644 --- a/test/ut3_tester_helper/main_helper.pkb +++ b/test/ut3_tester_helper/main_helper.pkb @@ -104,26 +104,26 @@ create or replace package body main_helper is commit; end; - procedure create_parse_proc_as_ut3$user# is + procedure create_parse_proc_as_ut3_user is pragma autonomous_transaction; begin execute immediate q'[ - create or replace procedure ut3$user#.parse_annotations is + create or replace procedure ut3_user.parse_annotations is begin ut3_develop.ut_runner.rebuild_annotation_cache('UT3_TESTER','PACKAGE'); end;]'; end; - procedure drop_parse_proc_as_ut3$user# is + procedure drop_parse_proc_as_ut3_user is pragma autonomous_transaction; begin - execute immediate 'drop procedure ut3$user#.parse_annotations'; + execute immediate 'drop procedure ut3_user.parse_annotations'; end; - procedure parse_dummy_test_as_ut3$user# is + procedure parse_dummy_test_as_ut3_user is pragma autonomous_transaction; begin - execute immediate 'begin ut3$user#.parse_annotations; end;'; + execute immediate 'begin ut3_user.parse_annotations; end;'; end; procedure append_to_list(a_list in out nocopy ut3_develop.ut_varchar2_list, a_item varchar2) is @@ -155,6 +155,24 @@ create or replace package body main_helper is begin ut3_develop.ut_session_context.clear_all_context; end; + + function lines_to_str(a_lines dbms_preprocessor.source_lines_t) return varchar2 is + l_result varchar2(32767); + begin + for i in 1 .. a_lines.count loop + l_result := l_result || a_lines(i); + end loop; + return l_result; + end; + + function make_source(a_lines ut3_develop.ut_varchar2_list) return dbms_preprocessor.source_lines_t is + l_result dbms_preprocessor.source_lines_t; + begin + for i in 1 .. a_lines.count loop + l_result(i) := a_lines(i); + end loop; + return l_result; + end; end; / diff --git a/test/ut3_tester_helper/main_helper.pks b/test/ut3_tester_helper/main_helper.pks index 6dd453446..71575e145 100644 --- a/test/ut3_tester_helper/main_helper.pks +++ b/test/ut3_tester_helper/main_helper.pks @@ -29,11 +29,11 @@ create or replace package main_helper is procedure cleanup_annotation_cache; - procedure create_parse_proc_as_ut3$user#; + procedure create_parse_proc_as_ut3_user; - procedure drop_parse_proc_as_ut3$user#; + procedure drop_parse_proc_as_ut3_user; - procedure parse_dummy_test_as_ut3$user#; + procedure parse_dummy_test_as_ut3_user; procedure append_to_list(a_list in out nocopy ut3_develop.ut_varchar2_list, a_item varchar2); @@ -47,5 +47,9 @@ create or replace package main_helper is procedure clear_ut_run_context; + function lines_to_str(a_lines dbms_preprocessor.source_lines_t) return varchar2; + + function make_source(a_lines ut3_develop.ut_varchar2_list) return dbms_preprocessor.source_lines_t; + end; / diff --git a/test/ut3_tester_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index c2a50170e..c56f31dc0 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -3,7 +3,7 @@ create or replace package body run_helper is procedure setup_cache_objects is pragma autonomous_transaction; begin - execute immediate q'[create or replace package ut3$user#.dummy_test_package as + execute immediate q'[create or replace package ut3_user.dummy_test_package as --%suite(dummy_test_suite) --%suitepath(some.path) --%rollback(manual) @@ -12,7 +12,7 @@ create or replace package body run_helper is --%beforetest(some_procedure) procedure some_dummy_test_procedure; end;]'; - execute immediate q'[create or replace procedure ut3$user#.dummy_test_procedure as + execute immediate q'[create or replace procedure ut3_user.dummy_test_procedure as --%some_annotation(some_text) --%rollback(manual) begin @@ -27,7 +27,7 @@ create or replace package body run_helper is execute immediate q'[grant execute on ut3_tester_helper.dummy_test_procedure to public]'; - execute immediate q'[create or replace package ut3$user#.bad_test_package as + execute immediate q'[create or replace package ut3_user.bad_test_package as --%rollback(manual) --%test(dummy_test) procedure some_dummy_test_procedure; @@ -37,7 +37,7 @@ create or replace package body run_helper is procedure setup_cache_objectstag is pragma autonomous_transaction; begin - execute immediate q'[create or replace package ut3$user#.dummy_test_package as + execute immediate q'[create or replace package ut3_user.dummy_test_package as --%suite(dummy_test_suite) --%suitepath(some.path) --%tags(dummy) @@ -48,7 +48,7 @@ create or replace package body run_helper is --%beforetest(some_procedure) procedure some_dummy_test_procedure; end;]'; - execute immediate q'[create or replace procedure ut3$user#.dummy_test_procedure as + execute immediate q'[create or replace procedure ut3_user.dummy_test_procedure as --%some_annotation(some_text) --%rollback(manual) begin @@ -67,7 +67,7 @@ create or replace package body run_helper is procedure setup_cache_twotags is pragma autonomous_transaction; begin - execute immediate q'[create or replace package ut3$user#.dummy_test_package as + execute immediate q'[create or replace package ut3_user.dummy_test_package as --%suite(dummy_test_suite) --%tags(suitetag1,suitetag2) --%rollback(manual) @@ -77,7 +77,7 @@ create or replace package body run_helper is --%beforetest(some_procedure) procedure some_dummy_test_procedure; end;]'; - execute immediate q'[create or replace procedure ut3$user#.dummy_test_procedure as + execute immediate q'[create or replace procedure ut3_user.dummy_test_procedure as --%some_annotation(some_text) --%rollback(manual) begin @@ -141,8 +141,8 @@ create or replace package body run_helper is pragma autonomous_transaction; begin setup_cache_objects(); - ut3_develop.ut_annotation_manager.rebuild_annotation_cache('UT3$USER#','PACKAGE'); - ut3_develop.ut_annotation_manager.rebuild_annotation_cache('UT3$USER#','PROCEDURE'); + ut3_develop.ut_annotation_manager.rebuild_annotation_cache('UT3_USER','PACKAGE'); + ut3_develop.ut_annotation_manager.rebuild_annotation_cache('UT3_USER','PROCEDURE'); ut3_develop.ut_annotation_manager.rebuild_annotation_cache('UT3_TESTER_HELPER','PROCEDURE'); end; @@ -150,10 +150,10 @@ create or replace package body run_helper is pragma autonomous_transaction; begin delete from ut3_develop.ut_annotation_cache_info - where object_type = 'PROCEDURE' and object_owner in ('UT3$USER#','UT3_TESTER_HELPER') + where object_type = 'PROCEDURE' and object_owner in ('UT3_USER','UT3_TESTER_HELPER') or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; - execute immediate q'[drop package ut3$user#.dummy_test_package]'; - execute immediate q'[drop procedure ut3$user#.dummy_test_procedure]'; + execute immediate q'[drop package ut3_user.dummy_test_package]'; + execute immediate q'[drop procedure ut3_user.dummy_test_procedure]'; execute immediate q'[drop procedure ut3_tester_helper.dummy_test_procedure]'; end; @@ -185,7 +185,7 @@ create or replace package body run_helper is begin create_db_link; execute immediate q'[ - create or replace package ut3$user#.test_db_link is + create or replace package ut3_user.test_db_link is --%suite --%test @@ -193,7 +193,7 @@ create or replace package body run_helper is end;]'; execute immediate q'[ - create or replace package body ut3$user#.test_db_link is + create or replace package body ut3_user.test_db_link is procedure runs_with_db_link is a_value integer; begin @@ -208,10 +208,10 @@ create or replace package body run_helper is procedure db_link_cleanup is begin drop_db_link; - begin execute immediate 'drop package ut3$user#.test_db_link'; exception when others then null; end; + begin execute immediate 'drop package ut3_user.test_db_link'; exception when others then null; end; end; - procedure create_suite_with_link is + procedure create_suite_with_link is pragma autonomous_transaction; begin create_db_link; @@ -250,7 +250,7 @@ create or replace package body run_helper is execute immediate 'grant execute on test_distributed_savepoint to public'; end; - procedure drop_suite_with_link is + procedure drop_suite_with_link is pragma autonomous_transaction; begin drop_db_link; @@ -258,7 +258,7 @@ create or replace package body run_helper is execute immediate 'drop package test_distributed_savepoint'; end; - procedure create_ut3$user#_tests is + procedure create_ut3_user_tests is pragma autonomous_transaction; begin execute immediate q'[create or replace package test_package_1 is @@ -343,17 +343,267 @@ create or replace package body run_helper is end; end test_package_3; ]'; + + execute immediate q'[create or replace package test_tag_pkg_1 is + --%suite + --%tags(suite1,release_3_1_13,development,complex,end_to_end) + --%suitepath(suite1) + --%rollback(manual) + + --%test(Test1 from test_tag_pkg_1) + --%tags(test1,development,fast) + procedure test1; + + --%test(Test2 from test_tag_pkg_1) + --%tags(test2,production,slow,patch_3_1_13) + procedure test2; + + end test_tag_pkg_1; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_1 is + procedure test1 is + begin + dbms_output.put_line('test_tag_pkg_1.test1 executed'); + end; + procedure test2 is + begin + dbms_output.put_line('test_tag_pkg_1.test2 executed'); + end; + end test_tag_pkg_1; + ]'; + + execute immediate q'[create or replace package test_tag_pkg_2 is + --%suite + --%tags(suite2,release_3_1_12,development,simple) + --%suitepath(suite1.suite2) + --%rollback(manual) + + --%test(Test3 from test_tag_pkg_2) + --%tags(test3,development,fast) + procedure test3; + + --%test(Test4 from test_tag_pkg_1) + --%tags(test4,production,slow) + procedure test4; + + end test_tag_pkg_2; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_2 is + procedure test3 is + begin + dbms_output.put_line('test_tag_pkg_2.test3 executed'); + end; + procedure test4 is + begin + dbms_output.put_line('test_tag_pkg_2.test4 executed'); + end; + end test_tag_pkg_2; + ]'; + + execute immediate q'[create or replace package test_tag_pkg_3 is + --%suite + --%tags(suite3,release_3_1_13,production,simple,end_to_end) + --%suitepath(suite3) + --%rollback(manual) + + --%test(Test5 from test_tag_pkg_3) + --%tags(test5,release_3_1_13,production,patch_3_1_13) + procedure test5; + + --%test(Test6 from test_tag_pkg_3) + --%tags(test6,development,patch_3_1_14) + procedure test6; + + end test_tag_pkg_3; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_3 is + procedure test5 is + begin + dbms_output.put_line('test_tag_pkg_3.test5 executed'); + end; + procedure test6 is + begin + dbms_output.put_line('test_tag_pkg_3.test6 executed'); + end; + end test_tag_pkg_3; + ]'; + + execute immediate q'[create or replace package suite1_level1_pkg is + + --%suite(suite1_level1) + --%suitepath(any_none) + --%rollback(manual) + + --%test(Test 1 from Suite1 on level 1) + --%tags(suite1,level1,test1,test1_level1) + procedure test1_level1; + + --%test(Test 2 from Suite1 on level 1) + procedure test2_level1; + + end suite1_level1_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_level1_pkg is + procedure test1_level1 is + begin + dbms_output.put_line('suite1_level1_pkg.test1_level1 executed'); + end; + procedure test2_level1 is + begin + dbms_output.put_line('suite1_level1_pkg.test2_level1 executed'); + end; + end suite1_level1_pkg; + ]'; + + execute immediate q'[create or replace package suite1_1_level2_pkg is + + --%suite(suite1_1_level2) + --%suitepath(any_none.suite1_level1) + --%rollback(manual) + + --%test(Test 1 from Suite1_2 on level 2) + --%tags(level2,test1,test1_level2) + procedure suite1_1_test1_level2; + + --%test(Test 2 from Suite1_2 on level 2) + procedure suite1_1_test2_level2; + + end suite1_1_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_1_level2_pkg is + procedure suite1_1_test1_level2 is + begin + dbms_output.put_line('suite1_1_level2_pkg.suite1_1_test1_level2 executed'); + end; + procedure suite1_1_test2_level2 is + begin + dbms_output.put_line('suite1_1_level2_pkg.suite1_1_test2_level2 executed'); + end; + end suite1_1_level2_pkg; + ]'; + + execute immediate q'[create or replace package suite1_2_level2_pkg is + + --%suite(suite1_2_level2) + --%tags(level2,suite1_2,suites) + --%suitepath(any_none.suite1_level1) + --%rollback(manual) + + --%test(Test 1 from Suite1_2 on level 2) + procedure suite1_2_test1_level2; + + --%test(Test 2 from Suite1_2 on level 2) + --%tags(level2,test2,test2_level2) + procedure suite1_2_test2_level1; + + end suite1_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_2_level2_pkg is + procedure suite1_2_test1_level2 is + begin + dbms_output.put_line('suite1_2_level2_pkg.suite1_2_test1_level2 executed'); + end; + procedure suite1_2_test2_level1 is + begin + dbms_output.put_line('suite1_2_level2_pkg.suite1_2_test2_level1 executed'); + end; + end suite1_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package suite2_level1_pkg is + + --%suite(suite2_level1) + --%tags(level1,suite2,suites) + --%suitepath(any_none) + --%rollback(manual) + + --%test(Test 1 from Suite1 on level 1) + --%tags(suite2,level1,test1,test1_level1) + procedure test1_level1; + + --%test(Test 2 from Suite1 on level 1) + procedure test2_level1; + + end suite2_level1_pkg; + ]'; + + execute immediate q'[create or replace package body suite2_level1_pkg is + procedure test1_level1 is + begin + dbms_output.put_line('suite2_level1_pkg.test1_level1 executed'); + end; + procedure test2_level1 is + begin + dbms_output.put_line('suite2_level1_pkg.test2_level1 executed'); + end; + end suite2_level1_pkg; + ]'; + + execute immediate q'[create or replace package suite2_2_level2_pkg is + + --%suite(suite2_2_level2) + --%tags(level2,suite2_2,suites) + --%suitepath(any_none.suite2_level1) + --%rollback(manual) + + --%test(Test 1 from Suite2_2 on level 2) + procedure suite2_2_test1_level2; + + --%test(Test 2 from Suite2_2 on level 2) + --%tags(level2,test2,test2_level2) + procedure suite2_2_test2_level2; + + end suite2_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite2_2_level2_pkg is + procedure suite2_2_test1_level2 is + begin + dbms_output.put_line('suite2_2_level2_pkg.suite2_2_test1_level2 executed'); + end; + procedure suite2_2_test2_level2 is + begin + dbms_output.put_line('suite2_2_level2_pkg.suite2_2_test2_level2 executed'); + end; + end suite2_2_level2_pkg; + ]'; + + execute immediate q'[grant execute on test_package_1 to public]'; execute immediate q'[grant execute on test_package_2 to public]'; execute immediate q'[grant execute on test_package_3 to public]'; + execute immediate q'[grant execute on test_tag_pkg_1 to public]'; + execute immediate q'[grant execute on test_tag_pkg_2 to public]'; + execute immediate q'[grant execute on test_tag_pkg_3 to public]'; + + execute immediate q'[grant execute on suite1_level1_pkg to public]'; + execute immediate q'[grant execute on suite1_1_level2_pkg to public]'; + execute immediate q'[grant execute on suite1_2_level2_pkg to public]'; + execute immediate q'[grant execute on suite2_level1_pkg to public]'; + execute immediate q'[grant execute on suite2_2_level2_pkg to public]'; end; - procedure drop_ut3$user#_tests is + procedure drop_ut3_user_tests is pragma autonomous_transaction; begin execute immediate q'[drop package test_package_1]'; execute immediate q'[drop package test_package_2]'; execute immediate q'[drop package test_package_3]'; + execute immediate q'[drop package test_tag_pkg_1]'; + execute immediate q'[drop package test_tag_pkg_2]'; + execute immediate q'[drop package test_tag_pkg_3]'; + + execute immediate q'[drop package suite2_2_level2_pkg]'; + execute immediate q'[drop package suite2_level1_pkg]'; + execute immediate q'[drop package suite1_2_level2_pkg]'; + execute immediate q'[drop package suite1_level1_pkg]'; + execute immediate q'[drop package suite1_1_level2_pkg]'; end; procedure create_test_suite is @@ -605,18 +855,29 @@ create or replace package body run_helper is --%test procedure some_test; + end;]'; + execute immediate q'[ + create or replace package ut3_develop.package_that_is_not_a_test + as + --%parameter(a comment inside) + --%parameters( description ) + + --%test + procedure some_test; + end;]'; end; procedure drop_ut3_suite is pragma autonomous_transaction; begin + execute immediate q'[drop package ut3_develop.package_that_is_not_a_test]'; execute immediate q'[drop package ut3_develop.some_test_package]'; end; function get_schema_ut_packages(a_owner in varchar2) return ut3_develop.ut_object_names is begin - return ut3_develop.ut_suite_manager.get_schema_ut_packages(ut3_develop.ut_varchar2_rows(a_owner)); + return ut3_develop.ut_suite_manager.get_schema_ut_packages(ut3_develop.ut_varchar2_rows(a_owner), null); end; function ut_output_buffer_tmp return t_out_buff_tab pipelined is @@ -665,5 +926,39 @@ create or replace package body run_helper is return l_result; end; + procedure slow_down_insert_into_table_buffer is + pragma autonomous_transaction; + begin + execute immediate q'[ + create or replace trigger ut3_develop.slow_down_buffer_insert + before insert on ut3_develop.ut_output_buffer_info_tmp + begin + dbms_session.sleep(1); + end;]'; + end; + + procedure cleanup_slow_down_insert_into_table_buffer is + pragma autonomous_transaction; + begin + execute immediate 'drop trigger ut3_develop.slow_down_buffer_insert'; + exception when others then + if sqlcode <> -4080 then raise; end if; + end; + + procedure run_as_job(a_job_action varchar2) is + l_job_name varchar2(50); + l_timestamp timestamp with time zone := current_timestamp; + pragma autonomous_transaction; + begin + l_job_name := 'utPLSQL__'||to_char(l_timestamp,'yyyymmddhh24missff'); + dbms_scheduler.create_job( + job_name => l_job_name, + job_type => 'PLSQL_BLOCK', + job_action => a_job_action, + start_date => l_timestamp, + enabled => TRUE, + auto_drop => TRUE + ); + end; end; / diff --git a/test/ut3_tester_helper/run_helper.pks b/test/ut3_tester_helper/run_helper.pks index 0657c1a51..6c5dd4a3b 100644 --- a/test/ut3_tester_helper/run_helper.pks +++ b/test/ut3_tester_helper/run_helper.pks @@ -17,8 +17,8 @@ create or replace package run_helper is procedure create_suite_with_link; procedure drop_suite_with_link; - procedure create_ut3$user#_tests; - procedure drop_ut3$user#_tests; + procedure create_ut3_user_tests; + procedure drop_ut3_user_tests; procedure create_test_suite; procedure drop_test_suite; @@ -74,5 +74,10 @@ create or replace package run_helper is a_name varchar2 := null ) return sys_refcursor; + procedure slow_down_insert_into_table_buffer; + + procedure cleanup_slow_down_insert_into_table_buffer; + + procedure run_as_job(a_job_action varchar2); end; / diff --git a/test/ut3_tester_helper/test_dummy_dble_nest_lst_obj.tps b/test/ut3_tester_helper/test_dummy_dble_nest_lst_obj.tps new file mode 100644 index 000000000..5134fef21 --- /dev/null +++ b/test/ut3_tester_helper/test_dummy_dble_nest_lst_obj.tps @@ -0,0 +1,17 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_DBLE_NEST_LST_OBJ'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_dble_nest_lst_obj force'; + end if; +end; +/ + +CREATE TYPE test_dummy_dble_nest_lst_obj AS OBJECT +( + some_number_id NUMBER, + some_name VARCHAR2 (25), + dummy_list test_dummy_double_nested_list +); +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/test_dummy_double_nested_list.tps b/test/ut3_tester_helper/test_dummy_double_nested_list.tps new file mode 100644 index 000000000..625462824 --- /dev/null +++ b/test/ut3_tester_helper/test_dummy_double_nested_list.tps @@ -0,0 +1,13 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_DOUBLE_NESTED_LIST'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_double_nested_list force'; + end if; +end; +/ + +CREATE TYPE test_dummy_double_nested_list AS + TABLE OF test_dummy_nested_object_list; +/ diff --git a/test/ut3_tester_helper/test_dummy_double_nested_object.tps b/test/ut3_tester_helper/test_dummy_double_nested_object.tps new file mode 100644 index 000000000..8f74f7ecb --- /dev/null +++ b/test/ut3_tester_helper/test_dummy_double_nested_object.tps @@ -0,0 +1,15 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_DOUBLE_NESTED_OBJ'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_double_nested_obj force'; + end if; +end; +/ + +create or replace type test_dummy_double_nested_obj as object ( + first_double_nested_obj test_dummy_nested_object, + "Value" varchar2(30) +) +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/test_dummy_nested_object.tps b/test/ut3_tester_helper/test_dummy_nested_object.tps new file mode 100644 index 000000000..6aef3bef8 --- /dev/null +++ b/test/ut3_tester_helper/test_dummy_nested_object.tps @@ -0,0 +1,15 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_NESTED_OBJECT'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_nested_object force'; + end if; +end; +/ + +create or replace type test_dummy_nested_object as object ( + first_nested_obj test_dummy_object, + sec_nested_obj test_dummy_object +) +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/test_dummy_nested_object_list.tps b/test/ut3_tester_helper/test_dummy_nested_object_list.tps new file mode 100644 index 000000000..fd47b55bd --- /dev/null +++ b/test/ut3_tester_helper/test_dummy_nested_object_list.tps @@ -0,0 +1,15 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_NESTED_OBJECT_LIST'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_nested_object_list force'; + end if; +end; +/ + +create or replace type test_dummy_nested_object_list as object ( + first_nested_obj test_dummy_object_list, + somename varchar2(50) +) +/ \ No newline at end of file diff --git a/test/ut3_tester_helper/test_dummy_object_list.tps b/test/ut3_tester_helper/test_dummy_object_list.tps index 67bba558e..849974da1 100644 --- a/test/ut3_tester_helper/test_dummy_object_list.tps +++ b/test/ut3_tester_helper/test_dummy_object_list.tps @@ -1,2 +1,12 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_DUMMY_OBJECT_LIST'; + if l_exists > 0 then + execute immediate 'drop type test_dummy_object_list force'; + end if; +end; +/ + create or replace type test_dummy_object_list as table of test_dummy_object / diff --git a/test/ut3_tester_helper/test_nested_tab_varray.tps b/test/ut3_tester_helper/test_nested_tab_varray.tps new file mode 100644 index 000000000..9f58d090e --- /dev/null +++ b/test/ut3_tester_helper/test_nested_tab_varray.tps @@ -0,0 +1,14 @@ +declare + l_exists integer; +begin + select count(1) into l_exists from user_types where type_name = 'TEST_NESTED_TAB_VARRAY'; + if l_exists > 0 then + execute immediate 'drop type test_nested_tab_varray force'; + end if; +end; +/ + +create or replace type test_nested_tab_varray as object ( + n_varray t_varray +) +/ diff --git a/test/ut3_user/api/test_ut_run.pkb b/test/ut3_user/api/test_ut_run.pkb index e3af7c5ad..8c99af7d7 100644 --- a/test/ut3_user/api/test_ut_run.pkb +++ b/test/ut3_user/api/test_ut_run.pkb @@ -6,20 +6,21 @@ create or replace package body test_ut_run is gc_client_info constant varchar2(32767) := 'test client info'; g_context_test_results clob; + g_timestamp timestamp; procedure clear_expectations is begin ut3_tester_helper.main_helper.clear_expectations(); end; - procedure create_ut3$user#_tests is + procedure create_ut3_user_tests is begin - ut3_tester_helper.run_helper.create_ut3$user#_tests(); + ut3_tester_helper.run_helper.create_ut3_user_tests(); end; - procedure drop_ut3$user#_tests is + procedure drop_ut3_user_tests is begin - ut3_tester_helper.run_helper.drop_ut3$user#_tests(); + ut3_tester_helper.run_helper.drop_ut3_user_tests(); end; procedure ut_version is @@ -256,7 +257,7 @@ create or replace package body test_ut_run is ut.expect(l_results).to_be_like( '%1) does_stuff%' || 'ORA-01403: no data found%' || - 'ORA-06512: at "UT3$USER#.PARENT_SUITE%' + 'ORA-06512: at "UT3_USER.PARENT_SUITE%' ); end; @@ -550,7 +551,7 @@ Failures:% ORA-04068: existing state of packages (DB_LOOPBACK%) has been discarded ORA-04061: existing state of package body "%.STATEFUL_PACKAGE" has been invalidated ORA-04065: not executed, altered or dropped package body "%.STATEFUL_PACKAGE"% - ORA-06512: at line 6% + ORA-06512: at line 7% 1 tests, 0 failed, 1 errored, 0 disabled, 0 warning(s)%'; --Act @@ -624,7 +625,7 @@ Failures:% begin execute immediate q'[select count(1) from all_objects o where o.owner = :object_owner and o.object_type = 'PACKAGE' and o.status = 'INVALID' and o.object_name= :object_name]' into l_is_invalid - using 'UT3$USER#','INVALID_PCKAG_THAT_REVALIDATES'; + using 'UT3_USER','INVALID_PCKAG_THAT_REVALIDATES'; select * bulk collect into l_results from table(ut3_develop.ut.run('invalid_pckag_that_revalidates')); @@ -741,7 +742,7 @@ Failures:% procedure remove_time_from_results(a_results in out nocopy ut3_develop.ut_varchar2_list) is begin for i in 1 .. a_results.count loop - a_results(i) := regexp_replace(a_results(i),'\[[0-9]*[\.,][0-9]+ sec\]',''); + a_results(i) := regexp_replace(a_results(i),'\[[0-9]*[\.,]?[0-9]+ sec\]',''); a_results(i) := regexp_replace(a_results(i),'Finished in [0-9]*[\.,][0-9]+ seconds',''); end loop; end; @@ -853,7 +854,7 @@ Failures:% l_results ut3_develop.ut_varchar2_list; begin select * bulk collect into l_random_results - from table ( ut3_develop.ut.run( 'ut3_tester_helper.test_package_1', a_random_test_order_seed => 3 ) ) + from table ( ut3_develop.ut.run( 'ut3_tester_helper.test_package_1', a_random_test_order_seed => 123 ) ) where trim(column_value) is not null and column_value not like 'Finished in %' and column_value not like '%Tests were executed with random order %'; @@ -947,7 +948,7 @@ Failures:% procedure two_test_run_by_two_tags is l_results clob; begin - ut3_tester_helper.run_helper.run(a_tags => 'subtest1,subtest2'); + ut3_tester_helper.run_helper.run(a_tags => 'subtest1|subtest2'); l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); --Assert ut.expect( l_results ).to_be_like( '%test_package_1%' ); @@ -957,7 +958,21 @@ Failures:% ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); end; - + + procedure two_test_run_by_two_tags_leg is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'subtest1,subtest2'); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).to_be_like( '%test_package_1%' ); + ut.expect( l_results ).to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_1.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_2.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + procedure suite_with_children_tag is l_results clob; begin @@ -1077,6 +1092,20 @@ Failures:% procedure tag_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1|suite2test1'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + end; + + procedure tag_run_func_path_list_leg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1,suite2test1'); --Assert @@ -1091,6 +1120,18 @@ Failures:% procedure tag_inc_exc_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => '(suite1test1|suite2test1)&!suite2'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + end; + + procedure tag_inc_exc_run_fun_pth_lst_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1,suite2test1,-suite2'); --Assert @@ -1103,6 +1144,22 @@ Failures:% procedure tag_exclude_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests,:tests2'),a_tags => '!suite1test2&!suite2test1&!test1suite3'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_3%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_3.test2%executed%' ); + end; + +procedure tag_exclude_run_fun_pth_lst_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests,:tests2'),a_tags => '-suite1test2,-suite2test1,-test1suite3'); --Assert @@ -1119,6 +1176,20 @@ Failures:% procedure tag_include_exclude_run_func is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => '(suite1)&(!suite1test2&!suite2test1&!test1suite3)'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); + end; + + procedure tag_include_exclude_run_fun_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(a_tags => 'suite1,-suite1test2,-suite2test1,-test1suite3'); --Assert @@ -1131,6 +1202,105 @@ Failures:% ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); end; + procedure tag_complex_expressions is + l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(fast|simple)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test6 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(!patch_3_1_13&!patch_3_1_14)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_1.test1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test5%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(patch_3_1_13&!slow)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(simple&end_to_end)|(development&fast)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_1.test1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_2.test3 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test6 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'any'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_2_level2_pkg.suite2_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_2_level2_pkg.suite2_2_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_2_level2_pkg.suite1_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_2_level2_pkg.suite1_2_test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_1_level2_pkg.suite1_1_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_1_level2_pkg.suite1_1_test2_level2 executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'none'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_1_level2_pkg.suite1_1_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_2_level2_pkg.suite2_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_2_level2_pkg.suite2_2_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_2_level2_pkg.suite1_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_2_level2_pkg.suite1_2_test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_1_level2_pkg.suite1_1_test1_level2 executed%' ); + + + end; + + procedure invalid_tag_expression is + l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development!&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&end_to_end|)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&!!end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(&development&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(development|end_to_end))'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + end; + procedure set_application_info is begin dbms_application_info.set_module( gc_module, gc_action ); @@ -1260,19 +1430,21 @@ Failures:% begin select * bulk collect into l_lines from table(ut3_develop.ut.run('check_context')); g_context_test_results := ut3_tester_helper.main_helper.table_to_clob(l_lines); + g_timestamp := current_timestamp; end; procedure sys_ctx_on_suite_beforeall is begin ut.expect(g_context_test_results).to_be_like( - '%BEFORE_SUITE:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.before_suite' + '%BEFORE_SUITE:COVERAGE_RUN_ID=________________________________%' + ||'%BEFORE_SUITE:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.before_suite' ||'%BEFORE_SUITE:CURRENT_EXECUTABLE_TYPE=beforeall' ||'%BEFORE_SUITE:RUN_PATHS=check_context' ||'%BEFORE_SUITE:SUITE_DESCRIPTION=Suite description' ||'%BEFORE_SUITE:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%BEFORE_SUITE:SUITE_PATH=some.suite.path.check_context' - ||'%BEFORE_SUITE:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_SUITE:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=before_suite%' @@ -1287,14 +1459,14 @@ Failures:% '%BEFORE_CONTEXT:CONTEXT_DESCRIPTION=context description' ||'%BEFORE_CONTEXT:CONTEXT_NAME=some_context' ||'%BEFORE_CONTEXT:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%BEFORE_CONTEXT:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_CONTEXT:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%BEFORE_CONTEXT:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.before_context' ||'%BEFORE_CONTEXT:CURRENT_EXECUTABLE_TYPE=beforeall' ||'%BEFORE_CONTEXT:RUN_PATHS=check_context' ||'%BEFORE_CONTEXT:SUITE_DESCRIPTION=Suite description' ||'%BEFORE_CONTEXT:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%BEFORE_CONTEXT:SUITE_PATH=some.suite.path.check_context' - ||'%BEFORE_CONTEXT:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_CONTEXT:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=before_context%' @@ -1308,17 +1480,17 @@ Failures:% '%BEFORE_EACH_TEST:CONTEXT_DESCRIPTION=context description' ||'%BEFORE_EACH_TEST:CONTEXT_NAME=some_context' ||'%BEFORE_EACH_TEST:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%BEFORE_EACH_TEST:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_EACH_TEST:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%BEFORE_EACH_TEST:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.before_each_test' ||'%BEFORE_EACH_TEST:CURRENT_EXECUTABLE_TYPE=beforeeach' ||'%BEFORE_EACH_TEST:RUN_PATHS=check_context' ||'%BEFORE_EACH_TEST:SUITE_DESCRIPTION=Suite description' ||'%BEFORE_EACH_TEST:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%BEFORE_EACH_TEST:SUITE_PATH=some.suite.path.check_context' - ||'%BEFORE_EACH_TEST:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_EACH_TEST:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%BEFORE_EACH_TEST:TEST_DESCRIPTION=Some test description' ||'%BEFORE_EACH_TEST:TEST_NAME='||gc_owner||'.check_context.the_test' - ||'%BEFORE_EACH_TEST:TEST_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_EACH_TEST:TEST_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=before_each_test%' @@ -1331,17 +1503,17 @@ Failures:% '%BEFORE_TEST:CONTEXT_DESCRIPTION=context description' ||'%BEFORE_TEST:CONTEXT_NAME=some_context' ||'%BEFORE_TEST:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%BEFORE_TEST:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_TEST:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%BEFORE_TEST:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.before_test' ||'%BEFORE_TEST:CURRENT_EXECUTABLE_TYPE=beforetest' ||'%BEFORE_TEST:RUN_PATHS=check_context' ||'%BEFORE_TEST:SUITE_DESCRIPTION=Suite description' ||'%BEFORE_TEST:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%BEFORE_TEST:SUITE_PATH=some.suite.path.check_context' - ||'%BEFORE_TEST:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_TEST:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%BEFORE_TEST:TEST_DESCRIPTION=Some test description' ||'%BEFORE_TEST:TEST_NAME='||gc_owner||'.check_context.the_test' - ||'%BEFORE_TEST:TEST_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%BEFORE_TEST:TEST_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=before_test%' @@ -1354,17 +1526,17 @@ Failures:% '%THE_TEST:CONTEXT_DESCRIPTION=context description' ||'%THE_TEST:CONTEXT_NAME=some_context' ||'%THE_TEST:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%THE_TEST:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%THE_TEST:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%THE_TEST:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.the_test' ||'%THE_TEST:CURRENT_EXECUTABLE_TYPE=test' ||'%THE_TEST:RUN_PATHS=check_context' ||'%THE_TEST:SUITE_DESCRIPTION=Suite description' ||'%THE_TEST:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%THE_TEST:SUITE_PATH=some.suite.path.check_context' - ||'%THE_TEST:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%THE_TEST:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%THE_TEST:TEST_DESCRIPTION=Some test description' ||'%THE_TEST:TEST_NAME='||gc_owner||'.check_context.the_test' - ||'%THE_TEST:TEST_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%THE_TEST:TEST_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=the_test%' @@ -1377,17 +1549,17 @@ Failures:% '%AFTER_TEST:CONTEXT_DESCRIPTION=context description' ||'%AFTER_TEST:CONTEXT_NAME=some_context' ||'%AFTER_TEST:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%AFTER_TEST:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_TEST:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%AFTER_TEST:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.after_test' ||'%AFTER_TEST:CURRENT_EXECUTABLE_TYPE=aftertest' ||'%AFTER_TEST:RUN_PATHS=check_context' ||'%AFTER_TEST:SUITE_DESCRIPTION=Suite description' ||'%AFTER_TEST:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%AFTER_TEST:SUITE_PATH=some.suite.path.check_context' - ||'%AFTER_TEST:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_TEST:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%AFTER_TEST:TEST_DESCRIPTION=Some test description' ||'%AFTER_TEST:TEST_NAME='||gc_owner||'.check_context.the_test' - ||'%AFTER_TEST:TEST_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_TEST:TEST_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=after_test%' @@ -1400,17 +1572,17 @@ Failures:% '%AFTER_EACH_TEST:CONTEXT_DESCRIPTION=context description' ||'%AFTER_EACH_TEST:CONTEXT_NAME=some_context' ||'%AFTER_EACH_TEST:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%AFTER_EACH_TEST:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_EACH_TEST:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%AFTER_EACH_TEST:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.after_each_test' ||'%AFTER_EACH_TEST:CURRENT_EXECUTABLE_TYPE=aftereach' ||'%AFTER_EACH_TEST:RUN_PATHS=check_context' ||'%AFTER_EACH_TEST:SUITE_DESCRIPTION=Suite description' ||'%AFTER_EACH_TEST:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%AFTER_EACH_TEST:SUITE_PATH=some.suite.path.check_context' - ||'%AFTER_EACH_TEST:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_EACH_TEST:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%AFTER_EACH_TEST:TEST_DESCRIPTION=Some test description' ||'%AFTER_EACH_TEST:TEST_NAME='||gc_owner||'.check_context.the_test' - ||'%AFTER_EACH_TEST:TEST_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_EACH_TEST:TEST_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=after_each_test%' @@ -1423,14 +1595,14 @@ Failures:% '%AFTER_CONTEXT:CONTEXT_DESCRIPTION=context description' ||'%AFTER_CONTEXT:CONTEXT_NAME=some_context' ||'%AFTER_CONTEXT:CONTEXT_PATH=some.suite.path.check_context.some_context' - ||'%AFTER_CONTEXT:CONTEXT_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_CONTEXT:CONTEXT_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%AFTER_CONTEXT:CURRENT_EXECUTABLE_NAME='||gc_owner||'.check_context.after_context' ||'%AFTER_CONTEXT:CURRENT_EXECUTABLE_TYPE=afterall' ||'%AFTER_CONTEXT:RUN_PATHS=check_context' ||'%AFTER_CONTEXT:SUITE_DESCRIPTION=Suite description' ||'%AFTER_CONTEXT:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%AFTER_CONTEXT:SUITE_PATH=some.suite.path.check_context' - ||'%AFTER_CONTEXT:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_CONTEXT:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=after_context%' @@ -1447,7 +1619,7 @@ Failures:% ||'%AFTER_SUITE:SUITE_DESCRIPTION=Suite description' ||'%AFTER_SUITE:SUITE_PACKAGE='||gc_owner||'.check_context' ||'%AFTER_SUITE:SUITE_PATH=some.suite.path.check_context' - ||'%AFTER_SUITE:SUITE_START_TIME='||to_char(current_timestamp,'yyyy-mm-dd"T"hh24:mi') + ||'%AFTER_SUITE:SUITE_START_TIME='||to_char(g_timestamp,'syyyy-mm-dd"T"hh24:mi') ||'%APPLICATION_INFO:MODULE=utPLSQL' ||'%APPLICATION_INFO:ACTION=check_context' ||'%APPLICATION_INFO:CLIENT_INFO=after_suite%' diff --git a/test/ut3_user/api/test_ut_run.pks b/test/ut3_user/api/test_ut_run.pks index 753af79da..c57788bff 100644 --- a/test/ut3_user/api/test_ut_run.pks +++ b/test/ut3_user/api/test_ut_run.pks @@ -4,8 +4,8 @@ create or replace package test_ut_run is procedure clear_expectations; - procedure create_ut3$user#_tests; - procedure drop_ut3$user#_tests; + procedure create_ut3_user_tests; + procedure drop_ut3_user_tests; --%test(ut.version() returns version of the framework) procedure ut_version; @@ -17,8 +17,8 @@ create or replace package test_ut_run is --%context(ut_run_procedure) --%displayname(ut.run() procedure options) - --%beforeall(create_ut3$user#_tests) - --%afterall(drop_ut3$user#_tests) + --%beforeall(create_ut3_user_tests) + --%afterall(drop_ut3_user_tests) --%test(Runs all tests in current schema with default reporter when no parameters given) procedure run_proc_no_params; @@ -88,8 +88,8 @@ create or replace package test_ut_run is --%context(ut_run_function) --%displayname(ut.run() function options) - --%beforeall(create_ut3$user#_tests) - --%afterall(drop_ut3$user#_tests) + --%beforeall(create_ut3_user_tests) + --%afterall(drop_ut3_user_tests) --%test(Runs all tests in current schema with default reporter when no parameters given) procedure run_func_no_params; @@ -167,8 +167,8 @@ create or replace package test_ut_run is --%context(random_order) --%displayname(Random test execution order) - --%beforeall(create_ut3$user#_tests) - --%afterall(drop_ut3$user#_tests) + --%beforeall(create_ut3_user_tests) + --%afterall(drop_ut3_user_tests) --%test(Runs tests in random order) procedure run_with_random_order; @@ -184,8 +184,8 @@ create or replace package test_ut_run is --%context(run with tags) --%displayname(Call ut.run with #tags) - --%beforeall(create_ut3$user#_tests) - --%afterall(drop_ut3$user#_tests) + --%beforeall(create_ut3_user_tests) + --%afterall(drop_ut3_user_tests) --%test(Execute test by tag ut_run) procedure test_run_by_one_tag; @@ -202,6 +202,9 @@ create or replace package test_ut_run is --%test(Execute tests by passing two tags) procedure two_test_run_by_two_tags; + --%test(Execute tests by passing two tags - Legacy notation) + procedure two_test_run_by_two_tags_leg; + --%test(Execute suite and all of its children) procedure suite_with_children_tag; @@ -235,15 +238,34 @@ create or replace package test_ut_run is --%test(Runs tests from given paths with paths list and a tag) procedure tag_run_func_path_list; + --%test(Runs tests from given paths with paths list and a tag - Legacy Notation) + procedure tag_run_func_path_list_leg; + --%test(Runs tests from given paths with paths list and include/exclude tags) procedure tag_inc_exc_run_func_path_list; + --%test(Runs tests from given paths with paths list and include/exclude tags - Legacy Notation) + procedure tag_inc_exc_run_fun_pth_lst_lg; + --%test(Runs tests from given path and excludes specific tags) procedure tag_exclude_run_func_path_list; + --%test(Runs tests from given path and excludes specific tags - Legacy Notation) + procedure tag_exclude_run_fun_pth_lst_lg; + --%test(Runs tests from given tags and exclude tags) procedure tag_include_exclude_run_func; + --%test(Runs tests from given tags and exclude tags - Legacy Notation) + procedure tag_include_exclude_run_fun_lg; + + --%test(Runs tests suing complex expressions) + procedure tag_complex_expressions; + + --%test(Testing invalid tag expression) + --%throws(-20219) + procedure invalid_tag_expression; + --%endcontext --%context(ut3_info context) diff --git a/test/ut3_user/api/test_ut_runner.pkb b/test/ut3_user/api/test_ut_runner.pkb index 2aa624ed8..d35495c70 100644 --- a/test/ut3_user/api/test_ut_runner.pkb +++ b/test/ut3_user/api/test_ut_runner.pkb @@ -265,27 +265,27 @@ end;'; --Arrange open l_expected for select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, - 'some.path.dummy_test_package' path, 0 disabled_flag,null tags + 'some.path.dummy_test_package' path, 0 disabled_flag, null disabled_reason,null tags from dual union all select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, 'dummy_test' item_description, 'UT_TEST' item_type, 6 item_line_no, - 'some.path.dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,null tags + 'some.path.dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag, null disabled_reason,null tags from dual union all select - 'UT3$USER#' object_owner, 'PATH' object_name, 'PATH' item_name, + 'UT3_USER' object_owner, 'PATH' object_name, 'PATH' item_name, null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, - 'some.path' path, 0 disabled_flag, null tags + 'some.path' path, 0 disabled_flag, null disabled_reason, null tags from dual union all select - 'UT3$USER#' object_owner, 'SOME' object_name, 'SOME' item_name, + 'UT3_USER' object_owner, 'SOME' object_name, 'SOME' item_name, null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, - 'some' path, 0 disabled_flag, null tags + 'some' path, 0 disabled_flag, null disabled_reason, null tags from dual; --Act - open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); + open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3_USER','DUMMY_TEST_PACKAGE')); --Assert ut.expect(l_actual).to_equal(l_expected); end; @@ -297,27 +297,27 @@ end;'; --Arrange open l_expected for select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, - 'some.path.dummy_test_package' path, 0 disabled_flag,'dummy' tags + 'some.path.dummy_test_package' path, 0 disabled_flag, null disabled_reason,'dummy' tags from dual union all select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, 'dummy_test' item_description, 'UT_TEST' item_type, 7 item_line_no, - 'some.path.dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,'testtag' tags + 'some.path.dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag, null disabled_reason,'testtag' tags from dual union all select - 'UT3$USER#' object_owner, 'PATH' object_name, 'PATH' item_name, + 'UT3_USER' object_owner, 'PATH' object_name, 'PATH' item_name, null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, - 'some.path' path, 0 disabled_flag, null tags + 'some.path' path, 0 disabled_flag, null disabled_reason, null tags from dual union all select - 'UT3$USER#' object_owner, 'SOME' object_name, 'SOME' item_name, + 'UT3_USER' object_owner, 'SOME' object_name, 'SOME' item_name, null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, - 'some' path, 0 disabled_flag, null tags + 'some' path, 0 disabled_flag, null disabled_reason, null tags from dual; --Act - open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); + open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3_USER','DUMMY_TEST_PACKAGE')); --Assert ut.expect(l_actual).to_equal(l_expected); end; @@ -329,17 +329,49 @@ end;'; --Arrange open l_expected for select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, - 'dummy_test_package' path, 0 disabled_flag,'suitetag1,suitetag2' tags + 'dummy_test_package' path, 0 disabled_flag, null disabled_reason,'suitetag1,suitetag2' tags from dual union all select - 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, 'dummy_test' item_description, 'UT_TEST' item_type, 6 item_line_no, - 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,'testtag1,testtag2' tags + 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag, null disabled_reason,'testtag1,testtag2' tags from dual; --Act - open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); + open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('UT3_USER','DUMMY_TEST_PACKAGE')); + --Assert + ut.expect(l_actual).to_equal(l_expected); + end; + + procedure test_get_suites_info_by_path is + l_expected sys_refcursor; + l_actual sys_refcursor; + begin + --Arrange + open l_expected for + select + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, + 'some.path.dummy_test_package' path, 0 disabled_flag, null disabled_reason,null tags + from dual union all + select + 'UT3_USER' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'dummy_test' item_description, 'UT_TEST' item_type, 6 item_line_no, + 'some.path.dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag, null disabled_reason,null tags + from dual union all + select + 'UT3_USER' object_owner, 'PATH' object_name, 'PATH' item_name, + null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, + 'some.path' path, 0 disabled_flag, null disabled_reason, null tags + from dual union all + select + 'UT3_USER' object_owner, 'SOME' object_name, 'SOME' item_name, + null item_description, 'UT_LOGICAL_SUITE' item_type, null item_line_no, + 'some' path, 0 disabled_flag, null disabled_reason, null tags + from dual; + --Act + open l_actual for select * from table(ut3_develop.ut_runner.get_suites_info('ut3_user:some.path.dummy_test_package')); --Assert ut.expect(l_actual).to_equal(l_expected); end; @@ -354,14 +386,14 @@ end;'; select 'UT3_DEVELOP.UT_DEBUG_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_COVERAGE_HTML_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_COVERAGE_SONAR_REPORTER', 'Y' from dual union all - select 'UT3_DEVELOP.UT_COVERALLS_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_DOCUMENTATION_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_JUNIT_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_REALTIME_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_SONAR_TEST_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_TEAMCITY_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_TFS_JUNIT_REPORTER', 'Y' from dual union all - select 'UT3_DEVELOP.UT_XUNIT_REPORTER', 'Y' from dual + select 'UT3_DEVELOP.UT_XUNIT_REPORTER', 'Y' from dual union all + select 'UT3_DEVELOP.UT_TAP_REPORTER', 'Y' from dual order by 1; --Act open l_actual for select * from table(ut3_develop.ut_runner.GET_REPORTERS_LIST()) order by 1; @@ -571,20 +603,20 @@ end;'; begin ut.expect( ut3_develop.ut_runner.is_test( - a_owner => 'UT3$USER#', + a_owner => 'UT3_USER', a_package_name => 'DUMMY_TEST_PACKAGE', a_procedure_name => 'SOME_DUMMY_TEST_PROCEDURE' ) ).to_be_true(); - ut.expect( ut3_develop.ut_runner.is_test( 'ut3$user#','dummy_test_package','some_dummy_test_procedure' ) ).to_be_true(); + ut.expect( ut3_develop.ut_runner.is_test( 'ut3_user','dummy_test_package','some_dummy_test_procedure' ) ).to_be_true(); end; procedure is_test_false is begin - ut.expect( ut3_develop.ut_runner.is_test( 'UT3$USER#','DUMMY_TEST_PACKAGE', 'BAD' ) ).to_be_false(); - ut.expect( ut3_develop.ut_runner.is_test( 'UT3$USER#','BAD_TEST_PACKAGE', 'some_dummy_test_procedure' ) ).to_be_false(); - ut.expect( ut3_develop.ut_runner.is_test( 'UT3$USER#','DUMMY_TEST_PACKAGE', null ) ).to_be_false(); - ut.expect( ut3_develop.ut_runner.is_test( 'UT3$USER#',null,'some_dummy_test_procedure' ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_test( 'UT3_USER','DUMMY_TEST_PACKAGE', 'BAD' ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_test( 'UT3_USER','BAD_TEST_PACKAGE', 'some_dummy_test_procedure' ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_test( 'UT3_USER','DUMMY_TEST_PACKAGE', null ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_test( 'UT3_USER',null,'some_dummy_test_procedure' ) ).to_be_false(); ut.expect( ut3_develop.ut_runner.is_test( null,'DUMMY_TEST_PACKAGE','some_dummy_test_procedure' ) ).to_be_false(); end; @@ -592,26 +624,26 @@ end;'; begin ut.expect( ut3_develop.ut_runner.is_suite( - a_owner => 'UT3$USER#', + a_owner => 'UT3_USER', a_package_name => 'DUMMY_TEST_PACKAGE' ) ).to_be_true(); - ut.expect( ut3_develop.ut_runner.is_suite( 'ut3$user#','dummy_test_package' ) ).to_be_true(); + ut.expect( ut3_develop.ut_runner.is_suite( 'ut3_user','dummy_test_package' ) ).to_be_true(); end; procedure is_suite_false is begin - ut.expect( ut3_develop.ut_runner.is_suite( 'UT3$USER#','BAD' ) ).to_be_false(); - ut.expect( ut3_develop.ut_runner.is_suite( 'UT3$USER#', null ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_suite( 'UT3_USER','BAD' ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_suite( 'UT3_USER', null ) ).to_be_false(); ut.expect( ut3_develop.ut_runner.is_suite( null,'DUMMY_TEST_PACKAGE' ) ).to_be_false(); - ut.expect( ut3_develop.ut_runner.is_suite( 'UT3$USER#','bad_test_package' ) ).to_be_false(); + ut.expect( ut3_develop.ut_runner.is_suite( 'UT3_USER','bad_test_package' ) ).to_be_false(); end; procedure has_suites_true is begin - ut.expect( ut3_develop.ut_runner.has_suites( a_owner => 'UT3$USER#' ) ).to_be_true(); - ut.expect( ut3_develop.ut_runner.has_suites( 'ut3$user#' ) ).to_be_true(); + ut.expect( ut3_develop.ut_runner.has_suites( a_owner => 'UT3_USER' ) ).to_be_true(); + ut.expect( ut3_develop.ut_runner.has_suites( 'ut3_user' ) ).to_be_true(); end; procedure has_suites_false is diff --git a/test/ut3_user/api/test_ut_runner.pks b/test/ut3_user/api/test_ut_runner.pks index 08282e997..e003ba852 100644 --- a/test/ut3_user/api/test_ut_runner.pks +++ b/test/ut3_user/api/test_ut_runner.pks @@ -65,6 +65,11 @@ create or replace package test_ut_runner is --%aftertest(cleanup_cache) procedure test_get_suites_info_twotag; + --%test(get_suites_info returns a cursor containing records for a newly created test with passed path) + --%beforetest(setup_cache_objects) + --%aftertest(cleanup_cache) + procedure test_get_suites_info_by_path; + --%test(get_reporters_list returns a cursor containing all built-in reporters and information about output-reporter) --%beforetest(setup_cache_objects) --%aftertest(cleanup_cache) diff --git a/test/ut3_user/expectations.pkb b/test/ut3_user/expectations.pkb index 30af48a44..be8627712 100644 --- a/test/ut3_user/expectations.pkb +++ b/test/ut3_user/expectations.pkb @@ -16,14 +16,14 @@ create or replace package body expectations as l_expected := q'[FAILURE Actual: 1 (number) was expected to equal: 0 (number) - at "UT3$USER#.SOME_PKG%", line 4 ut3_develop.ut.expect(1).to_equal(0); + at "UT3_USER.SOME_PKG%", line 4 ut3_develop.ut.expect(1).to_equal(0); at "anonymous block", line 1 - at "UT3$USER#.EXPECTATIONS%", line 10 + at "UT3_USER.EXPECTATIONS%", line 10 SUCCESS Actual: 0 (number) was expected to equal: 0 (number) FAILURE Actual: 1 (number) was expected to equal: 0 (number) - at "UT3$USER#.EXPECTATIONS%", line 11 ut3_develop.ut.expect(1).to_equal(0); + at "UT3_USER.EXPECTATIONS%", line 11 ut3_develop.ut.expect(1).to_equal(0); SUCCESS Actual: 0 (number) was expected to equal: 0 (number) ]'; diff --git a/test/ut3_user/expectations/binary/test_to_be_within.pkb b/test/ut3_user/expectations/binary/test_to_be_within.pkb new file mode 100644 index 000000000..c9aae6ee2 --- /dev/null +++ b/test/ut3_user/expectations/binary/test_to_be_within.pkb @@ -0,0 +1,360 @@ +create or replace package body test_to_be_within is + + procedure cleanup_expectations is + begin + ut3_tester_helper.main_helper.clear_expectations( ); + end; + + function be_within_expectation_block( + a_matcher_name varchar2, + a_actual_data_type varchar2, + a_actual_data varchar2, + a_expected_data_type varchar2, + a_expected_data varchar2, + a_distance varchar2, + a_distance_data_type varchar2, + a_matcher_end varchar2 + ) return varchar2 + is + l_execute varchar2(32000); + begin + l_execute := ' + declare + l_actual '||a_actual_data_type||' := '||a_actual_data||'; + l_expected '||a_expected_data_type||' := '||a_expected_data||'; + l_distance '||a_distance_data_type||' := '||a_distance||'; + begin + --act - execute the expectation + ut3_develop.ut.expect( l_actual ).'||a_matcher_name||'(l_distance).of_(l_expected)'||a_matcher_end||'; + end;'; + return l_execute; + end; + + procedure test_to_be_within_fail( + a_matcher_name varchar2, + a_data_type varchar2, + a_actual varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_data_type varchar2, + a_matcher_end varchar2 := null + ) is + begin + execute immediate be_within_expectation_block( + a_matcher_name,a_data_type, a_actual, a_data_type, a_expected, + a_distance,a_distance_data_type, a_matcher_end + ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).not_to_equal(0); + cleanup_expectations; + end; + + procedure test_to_be_within_success( + a_matcher_name varchar2, + a_data_type varchar2, + a_actual varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_data_type varchar2, + a_matcher_end varchar2 := null + ) is + begin + execute immediate be_within_expectation_block( + a_matcher_name,a_data_type, a_actual, a_data_type, a_expected, + a_distance,a_distance_data_type, a_matcher_end + ); + ut.expect + (ut3_tester_helper.main_helper.get_failed_expectations_num + ,be_within_expectation_block( + a_matcher_name,a_data_type, a_actual, a_data_type, a_expected, + a_distance,a_distance_data_type, a_matcher_end + ) + ).to_equal(0); + cleanup_expectations; + end; + + procedure success_tests is + begin + test_to_be_within_success('to_be_within','number', '2', '4','2','number'); + test_to_be_within_success('to_be_within','number', '4', '2','2','number'); + test_to_be_within_success('to_be_within','date', 'sysdate+1', 'sysdate','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','date', 'sysdate', 'sysdate+1','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','date', 'sysdate', 'sysdate+200','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','date', 'sysdate+200', 'sysdate','''1-0''','interval year to month'); + + test_to_be_within_success('to_be_within','timestamp', q'[TIMESTAMP '2017-08-09 07:00:00']', q'[TIMESTAMP '2017-08-08 06:59:48.667']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp', q'[TIMESTAMP '2017-08-08 06:59:48.667']', q'[TIMESTAMP '2017-08-09 07:00:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp', q'[TIMESTAMP '2017-08-09 07:00:00']', q'[TIMESTAMP '2018-08-09 07:00:00']','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','timestamp', q'[TIMESTAMP '2018-08-09 07:00:00']', q'[TIMESTAMP '2017-08-09 07:00:00']','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2018-08-09 07:00:00 -7:00']','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2018-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_success('to_be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2018-08-09 07:00:00 -7:00']','''1-0''','interval year to month'); + test_to_be_within_success('to_be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2018-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']','''1-0''','interval year to month'); + + test_to_be_within_success('to_( ut3_develop.be_within','number', '2', '4','2','number', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','number', '4', '2','2','number', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','date', 'sysdate+1', 'sysdate','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','date', 'sysdate', 'sysdate+1','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','date', 'sysdate', 'sysdate+200','''1-0''','interval year to month', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','date', 'sysdate+200', 'sysdate','''1-0''','interval year to month', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','timestamp', q'[TIMESTAMP '2018-08-09 07:00:00']', q'[TIMESTAMP '2017-08-09 07:00:00']','''1-0''','interval year to month', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_success('to_( ut3_develop.be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_fail('not_to_be_within','number', '2', '4','2','number'); + test_to_be_within_fail('not_to_be_within','number', '4', '2','2','number'); + test_to_be_within_fail('not_to_be_within','date', 'sysdate+1', 'sysdate','''1 0:00:11.333''','interval day to second'); + test_to_be_within_fail('not_to_be_within','date', 'sysdate', 'sysdate+1','''1 0:00:11.333''','interval day to second'); + test_to_be_within_fail('not_to_be_within','date', 'sysdate', 'sysdate+200','''1-0''','interval year to month'); + test_to_be_within_fail('not_to_be_within','date', 'sysdate+200', 'sysdate','''1-0''','interval year to month'); + test_to_be_within_fail('not_to( ut3_develop.be_within','number', '2', '4','2','number',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','number', '4', '2','2','number',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','date', 'sysdate+1', 'sysdate','''1 0:00:11.333''','interval day to second',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','date', 'sysdate', 'sysdate+1','''1 0:00:11.333''','interval day to second',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','date', 'sysdate', 'sysdate+200','''1-0''','interval year to month',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','date', 'sysdate+200', 'sysdate','''1-0''','interval year to month',')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','timestamp', q'[TIMESTAMP '2018-08-09 07:00:00']', q'[TIMESTAMP '2017-08-09 07:00:00']','''1-0''','interval year to month', ')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_fail('not_to( ut3_develop.be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second', ')'); + test_to_be_within_fail('not_to_be_within','timestamp', q'[TIMESTAMP '2018-08-09 07:00:00']', q'[TIMESTAMP '2017-08-09 07:00:00']','''1-0''','interval year to month'); + test_to_be_within_fail('not_to_be_within','timestamp_tz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second'); + test_to_be_within_fail('not_to_be_within','timestamp_ltz_unconstrained', q'[TIMESTAMP '2017-08-09 07:00:00 -7:00']', q'[TIMESTAMP '2017-08-08 05:59:48.668 -8:00']','''1 0:00:11.333''','interval day to second'); + end; + + procedure failed_tests is + begin + test_to_be_within_fail('to_be_within','number', '2', '4','1','number'); + test_to_be_within_fail('to_be_within','number', '4', '2','1','number'); + test_to_be_within_fail('to_be_within','date', 'sysdate', 'sysdate+1','''0 0:00:11.333''','interval day to second'); + test_to_be_within_fail('to_be_within','date', 'sysdate+1', 'sysdate','''0 0:00:11.333''','interval day to second'); + test_to_be_within_fail('to_be_within','date', 'sysdate', 'sysdate+750','''1-0''','interval year to month'); + test_to_be_within_fail('to_be_within','date', 'sysdate+750', 'sysdate','''1-0''','interval year to month'); + test_to_be_within_fail('to_( ut3_develop.be_within','number', '2', '4','1','number',')'); + test_to_be_within_fail('to_( ut3_develop.be_within','number', '4', '2','1','number',')'); + test_to_be_within_fail('to_( ut3_develop.be_within','date', 'sysdate', 'sysdate+1','''0 0:00:11.333''','interval day to second',')'); + test_to_be_within_fail('to_( ut3_develop.be_within','date', 'sysdate+1', 'sysdate','''0 0:00:11.333''','interval day to second',')'); + test_to_be_within_fail('to_( ut3_develop.be_within','date', 'sysdate', 'sysdate+750','''1-0''','interval year to month',')'); + test_to_be_within_fail('to_( ut3_develop.be_within','date', 'sysdate+750', 'sysdate','''1-0''','interval year to month',')'); + test_to_be_within_success('not_to_be_within','number', '2', '4','1','number'); + test_to_be_within_success('not_to_be_within','number', '4', '2','1','number'); + test_to_be_within_success('not_to_be_within','date', 'sysdate', 'sysdate+1','''0 0:00:11.333''','interval day to second'); + test_to_be_within_success('not_to_be_within','date', 'sysdate+1', 'sysdate','''0 0:00:11.333''','interval day to second'); + test_to_be_within_success('not_to_be_within','date', 'sysdate', 'sysdate+750','''1-0''','interval year to month'); + test_to_be_within_success('not_to_be_within','date', 'sysdate+750', 'sysdate','''1-0''','interval year to month'); + test_to_be_within_success('not_to( ut3_develop.be_within','number', '2', '4','1','number',')'); + test_to_be_within_success('not_to( ut3_develop.be_within','number', '4', '2','1','number',')'); + test_to_be_within_success('not_to( ut3_develop.be_within','date', 'sysdate', 'sysdate+1','''0 0:00:11.333''','interval day to second',')'); + test_to_be_within_success('not_to( ut3_develop.be_within','date', 'sysdate+1', 'sysdate','''0 0:00:11.333''','interval day to second',')'); + test_to_be_within_success('not_to( ut3_develop.be_within','date', 'sysdate', 'sysdate+750','''1-0''','interval year to month',')'); + test_to_be_within_success('not_to( ut3_develop.be_within','date', 'sysdate+750', 'sysdate','''1-0''','interval year to month',')'); + end; + + procedure fail_for_number_not_within is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(4).to_be_within(1).of_(7); + --Assert + l_expected_message := q'[Actual: 4 (number) was expected to be within 1 of 7 (number)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_for_ds_int_not_within is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(sysdate).to_be_within(interval '1' second).of_(sysdate+1); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within 1 second of % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_for_custom_ds_int is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(sysdate).to_be_within(INTERVAL '2 3:04:11.333' DAY TO SECOND).of_(sysdate+100); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within 2 days 3 hours 4 minutes 11.333 seconds of % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_for_ym_int_not_within is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(sysdate).to_be_within(INTERVAL '1' MONTH).of_(sysdate+ 47); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within 1 month of % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_for_custom_ym_int is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(sysdate).to_be_within(INTERVAL '1-3' YEAR TO MONTH).of_(sysdate+720); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within 1 year 3 months % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_msg_when_not_within is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + --Act + ut3_develop.ut.expect(1).to_be_within(3).of_(12); + --Assert + l_expected_message := q'[Actual: 1 (number) was expected to be within 3 of 12 (number)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure fail_msg_wrong_types is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(sysdate).to_be_within(interval '1' second).of_(systimestamp); + --Assert + l_expected_message := q'[Matcher 'be within' cannot be used to compare Actual (date) with Expected (timestamp with time zone) using distance (interval day to second).]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure null_expected is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(sysdate).to_be_within(interval '1' second).of_(to_date(null)); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within 1 second of NULL (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure null_actual_and_expected is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(to_date(null)).to_be_within(interval '1' second).of_(to_date(null)); + --Assert + l_expected_message := q'[Actual: NULL (date) was expected to be within 1 second of NULL (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure null_actual is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(to_date(null)).to_be_within(interval '1' second).of_(sysdate); + --Assert + l_expected_message := q'[Actual: NULL (date) was expected to be within 1 second of % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + + procedure null_distance is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(sysdate).to_be_within(cast(null as interval day to second)).of_(sysdate); + --Assert + l_expected_message := q'[Actual: % (date) was expected to be within NULL of % (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + + procedure invalid_distance_for_number is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(2).to_be_within(interval '1' second).of_(1); + --Assert + l_expected_message := q'[Matcher 'be within' cannot be used to compare Actual (number) with Expected (number) using distance (interval day to second).]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + + procedure invalid_distance_for_timestamp is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(sysdate).to_be_within(1).of_(sysdate); + --Assert + l_expected_message := q'[Matcher 'be within' cannot be used to compare Actual (date) with Expected (date) using distance (number).]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure failure_on_tiny_time_diff is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(timestamp'2022-01-29 23:23:23.000000009').to_be_within(interval '0.000000001' second).of_(timestamp'2022-01-29 23:23:23.000000001'); + --Assert + l_expected_message := q'[Actual: 2022-01-29T23:23:23.000000009 (timestamp) was expected to be within .000000001 seconds of 2022-01-29T23:23:23.000000001 (timestamp)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + + procedure failure_on_large_years_compare is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Act + ut3_develop.ut.expect(to_date('-4000-01-01','syyyy-mm-dd')).to_be_within(interval '9990' year).of_(date '9999-12-31'); + --Assert + l_expected_message := q'[Actual: -4000-01-01T00:00:00 (date) was expected to be within 9990 years of 9999-12-31T00:00:00 (date)]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + +end; +/ diff --git a/test/ut3_user/expectations/binary/test_to_be_within.pks b/test/ut3_user/expectations/binary/test_to_be_within.pks new file mode 100644 index 000000000..966e9baaf --- /dev/null +++ b/test/ut3_user/expectations/binary/test_to_be_within.pks @@ -0,0 +1,61 @@ +create or replace package test_to_be_within is + + --%suite((not)to_be_within) + --%suitepath(utplsql.test_user.expectations.binary) + + --%aftereach + procedure cleanup_expectations; + + --%test(gives success for values within a distance) + procedure success_tests; + + --%test(gives failure when value is not within distance) + procedure failed_tests; + + --%test(returns well formatted failure message when expectation fails) + procedure fail_for_number_not_within; + + --%test(returns well formatted failure message for inteval of 1 sec not within) + procedure fail_for_ds_int_not_within; + + --%test(returns well formatted failure message for custom ds interval not within) + procedure fail_for_custom_ds_int; + + --%test(returns well formatted failure message for inteval of 1 month not within) + procedure fail_for_ym_int_not_within; + + --%test(returns well formatted failure message for custom ym interval not within) + procedure fail_for_custom_ym_int; + + --%test(returns well formatted failure message for simple within) + procedure fail_msg_when_not_within; + + --%test(returns well formatted failure message when comparing different datatypes) + procedure fail_msg_wrong_types; + + --%test(failure on null expected value) + procedure null_expected; + + --%test(failure on null actual value) + procedure null_actual; + + --%test(failure on null expected and actual value) + procedure null_actual_and_expected; + + --%test(failure on null distance value) + procedure null_distance; + + --%test(failure on invalid distance datatype for number expected) + procedure invalid_distance_for_number; + + --%test(failure on invalid distance datatype for timestamp expected) + procedure invalid_distance_for_timestamp; + + --%test(failure on exceeding difference in day second time by nanosecond) + procedure failure_on_tiny_time_diff; + + --%test(failure when comparing very large year difference) + procedure failure_on_large_years_compare; + +end; +/ diff --git a/test/ut3_user/expectations/binary/test_to_be_within_pct.pkb b/test/ut3_user/expectations/binary/test_to_be_within_pct.pkb new file mode 100644 index 000000000..da634d90e --- /dev/null +++ b/test/ut3_user/expectations/binary/test_to_be_within_pct.pkb @@ -0,0 +1,221 @@ +create or replace package body test_to_be_within_pct is + + procedure cleanup_expectations is + begin + ut3_tester_helper.main_helper.clear_expectations( ); + end; + + function be_within_expectation_block( + a_matcher varchar2, + a_actual_type varchar2, + a_actual varchar2, + a_expected_type varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_type varchar2, + a_matcher_end varchar2 + ) return varchar2 + is + l_execute varchar2(32000); + begin + l_execute := ' + declare + l_actual '||a_actual_type||' := '||a_actual||'; + l_expected '||a_expected_type||' := '||a_expected||'; + l_distance '||a_distance_type||' := '||a_distance||'; + begin + --act - execute the expectation + ut3_develop.ut.expect( l_actual ).'||a_matcher||'( l_distance ).of_( l_expected )'||a_matcher_end||'; + end;'; + return l_execute; + end; + + procedure test_to_be_within_fail( + a_matcher varchar2, + a_actual_type varchar2, + a_actual varchar2, + a_expected_type varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_type varchar2, + a_failure_message varchar2 := null, + a_matcher_end varchar2 := null + ) is + l_failure_text varchar2(4000); + begin + execute immediate be_within_expectation_block( + a_matcher,a_actual_type, a_actual, a_expected_type, a_expected, + a_distance,a_distance_type, a_matcher_end + ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(1); + if a_failure_message is not null then + l_failure_text := ut3_tester_helper.main_helper.get_failed_expectations(1); + ut.expect( l_failure_text ).to_be_like('%'||a_failure_message||'%' ); + end if; + cleanup_expectations; + end; + + procedure test_to_be_within_success( + a_matcher varchar2, + a_actual_type varchar2, + a_actual varchar2, + a_expected_type varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_type varchar2, + a_matcher_end varchar2 := null + ) is + begin + execute immediate be_within_expectation_block( + a_matcher,a_actual_type, a_actual, a_expected_type, a_expected, + a_distance,a_distance_type, a_matcher_end + ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + cleanup_expectations; + end; + + + procedure test_to_be_within_error( + a_matcher varchar2, + a_actual_type varchar2, + a_actual varchar2, + a_expected_type varchar2, + a_expected varchar2, + a_distance varchar2, + a_distance_type varchar2, + a_error_message varchar2, + a_matcher_end varchar2 := null + ) is + begin + execute immediate be_within_expectation_block( + a_matcher,a_actual_type, a_actual, a_expected_type, a_expected, + a_distance,a_distance_type, a_matcher_end + ); + cleanup_expectations; + ut.fail('Expected exception but nothing was raised'); + exception + when others then + ut.expect(sqlerrm).to_be_like('%'||a_error_message||'%'); + cleanup_expectations; + end; + + + procedure expect_success is + begin + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + cleanup_expectations; + end; + + procedure expect_failure is + begin + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(1); + cleanup_expectations; + end; + + procedure success_tests is + begin + ut3_develop.ut.expect( 1 ).to_be_within_pct( 0.01 ).of_(1.0001); + expect_success; + + ut3_develop.ut.expect( 1.0001 ).to_( ut3_develop.be_within_pct( 0.01 ).of_(1) ); + expect_success; + + ut3_develop.ut.expect( 1.0002 ).not_to_be_within_pct( 0.01 ).of_(1); + expect_success; + + ut3_develop.ut.expect( 1 ).not_to( ut3_develop.be_within_pct( 0.01 ).of_(1.0002) ); + expect_success; + + ut3_develop.ut.expect( 1.0001 ).to_be_within_pct( -0.01 ).of_(1); + expect_success; + + ut3_develop.ut.expect( 1 ).to_( ut3_develop.be_within_pct( -0.01 ).of_(1.0001) ); + expect_success; + + ut3_develop.ut.expect( 1.00000001 ).not_to_be_within_pct( 0 ).of_(1); + expect_success; + + ut3_develop.ut.expect( 0 ).not_to( ut3_develop.be_within_pct( 0.01 ).of_(0.000001) ); + expect_success; + + ut3_develop.ut.expect( 0 ).to_be_within_pct( 0 ).of_( 0 ); + expect_success; + + ut3_develop.ut.expect( 0 ).to_be_within_pct( 100 ).of_( 1 ); + expect_success; + + ut3_develop.ut.expect( -1 ).to_be_within_pct( 200 ).of_( 1 ); + expect_success; + end; + + procedure failed_tests is + begin + ut3_develop.ut.expect( 1 ).not_to_be_within_pct( 0.01 ).of_(1.0001); + expect_failure; + + ut3_develop.ut.expect( 1.0001 ).not_to( ut3_develop.be_within_pct( 0.01 ).of_(1) ); + expect_failure; + + ut3_develop.ut.expect( 1.0002 ).to_be_within_pct( 0.01 ).of_(1); + expect_failure; + + ut3_develop.ut.expect( 1 ).to_( ut3_develop.be_within_pct( 0.01 ).of_(1.0002) ); + expect_failure; + + ut3_develop.ut.expect( 1.0001 ).not_to_be_within_pct( -0.01 ).of_(1); + expect_failure; + + ut3_develop.ut.expect( 1 ).not_to( ut3_develop.be_within_pct( -0.01 ).of_(1.0001) ); + expect_failure; + + ut3_develop.ut.expect( 1.00000001 ).to_be_within_pct( 0 ).of_(1); + expect_failure; + + ut3_develop.ut.expect( 0 ).to_( ut3_develop.be_within_pct( 0.01 ).of_(0.000001) ); + expect_failure; + + ut3_develop.ut.expect( 0 ).not_to_be_within_pct( 0 ).of_( 0 ); + expect_failure; + + ut3_develop.ut.expect( 0 ).not_to_be_within_pct( 100 ).of_( 1 ); + expect_failure; + + ut3_develop.ut.expect( -1 ).not_to_be_within_pct( 200 ).of_( 1 ); + expect_failure; + end; + + procedure fail_for_number_not_within is + begin + test_to_be_within_fail( + 'to_be_within_pct','number', '4', 'number','7', + '1','number', + q'[Actual: 4 (number) was expected to be within 1 % of 7 (number)]' + ); + end; + + procedure fail_at_invalid_argument_types is + begin + test_to_be_within_error( + 'to_be_within_pct','date', 'sysdate', 'date','sysdate', + '''0 0:00:11.333''','interval day to second', + 'wrong number or types of arguments in call to ''TO_BE_WITHIN_PCT''' + ); + test_to_be_within_error( + 'to_be_within_pct','number','1', 'date', 'sysdate', + '1','number', + ' wrong number or types of arguments in call to ''OF_''' + ); + test_to_be_within_error( + 'to_be_within_pct','number','1','number','1', + 'sysdate', 'date', + ' wrong number or types of arguments in call to ''TO_BE_WITHIN_PCT''' + ); + test_to_be_within_fail( + 'to_be_within_pct','date', 'sysdate', 'number','1', + '1','number', + 'Matcher ''be within pct'' cannot be used to compare Actual (date) with Expected (number) using distance (number).' + ); + end; + +end; +/ diff --git a/test/ut3_user/expectations/binary/test_to_be_within_pct.pks b/test/ut3_user/expectations/binary/test_to_be_within_pct.pks new file mode 100644 index 000000000..4b8f0cb4c --- /dev/null +++ b/test/ut3_user/expectations/binary/test_to_be_within_pct.pks @@ -0,0 +1,22 @@ +create or replace package test_to_be_within_pct is + + --%suite((not)to_be_within_pct) + --%suitepath(utplsql.test_user.expectations.binary) + + --%aftereach + procedure cleanup_expectations; + + --%test(gives success for values within a distance) + procedure success_tests; + + --%test(gives failure when number is not within distance) + procedure failed_tests; + + --%test(returns well formatted failure message when expectation fails) + procedure fail_for_number_not_within; + + --%test(fails at compile or run time for unsupported data-types ) + procedure fail_at_invalid_argument_types; + +end; +/ diff --git a/test/ut3_user/expectations/test_expectation_anydata.pkb b/test/ut3_user/expectations/test_expectation_anydata.pkb index be666b284..c70d41f9e 100644 --- a/test/ut3_user/expectations/test_expectation_anydata.pkb +++ b/test/ut3_user/expectations/test_expectation_anydata.pkb @@ -957,6 +957,23 @@ Rows: [ 60 differences, showing first 20 ] ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure collection_not_to_contain is + l_actual ut3_tester_helper.test_dummy_object_list; + l_expected ut3_tester_helper.test_dummy_object_list; + begin + --Arrange + select ut3_tester_helper.test_dummy_object( rownum, 'Something2 '||rownum, rownum+100) + bulk collect into l_actual + from dual connect by level <=4; + select ut3_tester_helper.test_dummy_object( rownum, 'Something '||rownum, rownum) + bulk collect into l_expected + from dual connect by level <=2 + order by rownum desc; + --Act + ut3_develop.ut.expect(anydata.convertCollection(l_actual)).not_to_contain(anydata.convertCollection(l_expected)); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure object_to_contain is begin --Arrange @@ -967,7 +984,7 @@ Rows: [ 60 differences, showing first 20 ] ut3_develop.ut.expect(g_test_actual).to_contain(g_test_expected); ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; - + procedure arr_empty_eq_arr_empty_unord is begin --Arrange @@ -991,6 +1008,252 @@ Rows: [ 60 differences, showing first 20 ] ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + + procedure failure_nesting_objects is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') )); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'C', '0') )); + --Act + l_expected_message := q'[%Actual: ut3_tester_helper.test_dummy_nested_object was expected to equal: ut3_tester_helper.test_dummy_nested_object +%Diff: +%Rows: [ 1 differences ] +%Row No. 1 - Actual: 1C0 +%Row No. 1 - Expected: 1B0]'; + ut3_develop.ut.expect(g_test_actual).to_equal(g_test_expected); + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure failure_double_nested_objects is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_double_nested_obj(ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') ),'Test')); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_double_nested_obj(ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'C', '0') ),'Test')); + --Act + l_expected_message := q'[%Actual: ut3_tester_helper.test_dummy_double_nested_obj was expected to equal: ut3_tester_helper.test_dummy_double_nested_obj +%Diff: +%Rows: [ 1 differences ] +%Row No. 1 - Actual: 1A01C0 +%Row No. 1 - Expected: 1A01B0]'; + ut3_develop.ut.expect(g_test_actual).to_equal(g_test_expected); + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure success_nesting_objects is + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') )); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') )); + --Act + ut3_develop.ut.expect( g_test_actual ).to_equal( g_test_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure success_double_nested_objects is + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_double_nested_obj(ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') ),'Test')); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_double_nested_obj(ut3_tester_helper.test_dummy_nested_object(ut3_tester_helper.test_dummy_object(1, 'A', '0'),ut3_tester_helper.test_dummy_object(1, 'B', '0') ),'Test')); + --Act + ut3_develop.ut.expect( g_test_actual ).to_equal( g_test_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure failure_nested_object_list is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + l_actual ut3_tester_helper.test_dummy_object_list; + l_expected ut3_tester_helper.test_dummy_object_list; + begin + --Arrange + select ut3_tester_helper.test_dummy_object( rownum + 1, 'Something '||rownum, rownum) + bulk collect into l_actual + from dual connect by level <=2 + order by rownum desc; + select ut3_tester_helper.test_dummy_object( rownum, 'Something '||rownum, rownum) + bulk collect into l_expected + from dual connect by level <=2 + order by rownum desc; + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object_list(l_actual,'Test')); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object_list(l_expected,'Test')); + --Act + l_expected_message := q'[%Actual: ut3_tester_helper.test_dummy_nested_object_list was expected to equal: ut3_tester_helper.test_dummy_nested_object_list +%Diff: +%Rows: [ 1 differences ] +%Row No. 1 - Actual: 2Something 221Something 11 +%Row No. 1 - Expected: 3Something 222Something 11]'; + ut3_develop.ut.expect(g_test_actual).to_equal(g_test_expected); + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure success_nested_object_list is + l_actual ut3_tester_helper.test_dummy_object_list; + l_expected ut3_tester_helper.test_dummy_object_list; + begin + --Arrange + select ut3_tester_helper.test_dummy_object( rownum , 'Something '||rownum, rownum) + bulk collect into l_actual + from dual connect by level <=2 + order by rownum desc; + select ut3_tester_helper.test_dummy_object( rownum, 'Something '||rownum, rownum) + bulk collect into l_expected + from dual connect by level <=2 + order by rownum desc; + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object_list(l_actual,'Test')); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_dummy_nested_object_list(l_expected,'Test')); + --Act + ut3_develop.ut.expect( g_test_actual ).to_equal( g_test_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure nested_varray_same_data is + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_nested_tab_varray(ut3_tester_helper.t_varray(1)) ); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_nested_tab_varray(ut3_tester_helper.t_varray(1)) ); + --Act + ut3_develop.ut.expect( g_test_actual ).to_equal( g_test_expected ); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure nested_varray_diff_data is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + begin + --Arrange + g_test_expected := anydata.convertObject( ut3_tester_helper.test_nested_tab_varray(ut3_tester_helper.t_varray(1)) ); + g_test_actual := anydata.convertObject( ut3_tester_helper.test_nested_tab_varray(ut3_tester_helper.t_varray(2)) ); + --Act + ut3_develop.ut.expect( g_test_actual ).to_equal( g_test_expected ); + l_expected_message := q'[%Actual: ut3_tester_helper.test_nested_tab_varray was expected to equal: ut3_tester_helper.test_nested_tab_varray +%Diff: +%Rows: [ 1 differences ] +%Row No. 1 - Actual: 2 +%Row No. 1 - Expected: 1]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + procedure user_def_type_null_issue_1098 is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + l_actual ut3_tester_helper.test_dummy_dble_nest_lst_obj; + l_expected ut3_tester_helper.test_dummy_dble_nest_lst_obj; + begin + l_actual:= ut3_tester_helper.test_dummy_dble_nest_lst_obj( + 1, 'North America', + ut3_tester_helper.test_dummy_double_nested_list ( + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list( + ut3_tester_helper.test_dummy_object(1, '100 Broadway', 02474), + ut3_tester_helper.test_dummy_object(2, '200 Indian School Rd', 85016) + ),'USA' + ), + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list(),'USA' + ) + ) + ); + + l_expected := ut3_tester_helper.test_dummy_dble_nest_lst_obj( + 1, + 'North America', + ut3_tester_helper.test_dummy_double_nested_list(ut3_tester_helper.test_dummy_nested_object_list(ut3_tester_helper.test_dummy_object_list( + ut3_tester_helper.test_dummy_object(1, '100 Broadway', 02474), + ut3_tester_helper.test_dummy_object(2, '200 Indian School Rd', 85016) + ), 'USA')) + ); + ut3_develop.ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected)).unordered; + + l_expected_message := q'[%Actual: ut3_tester_helper.test_dummy_dble_nest_lst_obj was expected to equal: ut3_tester_helper.test_dummy_dble_nest_lst_obj +%Diff: +%Rows: [ 2 differences ] +%Extra: 1North America1100 Broadway24742200 Indian School Rd85016USAUSA +%Missing: 1North America1100 Broadway24742200 Indian School Rd85016USA]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + procedure complex_nested_object_success is + l_actual_message varchar2(32767); + l_expected_message varchar2(32767); + l_actual ut3_tester_helper.test_dummy_dble_nest_lst_obj; + l_expected ut3_tester_helper.test_dummy_dble_nest_lst_obj; + begin + l_actual:= ut3_tester_helper.test_dummy_dble_nest_lst_obj( + 1, 'North America', + ut3_tester_helper.test_dummy_double_nested_list ( + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list( + ut3_tester_helper.test_dummy_object(1, '100 Broadway', 02474), + ut3_tester_helper.test_dummy_object(2, '200 Indian School Rd', 85016) + ),'USA' + ), + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list(),'USA' + ) + ) + ); + + l_expected := ut3_tester_helper.test_dummy_dble_nest_lst_obj( + 1, 'North America', + ut3_tester_helper.test_dummy_double_nested_list ( + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list( + ut3_tester_helper.test_dummy_object(1, '100 Broadway', 02474), + ut3_tester_helper.test_dummy_object(2, '200 Indian School Rd', 85016) + ),'USA' + ), + ut3_tester_helper.test_dummy_nested_object_list( + ut3_tester_helper.test_dummy_object_list(),'USA' + ) + ) + ); + ut3_develop.ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected)).unordered; + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + + end; + + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + procedure long_names_object_types is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace type + very_long_type_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it + is object ( + code number(18) + )]'; + execute immediate q'[ + begin + ut3_develop.ut.expect(anydata.convertObject( + very_long_type_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it(1) + )).to_equal(anydata.convertObject( + very_long_type_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it(1) + )); + end;]'; + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + execute immediate 'drop type very_long_type_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it'; + exception + when others then + execute immediate 'drop type very_long_type_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it'; + raise; + end; + $end + end; / \ No newline at end of file diff --git a/test/ut3_user/expectations/test_expectation_anydata.pks b/test/ut3_user/expectations/test_expectation_anydata.pks index a6eeb25fb..54c246bd3 100644 --- a/test/ut3_user/expectations/test_expectation_anydata.pks +++ b/test/ut3_user/expectations/test_expectation_anydata.pks @@ -195,6 +195,9 @@ create or replace package test_expectation_anydata is --%test( Success when anydata collection contains data from another anydata collection) procedure collection_to_contain; + --%test( Success when anydata collection not contains data from another anydata collection) + procedure collection_not_to_contain; + --%test( Success when anydata object contains data from another anydata) procedure object_to_contain; @@ -203,5 +206,41 @@ create or replace package test_expectation_anydata is --%test ( Empty Array not equal array with space ) procedure arr_empty_nqua_arr_e_unord; + + --%test ( Reports diff between not equal nested objects ) + procedure failure_nesting_objects; + + --%test ( Reports diff between not equal double nested objects ) + procedure failure_double_nested_objects; + + --%test (Reports success when comparing identical nested object ) + procedure success_nesting_objects; + + --%test ( Reports success when comparing identical double nested object ) + procedure success_double_nested_objects; + + --%test ( Reports diff between two not equal nested object list ) + procedure failure_nested_object_list; + + --%test ( Reports success when comparing identical nested object list ) + procedure success_nested_object_list; + + --%test ( Reports success when comparing identical nested VARRAYS ) + procedure nested_varray_same_data; + + --%test ( Reports diff between two not equal nested VARRAYS ) + procedure nested_varray_diff_data; + + --%test ( Comparision won't fail on user_defined type that is null as per issue 1098 ) + procedure user_def_type_null_issue_1098; + + --%test ( Reports success when comparing complex nested objects ) + procedure complex_nested_object_success; + + $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then + --%test ( Compares object types with long names - Issue #1235 ) + procedure long_names_object_types; + $end + end; / diff --git a/test/ut3_user/expectations/test_expectations_cursor.pkb b/test/ut3_user/expectations/test_expectations_cursor.pkb index f06132009..a4ef4e8f2 100644 --- a/test/ut3_user/expectations/test_expectations_cursor.pkb +++ b/test/ut3_user/expectations/test_expectations_cursor.pkb @@ -336,7 +336,7 @@ create or replace package body test_expectations_cursor is open l_expected for select 1 as col_1, 2 as col_2 from dual; open l_actual for select 2 as col_2, 1 as col_1 from dual; --Act - ut3_develop.ut.expect( l_actual ).to_equal( l_expected ).unordered_columns; + ut3_develop.ut.expect( l_actual ).to_( ut3_develop.equal( l_expected ).unordered_columns() ); --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; @@ -350,7 +350,7 @@ create or replace package body test_expectations_cursor is open l_expected for select 1 as col_1, 2 as col_2 from dual; open l_actual for select 2 as col_2, 1 as col_1 from dual; --Act - ut3_develop.ut.expect( l_actual ).to_equal( l_expected ).uc; + ut3_develop.ut.expect( l_actual ).to_( ut3_develop.equal( l_expected ).uc() ); --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; @@ -592,7 +592,7 @@ create or replace package body test_expectations_cursor is open l_actual for select rownum as rn, 'a' as "A_Column", 'c' as A_COLUMN, 'x' SOME_COL, 'd' "Some_Col" from dual connect by level < 4; open l_expected for select rownum as rn, 'a' as "A_Column", 'd' as A_COLUMN, 'x' SOME_COL, 'c' "Some_Col" from dual connect by level < 4; --Act - ut3_develop.ut.expect(l_actual).to_equal(l_expected).include('/ROW/RN|//A_Column|//SOME_COL'); + ut3_develop.ut.expect(l_actual).to_( ut3_develop.equal(l_expected).include('/ROW/RN|//A_Column|//SOME_COL') ); --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; @@ -2329,6 +2329,23 @@ Diff:% ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure cursor_not_to_contain2 + as + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + open l_expected for select 'TEST' username, -600 user_id from dual; + + open l_actual for select username, user_id from all_users + union all + select 'TEST1' username, -601 user_id from dual; + + --Act + ut3_develop.ut.expect(l_actual).not_to(ut3_develop.contain(l_expected)); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure cursor_not_to_contain_fail is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2359,6 +2376,37 @@ Diff:% ut.expect(l_actual_message).to_be_like(l_expected_message); end; + + procedure cursor_not_to_contain_fail2 is + l_actual sys_refcursor; + l_expected sys_refcursor; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + --Arrange + open l_expected for select 'TEST' username, -600 user_id from dual; + + open l_actual for select username, user_id from all_users + union all + select 'TEST' username, -600 user_id from dual; + + --Act + ut3_develop.ut.expect(l_actual).not_to(ut3_develop.contain(l_expected)); + --Assert + l_expected_message := q'[%Actual: (refcursor [ count = % ])% +%Data-types:% +%VARCHAR2NUMBER% +%Data:% +%was expected not to contain:(refcursor [ count = 1 ])% +%Data-types:% +%CHARNUMBER% +%Data:% +%TEST-600%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + procedure cursor_not_to_contain_joinby is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2372,7 +2420,21 @@ Diff:% --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; - + + procedure cursor_not_to_contain_joinby2 is + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + --Arrange + open l_actual for select username,rownum * 10 user_id from all_users where rownum < 5; + open l_expected for select username||to_char(rownum) username ,rownum user_id from all_users where rownum < 5; + + --Act + ut3_develop.ut.expect(l_actual).not_to(ut3_develop.contain(l_expected).join_by('USER_ID')); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure not_cont_join_incl_cols_as_lst is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2386,6 +2448,19 @@ Diff:% ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure not_con_join_incl_cols_as_lst2 is + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + --Arrange + open l_actual for select rownum as rn, 'b' as "A_Column", 'c' as A_COLUMN, 'x' SOME_COL, 'd' "Some_Col" from dual a connect by level < 10; + open l_expected for select rownum * 20 rn, 'a' as "A_Column", 'd' as A_COLUMN, 'x' SOME_COL, 'c' "Some_Col" from dual a connect by level < 4; + --Act + ut3_develop.ut.expect(l_actual).not_to(ut3_develop.contain(l_expected).include(ut3_develop.ut_varchar2_list('RN','//A_Column','SOME_COL')).join_by('RN')); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure not_cont_join_excl_cols_as_lst is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2399,6 +2474,19 @@ Diff:% ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + procedure not_con_join_excl_cols_as_lst2 is + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + --Arrange + open l_actual for select rownum as rn, 'a' as "A_Column", 'c' as A_COLUMN, 'y' SOME_COL, 'd' "Some_Col" from dual a connect by level < 10; + open l_expected for select rownum * 20 as rn, 'a' as "A_Column", 'd' as A_COLUMN, 'x' SOME_COL, 'c' "Some_Col" from dual a connect by level < 4; + --Act + ut3_develop.ut.expect(l_actual).not_to(ut3_develop.contain(l_expected).exclude(ut3_develop.ut_varchar2_list('//Some_Col','A_COLUMN')).join_by('RN')); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure to_contain_duplicates is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2440,6 +2528,16 @@ Diff:% ut.expect(l_actual_message).to_be_like(l_expected_message); end; + procedure to_not_contain_fails_1245 is + c1 sys_refcursor; + c2 sys_refcursor; + begin + open c1 for select 'a' as letter from dual union all select 'b' from dual; + open c2 for select 'c' as letter from dual; + ut3_develop.ut.expect(c1).not_to(ut3_develop.contain(c2)); + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + procedure udt_messg_format_eq is l_actual sys_refcursor; l_expected sys_refcursor; @@ -2507,7 +2605,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 % ut3_develop.ut.expect(l_actual).to_equal(l_expected);% +at "UT3_USER.TEST_EXPECTATIONS_CURSOR%", line % ut3_develop.ut.expect(l_actual).to_equal(l_expected);% Check the query and data for errors.'; open l_actual for @@ -2532,7 +2630,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 % ut3_develop.ut.expect(l_actual).to_equal(l_expected);% +at "UT3_USER.TEST_EXPECTATIONS_CURSOR%", line % ut3_develop.ut.expect(l_actual).to_equal(l_expected);% Check the query and data for errors.'; open l_expected for @@ -2852,5 +2950,47 @@ Rows: [ 2 differences ] ); end; + procedure cr_joinby_compare_issue_1293 is + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + --Arrange + open l_expected for + select 'FOO' username, 12 from dual union all + select 'TEST' username, -600 user_id from dual + order by 1 desc; + open l_actual for + select 'FOO' username, 12 from dual union all + select 'TEST' username, -600 user_id from dual union all + -- DUPLICATE!!! + select 'TEST' username, -600 user_id from dual + order by 1 asc; + --Act + ut3_develop.ut.expect(l_actual).to_equal(l_expected).join_by('USERNAME'); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); + end; + + procedure cr_not_joinby_comp_issue_1293 is + l_actual sys_refcursor; + l_expected sys_refcursor; + begin + --Arrange + open l_expected for + select 'FOO' username, 12 from dual union all + select 'TEST' username, -600 user_id from dual + order by 1 desc; + open l_actual for + select 'FOO' username, 12 from dual union all + select 'TEST' username, -600 user_id from dual union all + -- DUPLICATE!!! + select 'TEST' username, -600 user_id from dual + order by 1 asc; + --Act + ut3_develop.ut.expect(l_actual).to_equal(l_expected); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_be_greater_than(0); + end; + end; / diff --git a/test/ut3_user/expectations/test_expectations_cursor.pks b/test/ut3_user/expectations/test_expectations_cursor.pks index 0ed249a49..8cceb5e01 100644 --- a/test/ut3_user/expectations/test_expectations_cursor.pks +++ b/test/ut3_user/expectations/test_expectations_cursor.pks @@ -376,24 +376,42 @@ create or replace package test_expectations_cursor is --%test( Cursor not to contains data from another cursor) procedure cursor_not_to_contain; + --%test( Cursor not_to[contain] data from another cursor) + procedure cursor_not_to_contain2; + --%test( Cursor fail not to contains data from another cursor) procedure cursor_not_to_contain_fail; - + + --%test( Cursor fail not_to[contain] data from another cursor) + procedure cursor_not_to_contain_fail2; + --%test( Cursor not contains data from another cursor with joinby clause) procedure cursor_not_to_contain_joinby; + --%test( Cursor not_to[contain] data from another cursor with joinby clause) + procedure cursor_not_to_contain_joinby2; + --%test(Cursor not contains data with of columns to include and join by value) procedure not_cont_join_incl_cols_as_lst; + --%test(Cursor not_to[contain] data with of columns to include and join by value) + procedure not_con_join_incl_cols_as_lst2; + --%test(Cursor not contains data with of columns to exclude and join by value) procedure not_cont_join_excl_cols_as_lst; + --%test(Cursor not_to[contain] data with of columns to exclude and join by value) + procedure not_con_join_excl_cols_as_lst2; + --%test(Cursor to contain duplicates) procedure to_contain_duplicates; --%test(Cursor to contain duplicates fail) procedure to_contain_duplicates_fail; - + + --%test(Cursor using not_to[contain] fails #1245) + procedure to_not_contain_fails_1245; + --%test(Display a message with a uer defined type with only type name not structure on equal) procedure udt_messg_format_eq; @@ -467,5 +485,11 @@ create or replace package test_expectations_cursor is --%test( Multiple failures reported correctly - Issue #998 ) procedure multiple_cursor_expectations; + --%test( Compares cursors with duplicate rows using join by - Issue #1293 ) + procedure cr_joinby_compare_issue_1293; + + --%test( Compares cursors with duplicate rows - Issue #1293 ) + procedure cr_not_joinby_comp_issue_1293; + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pkb b/test/ut3_user/expectations/test_expectations_json.pkb index 670502c0d..2c501d71f 100644 --- a/test/ut3_user/expectations/test_expectations_json.pkb +++ b/test/ut3_user/expectations/test_expectations_json.pkb @@ -49,7 +49,7 @@ create or replace package body test_expectations_json is }'); --Act - ut3_develop.ut.expect( l_actual ).to_equal( l_actual ); + ut3_develop.ut.expect( l_actual ).to_( ut3_develop.equal( l_actual ) ); --Assert ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; @@ -62,15 +62,13 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); - l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); + l_actual := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + l_expected := json_element_t.parse('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); --Act ut3_develop.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Diff: 20 differences found -%3 incorrect types, 4 unequal values, 13 missing properties -%Missing property: "Alexander Skarsg?rd" on path: $ + l_expected_message := q'[%Missing property: "Alexander Skarsg?rd" on path: $ %Extra property: "Alexander Skarsgard" on path: $ %Missing property: "Alice Farmer" on path: $ %Extra property: "Clarke Peters" on path: $ @@ -93,6 +91,10 @@ create or replace package body test_expectations_json is l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); + ut.expect(l_actual_message).to_be_like('%Diff: 20 differences found%'); + ut.expect(l_actual_message).to_be_like('%13 missing properties%'); + ut.expect(l_actual_message).to_be_like('%4 unequal values%'); + ut.expect(l_actual_message).to_be_like('%3 incorrect types%'); end; procedure null_json_variable @@ -385,7 +387,7 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := json_object_t.parse(' { + l_actual := json_object_t.parse(' { "Actors": [ { "name": "Tom Cruise", @@ -422,7 +424,7 @@ create or replace package body test_expectations_json is ] }' ); - l_actual := json_object_t.parse(' { + l_expected := json_object_t.parse(' { "Actors": { "name": "Krzystof Jarzyna", @@ -442,8 +444,8 @@ create or replace package body test_expectations_json is --Act - ut3_develop.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors.children'))).to_equal(json_array_t(json_query(l_expected - .stringify,'$.Actors[1].children'))); + ut3_develop.ut.expect(json_array_t(json_query(l_actual.stringify,'$.Actors[1].children'))).to_equal(json_array_t(json_query(l_expected + .stringify,'$.Actors.children'))); --Assert l_expected_message := q'[%Actual: json was expected to equal: json %Diff: 1 differences found @@ -1053,7 +1055,7 @@ create or replace package body test_expectations_json is l_actual_message varchar2(32767); begin -- Arrange - l_expected := json_element_t.parse('[ + l_actual := json_element_t.parse('[ { "_id": "5ce6ec46cb9977b050f15d97", "index": 0, @@ -1280,7 +1282,7 @@ create or replace package body test_expectations_json is "favoriteFruit": "strawberry" } ]'); - l_actual := json_element_t.parse('[ + l_expected := json_element_t.parse('[ { "_id": "5ce6ec6660565269b16cf836", "index": 0, @@ -1556,9 +1558,7 @@ create or replace package body test_expectations_json is --Act ut3_develop.ut.expect( l_actual ).to_equal( l_expected ); --Assert - l_expected_message := q'[%Diff: 133 differences found, showing first 20 -%132 unequal values, 1 missing properties -%Extra property: object on path: $[5] + l_expected_message := q'[%Extra property: object on path: $[5] %Actual value: "5ce6ec46cb9977b050f15d97" was expected to be: "5ce6ec6660565269b16cf836" on path: $[0]."_id" %Actual value: "5ce6ec469ba57bef5c421021" was expected to be: "5ce6ec66383ddbf3c400e3ed" on path: $[1]."_id" %Actual value: "5ce6ec4632328a654d592cb6" was expected to be: "5ce6ec6600fb7aaee2d1243e" on path: $[3]."_id" @@ -1582,6 +1582,9 @@ create or replace package body test_expectations_json is l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); --Assert ut.expect(l_actual_message).to_be_like(l_expected_message); + ut.expect(l_actual_message).to_be_like('%Diff: 133 differences found, showing first 20%'); + ut.expect(l_actual_message).to_be_like('%1 missing properties%'); + ut.expect(l_actual_message).to_be_like('%132 unequal values%'); end; procedure check_json_objects is @@ -1604,6 +1607,201 @@ create or replace package body test_expectations_json is ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); end; + $if dbms_db_version.version >= 21 $then + + procedure success_on_same_data_njson + as + l_actual json; + begin + -- Arrange + l_actual := json(' { + "Actors": [ + { + "name": "Tom Cruise", + "age": 56, + "Born At": "Syracuse, NY", + "Birthdate": "July 3, 1962", + "photo": "https://jsonformatter.org/img/tom-cruise.jpg", + "wife": null, + "weight": 67.5, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Suri", + "Isabella Jane", + "Connor" + ] + }, + { + "name": "Robert Downey Jr.", + "age": 53, + "Born At": "New York City, NY", + "Birthdate": "April 4, 1965", + "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg", + "wife": "Susan Downey", + "weight": 77.1, + "hasChildren": true, + "hasGreyHair": false, + "children": [ + "Indio Falconer", + "Avri Roel", + "Exton Elias" + ] + } + ] + }'); + + --Act + ut3_develop.ut.expect( l_actual ).to_equal( l_actual ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure fail_on_diff_data_njson + as + l_expected json; + l_actual json; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + l_expected := json('{"Aidan Gillen": {"array": ["Game of Thron\"es","The Wire"],"string": "some string","int": 2,"aboolean": true, "boolean": true,"object": {"foo": "bar","object1": {"new prop1": "new prop value"},"object2": {"new prop1": "new prop value"},"object3": {"new prop1": "new prop value"},"object4": {"new prop1": "new prop value"}}},"Amy Ryan": {"one": "In Treatment","two": "The Wire"},"Annie Fitzgerald": ["Big Love","True Blood"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsgard": ["Generation Kill","True Blood"], "Clarke Peters": null}'); + + --Act + ut3_develop.ut.expect( l_actual ).to_equal( l_expected ); + --Assert + l_expected_message := q'[%Missing property: "Alexander Skarsg?rd" on path: $ +%Extra property: "Alexander Skarsgard" on path: $ +%Missing property: "Alice Farmer" on path: $ +%Extra property: "Clarke Peters" on path: $ +%Extra property: "one" on path: $."Amy Ryan" +%Missing property: "The Sopranos" on path: $."Annie Fitzgerald"[2] +%Extra property: "two" on path: $."Amy Ryan" +%Missing property: "Oz" on path: $."Annie Fitzgerald"[3] +%Missing property: "otherint" on path: $."Aidan Gillen" +%Extra property: "object1" on path: $."Aidan Gillen"."object" +%Extra property: "object2" on path: $."Aidan Gillen"."object" +%Extra property: "object3" on path: $."Aidan Gillen"."object" +%Extra property: "object4" on path: $."Aidan Gillen"."object" +%Actual type: 'array' was expected to be: 'object' on path: $."Amy Ryan" +%Actual type: 'string' was expected to be: 'number' on path: $."Aidan Gillen"."int" +%Actual type: 'string' was expected to be: 'boolean' on path: $."Aidan Gillen"."aboolean" +%Actual value: "True Blood" was expected to be: "Big Love" on path: $."Annie Fitzgerald"[0] +%Actual value: "Big Love" was expected to be: "True Blood" on path: $."Annie Fitzgerald"[1] +%Actual value: FALSE was expected to be: TRUE on path: $."Aidan Gillen"."boolean" +%Actual value: "Game of Thrones" was expected to be: "Game of Thron\"es" on path: $."Aidan Gillen"."array"[0]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + ut.expect(l_actual_message).to_be_like('%Diff: 20 differences found%'); + ut.expect(l_actual_message).to_be_like('%13 missing properties%'); + ut.expect(l_actual_message).to_be_like('%4 unequal values%'); + ut.expect(l_actual_message).to_be_like('%3 incorrect types%'); + end; + + --Please note that by the looks of things the call to json() results in null value being returned. + procedure null_json_variable_njson + as + l_expected json ; + begin + -- Arrange + l_expected := cast (null as json ); + + --Act + ut3_develop.ut.expect( l_expected ).to_be_null; + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure to_have_count_njson as + l_actual json; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3_develop.ut.expect( l_actual ).to_have_count( 6 ); + + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + + end; + + procedure fail_to_have_count_njson + as + l_actual json; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3_develop.ut.expect( l_actual ).to_have_count( 2 ); + --Assert + l_expected_message := q'[%Actual: (json [ count = 6 ]) was expected to have [ count = 2 ]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + + end; + + procedure not_to_have_count_njson + as + l_actual json; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3_develop.ut.expect( l_actual ).not_to_have_count( 7 ); + --Assert + ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0); + end; + + procedure fail_not_to_have_count_njson + as + l_actual json; + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + -- Arrange + l_actual := json('{"Aidan Gillen": {"array": ["Game of Thrones","The Wire"],"string": "some string","int": "2","otherint": 4, "aboolean": "true", "boolean": false,"object": {"foo": "bar"}},"Amy Ryan": ["In Treatment","The Wire"],"Annie Fitzgerald": ["True Blood","Big Love","The Sopranos","Oz"],"Anwan Glover": ["Treme","The Wire"],"Alexander Skarsg?rd": ["Generation Kill","True Blood"],"Alice Farmer": ["The Corner","Oz","The Wire"]}'); + + --Act + ut3_develop.ut.expect( l_actual ).not_to_have_count( 6 ); + --Assert + l_expected_message := q'[%Actual: json [ count = 6 ] was expected not to have [ count = 6 ]%]'; + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + $end + + procedure p_1113_reg_exp_chg_with_act is + l_expected_message varchar2(32767); + l_actual_message varchar2(32767); + begin + ut3_develop.ut.expect(json_element_t.parse('{"a":"value a"}')).to_equal(json_element_t.parse('{"a":"value b"}')); + + --Assert + l_expected_message := q'[%Actual: json was expected to equal: json +%Diff: 1 differences found +%1 unequal values +%Actual value: "value a" was expected to be: "value b" on path: $."a"%]'; + + l_actual_message := ut3_tester_helper.main_helper.get_failed_expectations(1); + --Assert + ut.expect(l_actual_message).to_be_like(l_expected_message); + end; + + end; / diff --git a/test/ut3_user/expectations/test_expectations_json.pks b/test/ut3_user/expectations/test_expectations_json.pks index 64dff42e8..410c3050b 100644 --- a/test/ut3_user/expectations/test_expectations_json.pks +++ b/test/ut3_user/expectations/test_expectations_json.pks @@ -39,13 +39,13 @@ create or replace package test_expectations_json is --%test( Json object to have count ) procedure to_have_count; - --%test( Fail Json object to have count) + --%test( Fail Json object to have count) procedure fail_to_have_count; --%test( Json object not to have count) procedure not_to_have_count; - --%test( Fail Json object not to have count) + --%test( Fail Json object not to have count) procedure fail_not_to_have_count; --%test( Json object to have count on array) @@ -74,6 +74,34 @@ create or replace package test_expectations_json is --%test( Compare two json arrays ) procedure check_json_arrays; + + $if dbms_db_version.version >= 21 $then + + --%test(Gives success for identical data using native json for 21c and above) + procedure success_on_same_data_njson; + + --%test(Gives failure for different data using native json for 21c and above) + procedure fail_on_diff_data_njson; + + --%test( Json variable is null using native json for 21c and above) + procedure null_json_variable_njson; + + --%test( Json object to have count using native json for 21c and above) + procedure to_have_count_njson; + + --%test( Fail Json object to have count using native json for 21c and above) + procedure fail_to_have_count_njson; + + --%test( Json object not to have count using native json for 21c and above) + procedure not_to_have_count_njson; + + --%test( Fail Json object not to have count using native json for 21c and above) + procedure fail_not_to_have_count_njson; + + $end + --%test( Regression scenario tests for issue #1113 where the expected has been switched with actual) + procedure p_1113_reg_exp_chg_with_act; + end; / diff --git a/test/ut3_user/reporters.pkb b/test/ut3_user/reporters.pkb index edd26189c..031683615 100644 --- a/test/ut3_user/reporters.pkb +++ b/test/ut3_user/reporters.pkb @@ -35,9 +35,13 @@ as procedure erroring_test; --%test(a disabled test) - --%disabled + --%disabled(Disabled for testing purpose) procedure disabled_test; + --%test(a disabled test with no reason) + --%disabled + procedure disabled_test_no_reason; + --%aftereach procedure aftereach; @@ -86,11 +90,10 @@ as procedure erroring_test is - l_variable integer; + l_integer_variable integer; begin dbms_output.put_line(''); - l_variable := 'a string'; - ut3_develop.ut.expect(l_variable).to_equal(1); + l_integer_variable := 'a string'; end; procedure disabled_test @@ -100,6 +103,13 @@ as ut3_develop.ut.expect(1,'this should not execute').to_equal(1); end; + procedure disabled_test_no_reason + is + begin + dbms_output.put_line(''); + ut3_develop.ut.expect(1,'this should not execute').to_equal(1); + end; + procedure beforeall is begin dbms_output.put_line(''); @@ -177,7 +187,7 @@ end;]'; --Assert ut.expect(l_actual).to_be_like('%' (varchar2)%]' - ||q'[at "UT3$USER#.CHECK_FAIL_ESCAPE%", line % ut3_develop.ut.expect('test').to_equal('');]' + ||q'[at "UT3_USER.CHECK_FAIL_ESCAPE%", line % ut3_develop.ut.expect('test').to_equal('');]' ||'%]]>%' ); end; diff --git a/test/ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pkb b/test/ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pkb index a18409463..ad6c9dbb4 100644 --- a/test/ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pkb +++ b/test/ut3_user/reporters/test_coverage/test_cov_cobertura_reporter.pkb @@ -20,14 +20,14 @@ create or replace package body test_cov_cobertura_reporter is l_expected := q'[ - + ]'||l_file_path||q'[ - + - + ]'||l_block_cov||q'[ diff --git a/test/ut3_user/reporters/test_coverage/test_coverage_standalone.pkb b/test/ut3_user/reporters/test_coverage/test_coverage_standalone.pkb index d94108a32..90cfb484e 100644 --- a/test/ut3_user/reporters/test_coverage/test_coverage_standalone.pkb +++ b/test/ut3_user/reporters/test_coverage/test_coverage_standalone.pkb @@ -15,14 +15,14 @@ create or replace package body test_coverage_standalone is l_expected := q'[ - + ]'||l_file_path||q'[ - + - + ]'||l_block_cov||q'[ diff --git a/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pkb b/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pkb deleted file mode 100644 index e3806cd97..000000000 --- a/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pkb +++ /dev/null @@ -1,77 +0,0 @@ -create or replace package body test_coveralls_reporter is - - procedure report_on_file is - l_expected clob; - l_actual clob; - l_file_path varchar2(250); - begin - --Arrange - l_file_path := 'test/ut3_develop.'||ut3_tester_helper.coverage_helper.covered_package_name||'.pkb'; - l_expected := q'[{"source_files":[ -{ "name": "]'||l_file_path||q'[", -"coverage": [ -null, -null, -null, -3, -null, -0 -] -} -]} - ]'; - l_actual := - ut3_tester_helper.coverage_helper.run_tests_as_job( - q'[ - ut3_develop.ut.run( - a_path => 'ut3_develop.test_dummy_coverage', - a_reporter => ut3_develop.ut_coveralls_reporter( ), - a_source_files => ut3_develop.ut_varchar2_list( ']'||l_file_path||q'[' ), - a_test_files => ut3_develop.ut_varchar2_list( ) - ) - ]' - ); - --Assert - ut.expect(l_actual).to_equal(l_expected); - end; - - procedure report_zero_coverage is - l_expected clob; - l_actual clob; - begin - --Arrange - l_expected := q'[{"source_files":[ -{ "name": "package body ut3_develop.]'||ut3_tester_helper.coverage_helper.covered_package_name||q'[", -"coverage": [ -0, -0, -0, -0, -0, -0, -0, -0, -0 -] -} -]} - ]'; - - --Act - l_actual := - ut3_tester_helper.coverage_helper.run_tests_as_job( - q'[ - ut3_develop.ut.run( - 'ut3_develop.test_dummy_coverage.zero_coverage', - ut3_develop.ut_coveralls_reporter(), - a_include_objects => ut3_develop.ut_varchar2_list('UT3_DEVELOP.]'||ut3_tester_helper.coverage_helper.covered_package_name||q'[') - ) - ]' - ); - --Assert - ut.expect(l_actual).to_equal(l_expected); - - end; - -end; -/ diff --git a/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pks b/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pks deleted file mode 100644 index d958b8f12..000000000 --- a/test/ut3_user/reporters/test_coverage/test_coveralls_reporter.pks +++ /dev/null @@ -1,13 +0,0 @@ -create or replace package test_coveralls_reporter is - - --%suite(ut_coveralls_reporter) - --%suitepath(utplsql.test_user.reporters.test_coverage) - - --%test(reports on a project file mapped to database object) - procedure report_on_file; - - --%test(reports zero coverage on each line of non-executed database object) - procedure report_zero_coverage; - -end; -/ diff --git a/test/ut3_user/reporters/test_coverage/test_extended_coverage.pkb b/test/ut3_user/reporters/test_coverage/test_extended_coverage.pkb index ec95c0bfb..d7f1b61d7 100644 --- a/test/ut3_user/reporters/test_coverage/test_extended_coverage.pkb +++ b/test/ut3_user/reporters/test_coverage/test_extended_coverage.pkb @@ -83,5 +83,236 @@ create or replace package body test_extended_coverage is ut.expect(l_actual).to_be_like(l_expected); end; + procedure coverage_with_dbms_stats is + l_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%' || + '%%'; + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_path => 'ut3_develop.test_stats', + a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), + a_coverage_schemes => ut3_develop.ut_varchar2_list( 'ut3_develop' ), + a_include_objects => ut3_develop.ut_varchar2_list('stats') + ) + ]' + ); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + end; + + procedure coverage_regex_include_schema is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%' || + '%%'; + l_not_expected := '%' || + '%%'; + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_paths => ut3_develop.ut_varchar2_list('ut3_develop.test_regex_dummy_cov', 'ut3_tester_helper.test_regex_dummy_cov'), + a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), + a_include_schema_expr => '^ut3_tester_hel.*', + a_include_objects => ut3_develop.ut_varchar2_list( 'ut3_develop.regex_dummy_cov' ) + ) + ]' + ); + + --Assert + --The below is a workaround for problem with large CLOB like comparison on 11g XE db. + ut.expect(to_char(substr(l_actual,instr(l_actual,''),2000))).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + ut.expect(l_actual).not_to_be_like('%ut3_tester_helper.test_regex_dummy_cov%'); + end; + + procedure coverage_regex_include_object is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%' || + '%%'; + l_not_expected := '%' || + '%%'; + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_paths => ut3_develop.ut_varchar2_list('ut3_develop.test_regex_dummy_cov', 'ut3_develop.test_regex123_dummy_cov'), + a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), + a_include_object_expr => 'regex123', + a_include_objects => ut3_develop.ut_varchar2_list( 'ut3_develop.regex_dummy_cov' ) + ) + ]' + ); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure coverage_regex_exclude_schema is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%' || + '%%'; + l_not_expected := '%' || + '%%'; + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_paths => ut3_develop.ut_varchar2_list('ut3_develop.test_regex_dummy_cov', 'ut3_tester_helper.test_regex_dummy_cov'), + a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), + a_exclude_schema_expr => '^ut3_tester', + a_exclude_objects => ut3_develop.ut_varchar2_list( 'ut3_develop.regex_dummy_cov' ) + ) + ]' + ); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure coverage_regex_exclude_object is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%' || + '%%'; + l_not_expected := '%' || + '%%'; + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_paths => ut3_develop.ut_varchar2_list('ut3_develop.test_regex_dummy_cov', 'ut3_develop.test_regex123_dummy_cov'), + a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), + a_exclude_object_expr => 'regex123', + a_exclude_objects => ut3_develop.ut_varchar2_list( 'ut3_develop.regex_dummy_cov' ) + ) + ]' + ); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure cove_rgx_inc_schema_norun is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%UT3_DEVELOP.REGEX_DUMMY_COV%'; + l_not_expected := '%UT3_TESTER_HELPER.REGEX123_DUMMY_COV%'; + --Act + l_actual := ut3_tester_helper.coverage_helper.gather_coverage_on_coverage( + q'[ut3_develop.ut_coverage_options( + coverage_run_id => l_coverage_run_id, + include_schema_expr => '^ut3_develop' + )]'); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure cove_rgx_inc_object_norun is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%UT3_DEVELOP.REGEX123_DUMMY_COV%'; + l_not_expected := '%UT3_DEVELOP.REGEX_DUMMY_COV%'; + --Act + l_actual := ut3_tester_helper.coverage_helper.gather_coverage_on_coverage( + q'[ut3_develop.ut_coverage_options( + coverage_run_id => l_coverage_run_id, + include_object_expr => 'regex123' + )]'); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure cove_rgx_exc_schema_norun is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%UT3_DEVELOP.REGEX_DUMMY_COV%'; + l_not_expected := '%UT3_TESTER_HELPER.REGEX_DUMMY_COV%'; + --Act + l_actual := ut3_tester_helper.coverage_helper.gather_coverage_on_coverage( + q'[ut3_develop.ut_coverage_options( + coverage_run_id => l_coverage_run_id, + schema_names => ut3_develop.ut_varchar2_rows( 'UT3_DEVELOP','UT3_TESTER_HELPER' ), + exclude_schema_expr => '^ut3_tester', + exclude_objects => ut3_develop.ut_varchar2_rows( 'ut3_develop.regex_dummy_cov' ) + )]'); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure cove_rgx_exc_object_norun is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%UT3_DEVELOP.REGEX_DUMMY_COV%'; + l_not_expected := '%UT3_DEVELOP.REGEX123_DUMMY_COV%'; + --Act + l_actual := ut3_tester_helper.coverage_helper.gather_coverage_on_coverage( + q'[ut3_develop.ut_coverage_options( + coverage_run_id => l_coverage_run_id, + schema_names => ut3_develop.ut_varchar2_rows( 'UT3_DEVELOP' ), + exclude_object_expr => 'regex123', + exclude_objects => ut3_develop.ut_varchar2_rows( 'ut3_develop.regex_dummy_cov' ) + )]'); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like(l_not_expected); + end; + + procedure cov_file_mapping_no_utrun is + l_expected clob; + l_not_expected clob; + l_actual clob; + begin + --Arrange + l_expected := '%UT3_DEVELOP.REGEX_DUMMY_COV%'; + --Act + l_actual := ut3_tester_helper.coverage_helper.gather_coverage_on_coverage( + q'[ut3_develop.ut_coverage_options( + coverage_run_id => l_coverage_run_id, + file_mappings => ut3_develop.ut_file_mappings( ut3_develop.ut_file_mapping('C:\tests\helpers\core.pkb','UT3_DEVELOP','REGEX_DUMMY_COV','PACKAGE BODY')) + )]'); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + end; + end; / diff --git a/test/ut3_user/reporters/test_coverage/test_extended_coverage.pks b/test/ut3_user/reporters/test_coverage/test_extended_coverage.pks index 600a87e16..82b19fbed 100644 --- a/test/ut3_user/reporters/test_coverage/test_extended_coverage.pks +++ b/test/ut3_user/reporters/test_coverage/test_extended_coverage.pks @@ -11,6 +11,56 @@ create or replace package test_extended_coverage is --%test(Coverage is gathered for specified file - extended coverage type) procedure coverage_for_file; - + + --%beforetest(ut3_tester_helper.coverage_helper.create_cov_with_dbms_stats) + --%aftertest(ut3_tester_helper.coverage_helper.drop_cov_with_dbms_stats) + --%tags(#1097,#1094) + --%test(Extended coverage does not fail the test run then tested code calls DBMS_STATS) + procedure coverage_with_dbms_stats; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with schema regex include with ut_run) + procedure coverage_regex_include_schema; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with object regex include with ut_run) + procedure coverage_regex_include_object; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with schema regex exclude with ut_run) + procedure coverage_regex_exclude_schema; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with object regex exclude with ut_run) + procedure coverage_regex_exclude_object; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with schema regex include without ut_run) + procedure cove_rgx_inc_schema_norun; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with object regex include without ut_run) + procedure cove_rgx_inc_object_norun; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with schema regex exclude without ut_run) + procedure cove_rgx_exc_schema_norun; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for objects with object regex exclude without ut_run) + procedure cove_rgx_exc_object_norun; + + --%beforetest(ut3_tester_helper.coverage_helper.create_regex_dummy_cov) + --%aftertest(ut3_tester_helper.coverage_helper.drop_regex_dummy_cov) + --%test(Collect coverage for file mapping without ut_run) + procedure cov_file_mapping_no_utrun; end; / diff --git a/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pkb b/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pkb index d1645acae..2f2200116 100644 --- a/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pkb +++ b/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pkb @@ -32,5 +32,70 @@ create or replace package body test_html_coverage_reporter is ut.expect(l_actual).to_be_like(l_expected); end; + procedure setup_long_lines is + pragma autonomous_transaction; + begin + + execute immediate q'[create or replace type string_array is table of varchar2(5 char);]'; + execute immediate q'[ + create or replace function f return integer is + l_string_array string_array; + l_count integer; + begin + -- line is 1912 chars long, 1911 characters seem to be the max. line length that works (@formatter:off) + l_string_array := string_array('aahed', 'aalii', 'aargh', 'aarti', 'abaca', 'abaci', 'abacs', 'abaft', 'abaka', 'abamp', 'aband', 'abash', 'abask', 'abaya', 'abbas', 'abbed', 'abbes', 'abcee', 'abeam', 'abear', 'abele', 'abers', 'abets', 'abies', 'abler', 'ables', 'ablet', 'ablow', 'abmho', 'abohm', 'aboil', 'aboma', 'aboon', 'abord', 'abore', 'abram', 'abray', 'abrim', 'abrin', 'abris', 'absey', 'absit', 'abuna', 'abune', 'abuts', 'abuzz', 'abyes', 'abysm', 'acais', 'acari', 'accas', 'accoy', 'acerb', 'acers', 'aceta', 'achar', 'ached', 'aches', 'achoo', 'acids', 'acidy', 'acing', 'acini', 'ackee', 'acker', 'acmes', 'acmic', 'acned', 'acnes', 'acock', 'acold', 'acred', 'acres', 'acros', 'acted', 'actin', 'acton', 'acyls', 'adaws', 'adays', 'adbot', 'addax', 'added', 'adder', 'addio', 'addle', 'adeem', 'adhan', 'adieu', 'adios', 'adits', 'adman', 'admen', 'admix', 'adobo', 'adown', 'adoze', 'adrad', 'adred', 'adsum', 'aduki', 'adunc', 'adust', 'advew', 'adyta', 'adzed', 'adzes', 'aecia', 'aedes', 'aegis', 'aeons', 'aerie', 'aeros', 'aesir', 'afald', 'afara', 'afars', 'afear', 'aflaj', 'afore', 'afrit', 'afros', 'agama', 'agami', 'agars', 'agast', 'agave', 'agaze', 'agene', 'agers', 'agger', 'aggie', 'aggri', 'aggro', 'aggry', 'aghas', 'agila', 'agios', 'agism', 'agist', 'agita', 'aglee', 'aglet', 'agley', 'agloo', 'aglus', 'agmas', 'agoge', 'agone', 'agons', 'agood', 'agora', 'agria', 'agrin', 'agros', 'agued', 'agues', 'aguna', 'aguti', 'aheap', 'ahent', 'ahigh', 'ahind', 'ahing', 'ahint', 'ahold', 'ahull', 'ahuru', 'aidas', 'aided', 'aides', 'aidoi', 'aidos', 'aiery', 'aigas', 'aight', 'ailed', 'aimed', 'aimer', 'ainee', 'ainga', 'aioli', 'aired', 'airer', 'airns', 'airth', 'airts', 'aitch', 'aitus', 'aiver', 'aiyee', 'aizle', 'ajies', 'ajiva', 'ajuga', 'ajwan', 'akees', 'akela', 'akene', 'aking', 'akita', 'akkas', 'alaap', 'alack', 'alamo', 'aland', 'alane', 'alang', 'a'); + select count(*) into l_count from table(l_string_array); + return l_count; + end;]'; + + execute immediate q'[ + create or replace package test_f is + --%suite + + --%test + procedure fail_ut_coverage_html_reporter; + end;]'; + + execute immediate q'[ + create or replace package body test_f is + procedure fail_ut_coverage_html_reporter is + begin + ut3_develop.ut.expect(f()).to_be_greater_or_equal(1); + end; + end; + ]'; + end; + + procedure cleanup_long_lines is + pragma autonomous_transaction; + begin + execute immediate 'drop package test_f'; + execute immediate 'drop function f'; + execute immediate 'drop type string_array force'; + end; + + procedure report_long_lines is + l_expected varchar2(32767); + l_actual clob; + l_name varchar2(250); + begin + --Arrange + l_expected := '%l_string_array := string_array%'; + + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + q'[ + ut3_develop.ut.run( + a_path => 'ut3_user.test_f', + a_reporter=> ut3_develop.ut_coverage_html_reporter(), + a_include_objects => ut3_develop.ut_varchar2_list( 'UT3_USER.F' ) + ) + ]' + ); + --Assert + ut.expect(l_actual).to_be_like(l_expected); + end; + + end test_html_coverage_reporter; / diff --git a/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pks b/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pks index 629d41793..a11f3e912 100644 --- a/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pks +++ b/test/ut3_user/reporters/test_coverage/test_html_coverage_reporter.pks @@ -6,5 +6,13 @@ create or replace package test_html_coverage_reporter is --%test(reports on a project file mapped to database object in extended profiler coverage) procedure report_on_file; + procedure setup_long_lines; + procedure cleanup_long_lines; + + --%test(reports on lines exceeding 4000 chars after conversion to XML) + --%beforetest(setup_long_lines) + --%aftertest(cleanup_long_lines) + procedure report_long_lines; + end test_html_coverage_reporter; / diff --git a/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb b/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb index 81a9a4c58..4b06f725f 100644 --- a/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb +++ b/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb @@ -66,6 +66,7 @@ create or replace package body test_proftab_coverage is ); --Assert ut.expect(l_actual).to_be_like(l_expected); + ut.expect(l_actual).not_to_be_like('%%'); end; procedure coverage_for_file is @@ -137,14 +138,14 @@ create or replace package body test_proftab_coverage is --Act insert into test_results - select * + select rownum, x.* from table( ut3_develop.ut.run( a_path => 'ut3_develop:coverage_testing', a_reporter=> ut3_develop.ut_coverage_sonar_reporter( ), a_include_objects => ut3_develop.ut_varchar2_list( 'ut3_develop.{p}' ) ) - ); + ) x; commit; end; ]'; @@ -164,7 +165,7 @@ create or replace package body test_proftab_coverage is l_expected := q'[ - + package body ut3_develop.{p} @@ -206,5 +207,26 @@ create or replace package body test_proftab_coverage is ut.expect(l_actual).to_be_like(coverage_helper.substitute_covered_package(l_expected)); end; + procedure no_issue_with_duplicate_source is + l_actual clob; + begin + --Act + l_actual := + ut3_tester_helper.coverage_helper.run_tests_as_job( + coverage_helper.substitute_covered_package( + q'[ + ut3_develop.ut.run( + 'ut3_develop.test_dummy_coverage.zero_coverage', + ut3_develop.ut_coverage_sonar_reporter(), + a_include_schema_expr =>'SYS' + ) + ]' + ) + ); + + --Running above line is successful if there is no exception thrown due to duplicate lines in coverage + ut.expect(l_actual).not_to_be_null(); + end; + end; / diff --git a/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pks b/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pks index 740862f86..fb1d0cfe3 100644 --- a/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pks +++ b/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pks @@ -28,5 +28,8 @@ create or replace package test_proftab_coverage is --%test(reports zero coverage on each line of non-executed database object - Issue #917) procedure report_zero_coverage; + --%test(Allows reporting coverage on objects across SYS or MDSYS without errors - Issue #1323 ) + procedure no_issue_with_duplicate_source; + end; / diff --git a/test/ut3_user/reporters/test_debug_reporter.pkb b/test/ut3_user/reporters/test_debug_reporter.pkb index ed7e14fcf..a022b5af8 100644 --- a/test/ut3_user/reporters/test_debug_reporter.pkb +++ b/test/ut3_user/reporters/test_debug_reporter.pkb @@ -21,7 +21,7 @@ create or replace package body test_debug_reporter as begin l_expected := '\s+' || '(\s+' || - '[0-9\-]+T[0-9:\.]+<\/TIMESTAMP>\s+' || + ' [0-9\-]+T[0-9:\.]+<\/TIMESTAMP>\s+' || '[0-9 \+:\.]+<\/TIME_FROM_START>\s+' || '[0-9 \+:\.]+<\/TIME_FROM_PREVIOUS>\s+' || '\w+<\/EVENT_NAME>\s+' || diff --git a/test/ut3_user/reporters/test_documentation_reporter.pkb b/test/ut3_user/reporters/test_documentation_reporter.pkb index 456fa86f9..d9c5d48c8 100644 --- a/test/ut3_user/reporters/test_documentation_reporter.pkb +++ b/test/ut3_user/reporters/test_documentation_reporter.pkb @@ -1 +1,59 @@ -create or replace package body test_documentation_reporter as procedure report_produces_expected_out is l_results ut3_develop.ut_varchar2_list; l_actual clob; l_expected varchar2(32767):=q'[%org utplsql tests helpers A suite for testing different outcomes from reporters A description of some context passing_test [% sec] a test with failing assertion [% sec] (FAILED - 1) a test raising unhandled exception [% sec] (FAILED - 2) a disabled test [0 sec] (DISABLED) % Failures: % 1) failing_test "Fails as values are different" Actual: 'number [1] ' (varchar2) was expected to equal: 'number [2] ' (varchar2)% at "UT3$USER#.TEST_REPORTERS%", line 36 ut3_develop.ut.expect('number [1] ','Fails as values are different').to_equal('number [2] '); % % 2) erroring_test ORA-06502: PL/SQL: numeric or value error: character to number conversion error ORA-06512: at "UT3$USER#.TEST_REPORTERS", line 44% ORA-06512: at line 6 Finished in % seconds 4 tests, 1 failed, 1 errored, 1 disabled, 0 warning(s)%]'; begin select * bulk collect into l_results from table( ut3_develop.ut.run( 'test_reporters', ut3_develop.ut_documentation_reporter() ) ); l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results); ut.expect(l_actual).to_be_like(l_expected); end; end; / \ No newline at end of file +create or replace package body test_documentation_reporter as + + procedure report_produces_expected_out is + l_results ut3_develop.ut_varchar2_list; + l_actual clob; + l_expected varchar2(32767):=q'[%org + utplsql + tests + helpers + A suite for testing different outcomes from reporters + + A description of some context + passing_test [% sec] + + + + + + a test with failing assertion [% sec] (FAILED - 1) + + + + a test raising unhandled exception [% sec] (FAILED - 2) + + + + a disabled test [0 sec] (DISABLED - Disabled for testing purpose) + a disabled test with no reason [0 sec] (DISABLED) + +% +Failures: +% + 1) failing_test + "Fails as values are different" + Actual: 'number [1] ' (varchar2) was expected to equal: 'number [2] ' (varchar2)% + at "UT3_USER.TEST_REPORTERS%", line 36 ut3_develop.ut.expect('number [1] ','Fails as values are different').to_equal('number [2] '); +% + 2) erroring_test + ORA-06502: PL/SQL: %: character to number conversion error + ORA-06512: at "UT3_USER.TEST_REPORTERS", line 44% + ORA-06512: at line 7 +Finished in % seconds +5 tests, 1 failed, 1 errored, 2 disabled, 0 warning(s)%]'; + + begin + select * + bulk collect into l_results + from table( + ut3_develop.ut.run( + 'test_reporters', + ut3_develop.ut_documentation_reporter() + ) + ); + l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results); + ut.expect(l_actual).to_be_like(l_expected); + end; + +end; +/ diff --git a/test/ut3_user/reporters/test_junit_reporter.pkb b/test/ut3_user/reporters/test_junit_reporter.pkb index 8f015e80b..4a574591c 100644 --- a/test/ut3_user/reporters/test_junit_reporter.pkb +++ b/test/ut3_user/reporters/test_junit_reporter.pkb @@ -219,12 +219,12 @@ create or replace package body test_junit_reporter as l_results ut3_develop.ut_varchar2_list; l_actual clob; l_expected varchar2(32767):=q'[ - - - - - - + + + + + + % @@ -249,6 +249,13 @@ create or replace package body test_junit_reporter as + + + + + + + diff --git a/test/ut3_user/reporters/test_realtime_reporter.pkb b/test/ut3_user/reporters/test_realtime_reporter.pkb index ad354133b..d4d83f7a3 100644 --- a/test/ut3_user/reporters/test_realtime_reporter.pkb +++ b/test/ut3_user/reporters/test_realtime_reporter.pkb @@ -45,6 +45,10 @@ create or replace package body test_realtime_reporter as --%test --%disabled procedure test_5; + + --%test + --%disabled(Cannot run this item at this time runtime > 10 mins.) + procedure test_6_disabled_reason; end;]'; execute immediate q'[create or replace package body check_realtime_reporting2 is procedure test_3_ok is @@ -62,6 +66,11 @@ create or replace package body test_realtime_reporter as begin null; end; + + procedure test_6_disabled_reason is + begin + null; + end; end;]'; execute immediate q'[create or replace package check_realtime_reporting3 is @@ -179,6 +188,8 @@ create or replace package body test_realtime_reporter as select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_4_nok' as item_id from dual union all select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_5' as item_id from dual union all select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_5' as item_id from dual union all + select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_6_disabled_reason' as item_id from dual union all + select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_6_disabled_reason' as item_id from dual union all select 'post-suite' as event_type, 'realtime_reporting.check_realtime_reporting2' as item_id from dual union all select 'pre-suite' as event_type, 'realtime_reporting.check_realtime_reporting1' as item_id from dual union all select 'pre-suite' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context' as item_id from dual union all @@ -226,6 +237,7 @@ create or replace package body test_realtime_reporter as select 'event/items/suite/items/suite/items/test' as node_path from dual union all select 'event/items/suite/items/suite/items/test' as node_path from dual union all select 'event/items/suite/items/suite/items/test' as node_path from dual union all + select 'event/items/suite/items/suite/items/test' as node_path from dual union all select 'event/items/suite/items/suite' as node_path from dual union all select 'event/items/suite/items/suite/items' as node_path from dual union all select 'event/items/suite/items/suite/items/suite' as node_path from dual union all @@ -237,7 +249,7 @@ create or replace package body test_realtime_reporter as procedure total_number_of_tests is l_actual integer; - l_expected integer := 8; + l_expected integer := 9; begin select t.event_doc.extract('/event/totalNumberOfTests/text()').getnumberval() into l_actual @@ -283,9 +295,9 @@ create or replace package body test_realtime_reporter as and t.event_doc.extract('//test/@id').getstringval() is not null; open l_expected for select level as test_number, - 8 as total_number_of_tests + 9 as total_number_of_tests from dual - connect by level <= 8; + connect by level <= 9; ut.expect(l_actual).to_equal(l_expected).unordered; end pre_test_nodes; @@ -311,9 +323,9 @@ create or replace package body test_realtime_reporter as and t.event_doc.extract('//test/counter/warning/text()').getnumberval() is not null; open l_expected for select level as test_number, - 8 as total_number_of_tests + 9 as total_number_of_tests from dual - connect by level <= 8; + connect by level <= 9; ut.expect(l_actual).to_equal(l_expected).unordered; end post_test_nodes; @@ -396,9 +408,9 @@ create or replace package body test_realtime_reporter as from table(g_events) t where t.event_doc.extract('/event[@type="post-test"]/test/@id').getstringval() = 'realtime_reporting.check_realtime_reporting3.test_6_with_runtime_error'; - ut3_tester_helper.main_helper.append_to_list(l_expected_list, ''); + ut3_tester_helper.main_helper.append_to_list(l_expected_list, '%ORA-06512: at line 7]]>'); l_expected := ut3_tester_helper.main_helper.table_to_clob(l_expected_list); ut.expect(l_actual).to_be_like(l_expected); end error_stack_of_test; @@ -415,7 +427,7 @@ create or replace package body test_realtime_reporter as = 'realtime_reporting.check_realtime_reporting3'; ut3_tester_helper.main_helper.append_to_list(l_expected_list, ''); + ut3_tester_helper.main_helper.append_to_list(l_expected_list, '%ORA-06512: at line 7]]>'); l_expected := ut3_tester_helper.main_helper.table_to_clob(l_expected_list); ut.expect(l_actual).to_be_like(l_expected); end error_stack_of_testsuite; @@ -494,6 +506,22 @@ create or replace package body test_realtime_reporter as ut.expect(l_xml is not null).to_be_true(); end; + procedure disabled_reason is + l_actual varchar2(32767); + l_expected varchar2(80) := dbms_xmlgen.convert('Cannot run this item at this time runtime > 10 mins.'); + begin + select t.event_doc.extract( + '//test/disabledReason/text()' + ).getstringval() + into l_actual + from table(g_events) t + where xmlexists( + '/event[@type="pre-run"]/*//test[@id="realtime_reporting.check_realtime_reporting2.test_6_disabled_reason"]' + passing t.event_doc + ); + ut.expect(l_actual).to_equal(l_expected); + end; + procedure remove_test_suites is pragma autonomous_transaction; begin diff --git a/test/ut3_user/reporters/test_realtime_reporter.pks b/test/ut3_user/reporters/test_realtime_reporter.pks index 453824ca5..087713f79 100644 --- a/test/ut3_user/reporters/test_realtime_reporter.pks +++ b/test/ut3_user/reporters/test_realtime_reporter.pks @@ -57,6 +57,9 @@ create or replace package test_realtime_reporter as --%test(Escape nested CDATA sections in test output) procedure nested_cdata_output; + --%test(Provide reason disabled test) + procedure disabled_reason; + --%afterall procedure remove_test_suites; diff --git a/test/ut3_user/reporters/test_sonar_test_reporter.pkb b/test/ut3_user/reporters/test_sonar_test_reporter.pkb index 5821b2071..7c30fd91d 100644 --- a/test/ut3_user/reporters/test_sonar_test_reporter.pkb +++ b/test/ut3_user/reporters/test_sonar_test_reporter.pkb @@ -9,7 +9,8 @@ create or replace package body test_sonar_test_reporter as % %%% %%% -%% +%% +%% ]'; diff --git a/test/ut3_user/reporters/test_tap_reporter.pkb b/test/ut3_user/reporters/test_tap_reporter.pkb new file mode 100644 index 000000000..786c53ee0 --- /dev/null +++ b/test/ut3_user/reporters/test_tap_reporter.pkb @@ -0,0 +1,254 @@ +create or replace package body test_tap_reporter as + + gc_boilerplate_suitepath_expression constant varchar2(300) := 'TAP version 14\s*1..1\s*# Subtest: org\s{5}1..1\s{5}# Subtest: utplsql\s{9}1..1\s{9}# Subtest: tests\s{13}1..1\s{13}# Subtest: helpers\s{17}1..1\s{17}# Subtest: A suite for testing different outcomes from reporters'; + + + procedure compile_tests as + pragma autonomous_transaction; + begin + + execute immediate q'[ + create or replace package test_tap_escaping as + --%suite(Some \ and # to be escaped) + + --%test(Even more \\ and multiple ###) + procedure more_escaping; + + --%test(Disabled test) + --%disabled(With \ and # in skip reason) + procedure not_skipping_escapes; + + --%test(Escaped Comments) + procedure escaped_comments; + + --%context(Some context) + + --%test(Another disabled test) + --%disabled + procedure another_disabled_test; + + --%endcontext + end test_tap_escaping; + ]'; + + execute immediate q'[ + create or replace package body test_tap_escaping as + + procedure more_escaping as + begin + ut.expect(1).to_equal(1); + end more_escaping; + + + procedure not_skipping_escapes as + begin + ut.expect(10).to_equal(1); + end not_skipping_escapes; + + procedure escaped_comments as + begin + dbms_output.put_line('This \ and # should not be escaped, and this not as well!!!'); + ut.expect(1).to_equal(1); + end escaped_comments; + + procedure another_disabled_test as + begin + ut.expect(10).to_equal(1); + end; + end test_tap_escaping; + ]'; + + end compile_tests; + + + procedure simple_succeeding_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || '\s{21}1..1\s{21}# \s{21}# Subtest: A description of some context\s{25}1..1\s{25}ok - passing_test\s{25}# \s{25}# \s{25}# \s{25}# \s{25}# \s{21}# \sok - org\s*'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_succeeding_test; + + procedure simple_failing_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}not ok - a test with failing assertion\s{23}---\s{23}message: '"Fails as values are different"'\s{23}severity: fail\s{23}...\s{21}# \s{21}# \s{21}# \s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.failing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_failing_test; + + + procedure simple_erroring_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}not ok - a test raising unhandled exception\s{23}---\s{23}message: |\s{25ORA-06502: .*\s{25}ORA-06512: at line [[:digit:]]+\s{23}severity: error\s{23}...\s{21}# \s{21}# \s{21}# \s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.erroring_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_erroring_test; + + + procedure disabled_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}ok - a disabled test # SKIP: Disabled for testing purpose\s{21}# \sok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.disabled_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end disabled_test; + + + procedure disabled_test_no_description as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}ok - a disabled test with no reason # SKIP\s{21}# \sok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.disabled_test_no_reason',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end disabled_test_no_description; + + + procedure multiple_tests_different_outcome as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[TAP version 14\s*1..1\s*# Subtest: org.*# Subtest: A suite.*\s{21}1..5\s{21}# \s{21}# Subtest: A desc.*\s{25}1..1\s{25}ok - passing_test\s{25}# \s{25}# \s{21}not ok - a test w.*\s{23}---\s{23}message:.*\s{21}# .*\s{21}# not ok - a test rai.*\s{23}---\s{23}message: |.*ok - a disabled test # SKIP: Disabled for testing purpose.*ok - a dis.* # SKIP\s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected, 'n'); + end multiple_tests_different_outcome; + + + procedure escape_suite_name as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: Some \\ and \# to be escaped%ok - Some \\ and \# to be escaped%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.more_escaping',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end escape_suite_name; + + + procedure escape_multiple_characters_test_name as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Even more \\\\ and multiple \#\#\#%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.more_escaping',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end escape_multiple_characters_test_name; + + + procedure special_characters_in_disabled_reason as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Disabled test # SKIP: With \\ and \# in skip reason%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.not_skipping_escapes',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end special_characters_in_disabled_reason; + + + procedure special_characters_in_comment as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Escaped Comments%# This \ and # should not be escaped, and this not as well!!!%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.escaped_comments',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end special_characters_in_comment; + + + procedure context_as_commented_subtests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: A suite for testing different outcomes from reporters%# Subtest: A description of some context%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + + end context_as_commented_subtests; + + + procedure suitepath_as_chopped_subtests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[.*# Subtest: org.*1\s{5}# Subtest: utplsql.*1\s{9}# Subtest: tests.*1\s{13}# Subtest: helpers.*1\s{17}# Subtest: A suite for testing different outcomes from reporters.*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected, 'n'); + + end suitepath_as_chopped_subtests; + + + procedure include_context_with_skipped_tests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: Some context%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.another_disabled_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + + end include_context_with_skipped_tests; + + + procedure drop_help_tests as + pragma autonomous_transaction; + begin + execute immediate 'drop package test_tap_escaping'; + end drop_help_tests; +end test_tap_reporter; +/ \ No newline at end of file diff --git a/test/ut3_user/reporters/test_tap_reporter.pks b/test/ut3_user/reporters/test_tap_reporter.pks new file mode 100644 index 000000000..27f9651bc --- /dev/null +++ b/test/ut3_user/reporters/test_tap_reporter.pks @@ -0,0 +1,53 @@ +create or replace package test_tap_reporter as + + --%suite(ut_tap_reporter) + --%suitepath(utplsql.test_user.reporters) + + --%beforeall + procedure compile_tests; + + --%test(Simple succeeding test) + procedure simple_succeeding_test; + + --%test(Simple failing test) + procedure simple_failing_test; + + --%test(Simple erroring test) + procedure simple_erroring_test; + + --%test(Skipped test) + procedure disabled_test; + + --%test(Skipped test without description) + procedure disabled_test_no_description; + + --%test(Multiple tests with different outcome) + procedure multiple_tests_different_outcome; + + --%test(Escape special characters in suite name) + procedure escape_suite_name; + + --%test(Escape multiple special characters in test name) + procedure escape_multiple_characters_test_name; + + --%test(Disabled Test with special characters in disable reason) + procedure special_characters_in_disabled_reason; + + --%test(Don't escape special characters in comment) + procedure special_characters_in_comment; + + --%test(Include context as commented subtests) + procedure context_as_commented_subtests; + + --%test(Suitepath as chopped subtests) + procedure suitepath_as_chopped_subtests; + + --%test(Include context with only skipped tests in output) + procedure include_context_with_skipped_tests; + + + --%afterall + procedure drop_help_tests; + +end test_tap_reporter; +/ \ No newline at end of file diff --git a/test/ut3_user/reporters/test_teamcity_reporter.pkb b/test/ut3_user/reporters/test_teamcity_reporter.pkb index 6d471b2b0..517ddc037 100644 --- a/test/ut3_user/reporters/test_teamcity_reporter.pkb +++ b/test/ut3_user/reporters/test_teamcity_reporter.pkb @@ -31,12 +31,37 @@ create or replace package body test_teamcity_reporter as end; end;]'; + execute immediate q'[create or replace package check_multiple_failures is + --%suite + + --%test + procedure multi_failure; + + --%test + procedure multi_failure_on_error; + end;]'; + execute immediate q'[create or replace package body check_multiple_failures is + procedure multi_failure is + begin + ut3_develop.ut.expect(1).to_be_null; + ut3_develop.ut.expect(2).to_equal(1); + ut3_develop.ut.expect('Bad').to_equal('Good'); + end; + procedure multi_failure_on_error is + l_integer_variable integer; + begin + ut3_develop.ut.expect(1).to_be_null; + ut3_develop.ut.expect(2).to_equal(1); + ut3_develop.ut.expect('Bad').to_equal('Good'); + l_integer_variable := 'a string'; + end; + end;]'; + end; procedure report_produces_expected_out is l_output_data ut3_develop.ut_varchar2_list; - l_output clob; l_expected varchar2(32767); begin l_expected := q'{%##teamcity[testSuiteStarted timestamp='%' name='org'] @@ -45,29 +70,29 @@ create or replace package body test_teamcity_reporter as %##teamcity[testSuiteStarted timestamp='%' name='org.utplsql.tests.helpers'] %##teamcity[testSuiteStarted timestamp='%' name='A suite for testing different outcomes from reporters'] %##teamcity[testSuiteStarted timestamp='%' name='A description of some context'] -%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.test_reporters.passing_test'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.test_reporters.passing_test'] -%##teamcity[testFinished timestamp='%' duration='%' name='ut3$user#.test_reporters.passing_test'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.test_reporters.passing_test'] %##teamcity[testSuiteFinished timestamp='%' name='A description of some context'] -%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.test_reporters.failing_test'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.test_reporters.failing_test'] -%##teamcity[testFailed timestamp='%' details='Actual: |'number |[1|] |' (varchar2) was expected to equal: |'number |[2|] |' (varchar2)' message='Fails as values are different' name='ut3$user#.test_reporters.failing_test'] -%##teamcity[testFinished timestamp='%' duration='%' name='ut3$user#.test_reporters.failing_test'] -%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.test_reporters.erroring_test'] +%##teamcity[testFailed timestamp='%' details='Actual: |'number |[1|] |' (varchar2) was expected to equal: |'number |[2|] |' (varchar2)' message='Fails as values are different' name='ut3_user.test_reporters.failing_test'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.test_reporters.failing_test'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' 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'] +%##teamcity[testStdErr timestamp='%' name='ut3_user.test_reporters.erroring_test' out='Test exception:|nORA-06502: PL/SQL: %: 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: %: 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'] %##teamcity[testSuiteFinished timestamp='%' name='A suite for testing different outcomes from reporters'] %##teamcity[testSuiteFinished timestamp='%' name='org.utplsql.tests.helpers'] %##teamcity[testSuiteFinished timestamp='%' name='org.utplsql.tests'] @@ -84,13 +109,12 @@ create or replace package body test_teamcity_reporter as procedure escape_special_chars is l_output_data ut3_develop.ut_varchar2_list; - l_output clob; l_expected varchar2(32767); begin l_expected := q'{%##teamcity[testSuiteStarted timestamp='%' name='A suite with |'quote|''] -%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.check_escape_special_chars.test_do_stuff'] -%##teamcity[testFailed timestamp='%' details='Actual: (varchar2)|n |' |[ |r|n |] |'|n was expected to be null' name='ut3$user#.check_escape_special_chars.test_do_stuff'] -%##teamcity[testFinished timestamp='%' duration='%' name='ut3$user#.check_escape_special_chars.test_do_stuff'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.check_escape_special_chars.test_do_stuff'] +%##teamcity[testFailed timestamp='%' details='Actual: (varchar2)|n |' |[ |r|n |] |'|n was expected to be null' name='ut3_user.check_escape_special_chars.test_do_stuff'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.check_escape_special_chars.test_do_stuff'] %##teamcity[testSuiteFinished timestamp='%' name='A suite with |'quote|'']}'; --act select * @@ -103,13 +127,12 @@ create or replace package body test_teamcity_reporter as procedure trims_long_output is l_output_data ut3_develop.ut_varchar2_list; - l_output clob; l_expected varchar2(32767); begin l_expected := q'{%##teamcity[testSuiteStarted timestamp='%' name='check_trims_long_output'] -%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3$user#.check_trims_long_output.long_output'] -%##teamcity[testFailed timestamp='%' details='Actual: (varchar2)|n |'aVarcharaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|[...|]' name='ut3$user#.check_trims_long_output.long_output'] -%##teamcity[testFinished timestamp='%' duration='%' name='ut3$user#.check_trims_long_output.long_output'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.check_trims_long_output.long_output'] +%##teamcity[testFailed timestamp='%' details='Actual: (varchar2)|n |'aVarcharaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|[...|]' name='ut3_user.check_trims_long_output.long_output'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.check_trims_long_output.long_output'] %##teamcity[testSuiteFinished timestamp='%' name='check_trims_long_output']}'; --act select * @@ -120,11 +143,54 @@ create or replace package body test_teamcity_reporter as ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); end; + procedure report_multiple_expectations is + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'{%##teamcity[testSuiteStarted timestamp='%' name='check_multiple_failures'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.check_multiple_failures.multi_failure'] +%##teamcity[testFailed timestamp='%' details='Actual: 1 (number) was expected to be null' name='ut3_user.check_multiple_failures.multi_failure'] +%##teamcity[testFailed timestamp='%' details='Actual: 2 (number) was expected to equal: 1 (number)' name='ut3_user.check_multiple_failures.multi_failure'] +%##teamcity[testFailed timestamp='%' details='Actual: |'Bad|' (varchar2) was expected to equal: |'Good|' (varchar2)' name='ut3_user.check_multiple_failures.multi_failure'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.check_multiple_failures.multi_failure'] +%##teamcity[testSuiteFinished timestamp='%' name='check_multiple_failures']}'; + --act + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('check_multiple_failures.multi_failure',ut3_develop.ut_teamcity_reporter())); + + --assert + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end; + + procedure report_multiple_expect_on_err is + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'{%##teamcity[testSuiteStarted timestamp='%' name='check_multiple_failures'] +%##teamcity[testStarted timestamp='%' captureStandardOutput='true' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testStdErr timestamp='%' name='ut3_user.check_multiple_failures.multi_failure_on_error' out='Test exception:|nORA-06502: PL/SQL: %: character to number conversion error|nORA-06512: at "UT3_USER.CHECK_MULTIPLE_FAILURES", line %|nORA-06512: at %|n'] +%##teamcity[testFailed timestamp='%' details='Test exception:|nORA-06502: PL/SQL: %: character to number conversion error|nORA-06512: at "UT3_USER.CHECK_MULTIPLE_FAILURES", line %|nORA-06512: at %|n' message='Error occured' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testFailed timestamp='%' details='Actual: 1 (number) was expected to be null' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testFailed timestamp='%' details='Actual: 2 (number) was expected to equal: 1 (number)' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testFailed timestamp='%' details='Actual: |'Bad|' (varchar2) was expected to equal: |'Good|' (varchar2)' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testFinished timestamp='%' duration='%' name='ut3_user.check_multiple_failures.multi_failure_on_error'] +%##teamcity[testSuiteFinished timestamp='%' name='check_multiple_failures']}'; + --act + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('check_multiple_failures.multi_failure_on_error',ut3_develop.ut_teamcity_reporter())); + + --assert + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end; + procedure remove_test_package is pragma autonomous_transaction; begin execute immediate 'drop package check_escape_special_chars'; execute immediate 'drop package check_trims_long_output'; + execute immediate 'drop package check_multiple_failures'; end; end; diff --git a/test/ut3_user/reporters/test_teamcity_reporter.pks b/test/ut3_user/reporters/test_teamcity_reporter.pks index f849751f1..1b9277e7a 100644 --- a/test/ut3_user/reporters/test_teamcity_reporter.pks +++ b/test/ut3_user/reporters/test_teamcity_reporter.pks @@ -15,6 +15,12 @@ create or replace package test_teamcity_reporter as --%test(Trims output so it fits into 4000 chars) procedure trims_long_output; + --%test(Reports failures on multiple expectations) + procedure report_multiple_expectations; + + --%test(Reports failures on multiple expectations) + procedure report_multiple_expect_on_err; + --%afterall procedure remove_test_package; diff --git a/test/ut3_user/reporters/test_tfs_junit_reporter.pkb b/test/ut3_user/reporters/test_tfs_junit_reporter.pkb index 7eb4f8ab6..105517763 100644 --- a/test/ut3_user/reporters/test_tfs_junit_reporter.pkb +++ b/test/ut3_user/reporters/test_tfs_junit_reporter.pkb @@ -8,7 +8,7 @@ create or replace package body test_tfs_junit_reporter as --%test(A test with ) procedure test_do_stuff; - + end;]'; execute immediate q'[create or replace package body check_junit_reporting is procedure test_do_stuff is @@ -18,12 +18,12 @@ create or replace package body test_tfs_junit_reporter as end; end;]'; - + execute immediate q'[create or replace package check_junit_rep_suitepath is --%suitepath(core) --%suite(check_junit_rep_suitepath) --%displayname(Check JUNIT Get path for suitepath) - + --%test(check_junit_rep_suitepath) --%displayname(Check JUNIT Get path for suitepath) procedure check_junit_rep_suitepath; @@ -38,7 +38,7 @@ create or replace package body test_tfs_junit_reporter as execute immediate q'[create or replace package check_junit_flat_suitepath is --%suitepath(core.check_junit_rep_suitepath) --%suite(flatsuitepath) - + --%beforeall procedure donuffin; end;]'; @@ -49,8 +49,85 @@ create or replace package body test_tfs_junit_reporter as end; end;]'; - end; + execute immediate q'[create or replace package check_junit_in_context is + --%suitepath(core.check_junit_rep_suitepath) + --%suite(inctxsuite) + --%displayname(JUNIT test are inside context) + + -- %context(incontext) + -- %name(incontext) + + --%test(incontext) + --%displayname(Check JUNIT Get path incontext) + procedure check_junit_rep_incontext; + + -- %endcontext + end;]'; + execute immediate q'[create or replace package body check_junit_in_context is + procedure check_junit_rep_incontext is + begin + ut3_develop.ut.expect(1).to_equal(1); + end; + end;]'; + + execute immediate q'[create or replace package check_junit_out_context is + --%suitepath(core) + --%suite(outctxsuite) + --%displayname(JUNIT test are outside context) + + -- %context(outcontext) + -- %name(outcontext) + + -- %endcontext + + + --%test(outctx) + --%displayname(outctx) + procedure outctx; + + + end;]'; + execute immediate q'[create or replace package body check_junit_out_context is + procedure outctx is + begin + ut3_develop.ut.expect(1).to_equal(1); + end; + end;]'; + + execute immediate q'[create or replace package check_junit_inout_context is + --%suitepath(core) + --%suite(inoutcontext) + --%displayname(Test in and out of context) + -- %context(incontext) + -- %name(Production_context_Features) + + --%test(inctx) + --%displayname(inctx) + procedure inctx; + + -- %endcontext + + + --%test(outctx) + --%displayname(outctx) + procedure outctx; + + + end;]'; + execute immediate q'[create or replace package body check_junit_inout_context is + procedure inctx is + begin + ut3_develop.ut.expect(1).to_equal(1); + end; + + procedure outctx is + begin + ut3_develop.ut.expect(1).to_equal(1); + end; + end;]'; + + end; procedure escapes_special_chars is l_results ut3_develop.ut_varchar2_list; @@ -92,7 +169,7 @@ create or replace package body test_tfs_junit_reporter as --Assert ut.expect(l_actual).to_be_like('%testcase classname="check_junit_reporting"%'); end; - + procedure check_flatten_nested_suites is l_results ut3_develop.ut_varchar2_list; l_actual clob; @@ -111,7 +188,7 @@ create or replace package body test_tfs_junit_reporter as
    %'); end; - + procedure check_nls_number_formatting is l_results ut3_develop.ut_varchar2_list; l_actual clob; @@ -163,5 +240,73 @@ create or replace package body test_tfs_junit_reporter as reporters.check_xml_encoding_included(ut3_develop.ut_tfs_junit_reporter(), 'UTF-8'); end; + procedure reports_only_test_in_ctx is + l_results ut3_develop.ut_varchar2_list; + l_actual clob; + begin + --Act + select * + bulk collect into l_results + from table(ut3_develop.ut.run('check_junit_in_context',ut3_develop.ut_tfs_junit_reporter())); + l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results); + --Assert + ut.expect(l_actual).to_be_like(' + + + + + + + + +%'); + end; + + procedure reports_only_test_out_ctx is + l_results ut3_develop.ut_varchar2_list; + l_actual clob; + begin + --Act + select * + bulk collect into l_results + from table(ut3_develop.ut.run('check_junit_out_context',ut3_develop.ut_tfs_junit_reporter())); + l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results); + --Assert + ut.expect(l_actual).to_be_like(' + + + + + + + + +%'); + end; + + procedure reports_only_test_inout_ctx is + l_results ut3_develop.ut_varchar2_list; + l_actual clob; + begin + --Act + select * + bulk collect into l_results + from table(ut3_develop.ut.run('check_junit_inout_context',ut3_develop.ut_tfs_junit_reporter())); + l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results); + --Assert + ut.expect(l_actual).to_be_like(' + + + + + + + + + + +%'); + end; + end; -/ +/ \ No newline at end of file diff --git a/test/ut3_user/reporters/test_tfs_junit_reporter.pks b/test/ut3_user/reporters/test_tfs_junit_reporter.pks index 07acb4d21..70f78e334 100644 --- a/test/ut3_user/reporters/test_tfs_junit_reporter.pks +++ b/test/ut3_user/reporters/test_tfs_junit_reporter.pks @@ -30,6 +30,15 @@ create or replace package test_tfs_junit_reporter as --%test(Includes XML header with encoding when encoding provided) procedure check_encoding_included; + --%test(Reports only testsuites where there are any testcases, all tests are in context) + procedure reports_only_test_in_ctx; + + --%test(Reports only testsuites where there are any testcases, all tests are outside context) + procedure reports_only_test_out_ctx; + + --%test(Reports only testsuites where there are any testcases, one test in ctx one test outside) + procedure reports_only_test_inout_ctx; + --%afterall procedure remove_test_package; end;