diff --git a/.github/scripts/create_test_users.sh b/.github/scripts/create_test_users.sh index fe43b7467..fff44f1bd 100755 --- a/.github/scripts/create_test_users.sh +++ b/.github/scripts/create_test_users.sh @@ -86,7 +86,7 @@ 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 + 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; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be6e18ca2..8c77c486c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,8 @@ jobs: 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' @@ -40,6 +42,8 @@ jobs: 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 @@ -84,6 +88,19 @@ jobs: # 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} @@ -116,17 +133,17 @@ jobs: run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/install.sh - name: Check code style - if: ${{ matrix.id == 1 }} + 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.id == 1 }} + 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.id == 1 }} + if: ${{ matrix.validate-utplsql-uninstall == true }} id: reinstall-utplsql run: docker exec -w /utPLSQL ${DOCKER_ENV} oracle .github/scripts/install.sh @@ -190,7 +207,7 @@ jobs: - name: SonarCloud Scan id: sonar - if: ${{ always() && matrix.db_version_name == '23-free' }} + 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 @@ -276,17 +293,3 @@ jobs: repository: ${{ matrix.repo }} event-type: utPLSQL-build - slack-workflow-status: - if: always() - name: Post Workflow Status To Slack - needs: [ build, publish, dispatch ] - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Slack Workflow Notification - uses: Gamesight/slack-workflow-status@master - with: - repo_token: ${{secrets.GITHUB_TOKEN}} - slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} - name: 'Github Actions[bot]' - icon_url: 'https://octodex.github.com/images/mona-the-rivetertocat.png' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab043b6eb..4b5d59463 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,18 +65,3 @@ jobs: utPLSQL.tar.gz utPLSQL.tar.gz.md5 - slack-workflow-status: - if: always() - name: Post Workflow Status To Slack - needs: [ upload_artifacts ] - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Slack Workflow Notification - uses: Gamesight/slack-workflow-status@master - with: - repo_token: ${{secrets.GITHUB_TOKEN}} - slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} - name: 'Github Actions[bot]' - icon_url: 'https://octodex.github.com/images/mona-the-rivetertocat.png' - diff --git a/BUILD_NO b/BUILD_NO index ae1550799..dd37b7287 100644 --- a/BUILD_NO +++ b/BUILD_NO @@ -1 +1 @@ -4373 +4414 diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ad2141f..f75662f75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Releases are available on the [GitHub Releases page](https://github.com/utPLSQL/ --- -## [v3.2.01] - unreleased +## [v3.2.01] - 2026-04-22 ## Important - Dropped support for Oracle Database versions older than 19c diff --git a/development/cleanup.sh b/development/cleanup.sh index 65f3ab0c1..d36f21540 100755 --- a/development/cleanup.sh +++ b/development/cleanup.sh @@ -29,11 +29,13 @@ 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/docs/about/authors.md b/docs/about/authors.md index e3736ca96..687c6cfaa 100644 --- a/docs/about/authors.md +++ b/docs/about/authors.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) ### utPLSQL v3 Major Contributors diff --git a/docs/about/license.md b/docs/about/license.md index a8bc7c21a..6567b5db7 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # Version Information @@ -9,9 +9,5 @@ External code used in the development of this project, but is not required for u | 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 index 722f603ee..df666731f 100644 --- a/docs/about/project-details.md +++ b/docs/about/project-details.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # utPLSQL Project Details diff --git a/docs/about/support.md b/docs/about/support.md index eb73d4adb..f27ee0c1e 100644 --- a/docs/about/support.md +++ b/docs/about/support.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # How to get support diff --git a/docs/index.md b/docs/index.md index eb44d57c4..6ae326c1c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) ## What is utPLSQL diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md index 5a2d2cd83..76d90bc30 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.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # Advanced data comparison diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 8add10a7b..fd51fec8f 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--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. diff --git a/docs/userguide/best-practices.md b/docs/userguide/best-practices.md index 100b7da11..c9f08eab5 100644 --- a/docs/userguide/best-practices.md +++ b/docs/userguide/best-practices.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) The following are best practices we at utPLSQL have learned about PL/SQL and Unit Testing. diff --git a/docs/userguide/coverage.md b/docs/userguide/coverage.md index 7c80396b2..03cb8bfd3 100644 --- a/docs/userguide/coverage.md +++ b/docs/userguide/coverage.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) 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: diff --git a/docs/userguide/exception-reporting.md b/docs/userguide/exception-reporting.md index 9648be0f6..1e892215d 100644 --- a/docs/userguide/exception-reporting.md +++ b/docs/userguide/exception-reporting.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) 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. diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index b4745dd18..c51e6c99c 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) ## Expectation concepts diff --git a/docs/userguide/getting-started.md b/docs/userguide/getting-started.md index 8e5b8d9bd..a722f8ac7 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.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # Getting started with TDD and utPLSQL diff --git a/docs/userguide/install.md b/docs/userguide/install.md index 23b5f5cb4..615883720 100644 --- a/docs/userguide/install.md +++ b/docs/userguide/install.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) ## Supported database versions diff --git a/docs/userguide/querying_suites.md b/docs/userguide/querying_suites.md index 76044c619..167fba1b4 100644 --- a/docs/userguide/querying_suites.md +++ b/docs/userguide/querying_suites.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) ## Obtaining information about suites diff --git a/docs/userguide/reporters.md b/docs/userguide/reporters.md index 7d7606963..807fa537f 100644 --- a/docs/userguide/reporters.md +++ b/docs/userguide/reporters.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) utPLSQL provides several reporting formats. The sections below describe most of them. diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md index d8e5ad36c..606a5ef97 100644 --- a/docs/userguide/running-unit-tests.md +++ b/docs/userguide/running-unit-tests.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) utPLSQL framework provides two main entry points to run unit tests from within the database: diff --git a/docs/userguide/upgrade.md b/docs/userguide/upgrade.md index 88ca240c1..a49118fbd 100644 --- a/docs/userguide/upgrade.md +++ b/docs/userguide/upgrade.md @@ -1,4 +1,4 @@ -![version](https://img.shields.io/badge/version-v3.2.01.4373--develop-blue.svg) +![version](https://img.shields.io/badge/version-v3.2.01.4414--develop-blue.svg) # Upgrading from version 2 diff --git a/readme.md b/readme.md index df3da2749..7af2183c5 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ ![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) +[![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) diff --git a/sonar-project.properties b/sonar-project.properties index 24e77c11f..efcaeb5e6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -15,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 diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 992af2fe0..e7d9f2719 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -121,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]' @@ -268,6 +269,7 @@ create or replace package body ut_annotation_manager as 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; diff --git a/source/core/coverage/ut_coverage.pkb b/source/core/coverage/ut_coverage.pkb index e8e6ed529..f3181fdc7 100644 --- a/source/core/coverage/ut_coverage.pkb +++ b/source/core/coverage/ut_coverage.pkb @@ -55,10 +55,11 @@ create or replace package body ut_coverage is s.text from {sources_view} s {join_file_mappings} 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 {excuded_objects_cardinality})*/ 1 + select /*+ cardinality(el {excluded_objects_cardinality})*/ 1 from table(:l_excluded_objects) el where s.owner = el.owner and s.name = el.name ) @@ -152,7 +153,7 @@ create or replace package body ut_coverage is 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, '{excuded_objects_cardinality}', ut_utils.scale_cardinality(cardinality(coalesce(a_coverage_options.exclude_objects, ut_object_names())))); + 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; diff --git a/source/core/output_buffers/ut_output_buffer_base.tpb b/source/core/output_buffers/ut_output_buffer_base.tpb index f92827d28..dc9b34b88 100644 --- a/source/core/output_buffers/ut_output_buffer_base.tpb +++ b/source/core/output_buffers/ut_output_buffer_base.tpb @@ -18,18 +18,20 @@ 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); - select /*+ no_parallel */ count(*) into l_exists from ut_output_buffer_info_tmp where output_id = self.output_id; - if ( l_exists > 0 ) then - update /*+ no_parallel */ ut_output_buffer_info_tmp set start_date = self.start_date where output_id = self.output_id; - else - insert /*+ no_parallel */ 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; diff --git a/source/core/ut_suite_tag_filter.pkb b/source/core/ut_suite_tag_filter.pkb index 353793bc5..17b5ba4b0 100644 --- a/source/core/ut_suite_tag_filter.pkb +++ b/source/core/ut_suite_tag_filter.pkb @@ -19,29 +19,28 @@ create or replace package body ut_suite_tag_filter is /** * Constants use in postfix and infix transformations */ - gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!'); + 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_tags_column_name constant varchar2(250) := 'tags'; 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; + + 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(); + 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 + To support a legact tag notation , = OR - = NOT we will perform a replace of that characters into @@ -57,36 +56,36 @@ create or replace package body ut_suite_tag_filter is l_tags_exclude varchar2(4000); l_return_tag varchar2(4000); begin - if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then + if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then select '('||listagg( t.column_value,'|') - within group( order by 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 '-%'; - + where t.column_value like '-%'; + l_return_tag:= - case + 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 + then l_tags_exclude end; - else + else l_return_tag := a_tags; - end if; + end if; return l_return_tag; end; - + /* https://stackoverflow.com/questions/29634992/shunting-yard-validate-expression */ @@ -97,14 +96,14 @@ create or replace package body ut_suite_tag_filter is l_expect_operand boolean := true; l_expect_operator boolean := false; l_idx pls_integer; - begin + 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); + 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; @@ -113,56 +112,56 @@ create or replace package body ut_suite_tag_filter is 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; + 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; + 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; + 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; + 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; + 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; + l_expect_operator:= true; else - if not(l_expect_operand) then - raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + 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; + 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; + 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) + + 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); @@ -175,9 +174,9 @@ create or replace package body ut_suite_tag_filter is 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 + + 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 @@ -185,12 +184,12 @@ create or replace package body ut_suite_tag_filter is --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 + --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 + --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; @@ -199,7 +198,7 @@ create or replace package body ut_suite_tag_filter is end if; l_idx := a_postfix_exp.next(l_idx); end loop; - + return l_infix_stack.pop; end conv_postfix_to_infix_sql; @@ -208,73 +207,69 @@ create or replace package body ut_suite_tag_filter 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)),gc_tags_column_name); + 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; + 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_suite_tags ut_suite_cache_rows := ut_suite_cache_rows(); + l_results ut_suite_cache_rows; l_sql varchar2(32000); - l_tags varchar2(4000):= create_where_filter(a_tags); + 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 ]'||gc_tags_column_name||q'[ - from table(:suite_items) c - ), - suites_matching_expr as ( - select c.id,c.path as path,c.self_type,c.object_owner,c.tags - from suites_mv c - where c.self_type in ('UT_SUITE','UT_CONTEXT') - and ]'||l_tags||q'[ - ), - tests_matching_expr as ( - select c.id,c.path as path,c.self_type,c.object_owner,c.tags as ]'||gc_tags_column_name||q'[ - from suites_mv c where c.self_type in ('UT_TEST') - and ]'||l_tags||q'[ - ), - tests_with_tags_inh_from_suite as ( - select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner - from suites_mv c join suites_matching_expr t - on (c.path||'.' like t.path || '.%' /*all descendants and self*/ and c.object_owner = t.object_owner) - ), - tests_with_tags_prom_to_suite as ( - select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner - from suites_mv c join tests_matching_expr t - on (t.path||'.' like c.path || '.%' /*all ancestors and self*/ and c.object_owner = t.object_owner) - ) - select obj from suites_mv c, - (select id,row_number() over (partition by id order by id) r_num from - (select id - from tests_with_tags_prom_to_suite tst - where ]'||l_tags||q'[ - union all - select id from tests_with_tags_inh_from_suite tst - where ]'||l_tags||q'[ - ) - ) t where c.id = t.id and r_num = 1 ]'; - execute immediate l_sql bulk collect into l_suite_tags using a_suite_items; - return l_suite_tags; + 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; + 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); diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 9afb2ffb0..32cbb3f8c 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -1024,13 +1024,13 @@ 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 --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 --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; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 388928a74..092c25583 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -21,7 +21,7 @@ create or replace package ut_utils authid definer is * */ - gc_version constant varchar2(50) := 'v3.2.01.4373-develop'; + gc_version constant varchar2(50) := 'v3.2.01.4414-develop'; subtype t_executable_type is varchar2(30); gc_before_all constant t_executable_type := 'beforeall'; @@ -457,12 +457,12 @@ 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 diff --git a/source/reporters/ut_coverage_report_html_helper.pkb b/source/reporters/ut_coverage_report_html_helper.pkb index 7ebf891f9..d81211f03 100644 --- a/source/reporters/ut_coverage_report_html_helper.pkb +++ b/source/reporters/ut_coverage_report_html_helper.pkb @@ -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 @@ -231,7 +231,7 @@ create or replace package body ut_coverage_report_html_helper is l_title varchar2(100) := 'All files'; l_coverage_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/test/install_and_run_tests.sh b/test/install_and_run_tests.sh index 1719f137a..0c17b08b7 100755 --- a/test/install_and_run_tests.sh +++ b/test/install_and_run_tests.sh @@ -1,10 +1,9 @@ #!/bin/bash set -ev -. ./development/env.sh - #goto git root directory git rev-parse && cd "$(git rev-parse --show-cdup)" +. ./development/env.sh cd test time . ./install_tests.sh diff --git a/test/ut3_tester/core/test_output_buffer.pkb b/test/ut3_tester/core/test_output_buffer.pkb index 317e7e3d5..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,10 +18,10 @@ 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(); @@ -196,5 +204,14 @@ create or replace package body test_output_buffer is ut.expect(lengthb(l_text)).to_be_less_or_equal(l_max_len); end; - end test_output_buffer; + 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 5b6cd678b..aa5c830d3 100644 --- a/test/ut3_tester/core/test_output_buffer.pks +++ b/test/ut3_tester/core/test_output_buffer.pks @@ -53,5 +53,10 @@ create or replace package test_output_buffer is --%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_ut_suite_tag_filter.pkb b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb index edfb27cfc..7ce6a1c3e 100644 --- a/test/ut3_tester/core/test_ut_suite_tag_filter.pkb +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb @@ -62,6 +62,15 @@ create or replace package body test_ut_suite_tag_filter is 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 @@ -78,5 +87,37 @@ create or replace package body test_ut_suite_tag_filter is 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)) + ); + + 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 index 0f84b751b..e7b255d4b 100644 --- a/test/ut3_tester/core/test_ut_suite_tag_filter.pks +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pks @@ -24,10 +24,17 @@ create or replace package test_ut_suite_tag_filter is --%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; + 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_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index 9d65d6667..c56f31dc0 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -211,7 +211,7 @@ create or replace package body run_helper is 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; @@ -926,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 ad0f615e5..6c5dd4a3b 100644 --- a/test/ut3_tester_helper/run_helper.pks +++ b/test/ut3_tester_helper/run_helper.pks @@ -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_user/api/test_ut_run.pkb b/test/ut3_user/api/test_ut_run.pkb index f0cfd8373..b4040bb43 100644 --- a/test/ut3_user/api/test_ut_run.pkb +++ b/test/ut3_user/api/test_ut_run.pkb @@ -854,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 => 6 ) ) + 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 %'; 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 f3aad5702..4b06f725f 100644 --- a/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb +++ b/test/ut3_user/reporters/test_coverage/test_proftab_coverage.pkb @@ -207,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; /