diff --git a/.gitattributes b/.gitattributes
index 02d2d5e4e..9cf0f2457 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9,3 +9,8 @@ tests export-ignore
development export-ignore
node_modules export-ignore
^docs/* linguist-documentation
+*.pkb linguist-language=PLSQL
+*.pks linguist-language=PLSQL
+*.sql linguist-language=PLSQL
+*.tpb linguist-language=PLSQL
+*.tps linguist-language=PLSQL
diff --git a/.travis.yml b/.travis.yml
index 7e9d94ccf..b48adb107 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,13 +5,11 @@ addons:
apt:
packages:
- unzip
- # Java9 Required for Sonar and SQLCL
- - oracle-java9-installer
- - oracle-java9-set-default
+ # Java9+ Required for Sonar and SQLCL
+ - openjdk-9-jre-headless
sonarcloud:
organization: utplsql
- token:
- secure: ${SONAR_TOKEN}
+ token: ${SONAR_TOKEN}
services:
- docker
@@ -47,7 +45,7 @@ env:
#utPLSQL released version directory
- UTPLSQL_DIR="utPLSQL_latest_release"
- SELFTESTING_BRANCH=${TRAVIS_BRANCH}
- - UTPLSQL_CLI_VERSION="3.1.6"
+ - UTPLSQL_CLI_VERSION="3.1.8"
# Maven
- MAVEN_HOME=/usr/local/maven
- MAVEN_CFG=$HOME/.m2
diff --git a/VERSION b/VERSION
index 25742a1c4..76cb3f689 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v3.1.8
+v3.1.9
diff --git a/docs/about/authors.md b/docs/about/authors.md
index a18bfca23..776e5cd44 100644
--- a/docs/about/authors.md
+++ b/docs/about/authors.md
@@ -1,4 +1,4 @@
-
+
### utPLSQL v3 Major Contributors
diff --git a/docs/about/license.md b/docs/about/license.md
index 832b9e394..edf1181bd 100644
--- a/docs/about/license.md
+++ b/docs/about/license.md
@@ -1,4 +1,4 @@
-
+
# Version Information
diff --git a/docs/about/project-details.md b/docs/about/project-details.md
index f8dfb024c..46ddbf89b 100644
--- a/docs/about/project-details.md
+++ b/docs/about/project-details.md
@@ -1,4 +1,4 @@
-
+
# utPLSQL Project Details
diff --git a/docs/about/support.md b/docs/about/support.md
index 1e2dcf3cc..4092c00c9 100644
--- a/docs/about/support.md
+++ b/docs/about/support.md
@@ -1,4 +1,4 @@
-
+
# How to get support
diff --git a/docs/compare_version2_to_3.md b/docs/compare_version2_to_3.md
new file mode 100644
index 000000000..5f53e41d4
--- /dev/null
+++ b/docs/compare_version2_to_3.md
@@ -0,0 +1,56 @@
+For version 3 has been a complete rewrite of the framework, the way it can be used is different to
+the previous versions, but also more in line with other modern unit-testing frameworks like JUnit and RSpec.
+
+There is a [migration tool](https://github.com/utPLSQL/utPLSQL-v2-v3-migration) that can help you to migrate your existing utPLSQL v2 tests to the v3 capabilities.
+
+# Feature comparison
+
+| Feature | Version 2 | Version 3 |
+| -------------------------------------- | ---------------------- | ---------------------- |
+| Easy to install | Yes | Yes |
+| Documentation | Yes | Yes |
+| License | GPL v2 | Apache 2.0 |
+| **Tests Creation** | | |
+| Declarative test configuration | No | Yes - Annotations1|
+| Tests as Packages | Yes | Yes |
+| Multiple Tests in a single Package | Yes | Yes |
+| Optional Setup/Teardown | No | Yes |
+| Different Setup/Teardown
For Each Test in a Single Package| No | Yes - Annotations1 |
+| Suite Definition Storage | Tables | Package - Annotations1 |
+| Multiple Suites | Yes | Yes |
+| Suites can contain Suites | No | Yes |
+| Automatic Test detection | No | Yes - Annotations1|
+| Unconstrained naming of Test packages | No - prefixes | Yes - name not relevant|
+| Require Prefix on Test procedures | No - prefixes | Yes - name not relevant|
+| Auto Compilation of Tests | Yes | No (Let us know if you use this) |
+| Assertion Library | 30 assertions2 | 26 matchers (13 + 13 negated) |
+| Extendable assertions | No | Yes - custom matchers |
+| PLSQL Record Assertions | generated code through **utRecEq** Package | [possible on Oracle 12c+](https://oracle-base.com/articles/12c/using-the-table-operator-with-locally-defined-types-in-plsql-12cr1) using [cursor matchers](userguide/expectations.md#comparing-cursors)|
+| Test Skeleton Generation | Yes | No (Let us know if you use this) |
+| **Test Execution3** | | |
+| Single Test Package Execution | Yes | Yes |
+| Single Test Procedure Execution | No | Yes |
+| Test Suite Execution | Yes | Yes |
+| Subset of Suite Execution | No | Yes |
+| Multiple Suite Execution | No | Yes |
+| Organizing Suites into hierarchies | No | Yes |
+| **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 |
+| Multiple Output Reporters can be used during test execution | No| Yes |
+| DBMS_OUTPUT | Yes | Yes (clean formatting) |
+| File | Yes (to db server only)| Yes (on client side) |
+| Stored in Table | Yes | No (can be added as custom reporter) |
+| XUnit format support | No | Yes |
+| HTML Format | Yes | No |
+| Custom Output reporter | Yes-needs configuration| Yes - no config needed |
+
+1 Annotations are specially formatted comments in your package specification. This enables *declarative* test configuration that is coupled with the source code. See Documentation for more details.
+
+2 **utAssert2** package - Contains 59 Assertions - 2 Not implemented = 57, 28 are duplicated only change on outcome_in parameter 57-28 = 29, **utPipe** package - Contains 1 Assertion 29 + 1 = 30
+
+3 Test execution comparison is in a single call so the results are combined. We know it was always possible to group in any way with multiple calls. But that may not be desired under a CI system where you want a single JUnit XML Output.
diff --git a/docs/index.md b/docs/index.md
index 4ed6794ec..20d119f93 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,4 +1,4 @@
-
+
# Introduction to utPLSQL
@@ -25,6 +25,7 @@ The framework follows industry standards and best patterns of modern Unit Testin
- [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
diff --git a/docs/userguide/advanced_data_comparison.md b/docs/userguide/advanced_data_comparison.md
index 60f00e4bb..36ed86ae6 100644
--- a/docs/userguide/advanced_data_comparison.md
+++ b/docs/userguide/advanced_data_comparison.md
@@ -1,4 +1,4 @@
-
+
# Advanced data comparison
diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md
index 3567ffd96..33950623d 100644
--- a/docs/userguide/annotations.md
+++ b/docs/userguide/annotations.md
@@ -1,19 +1,124 @@
-
+
# Annotations
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 configuration files or tables are needed. The annotation names are based on popular testing frameworks such as JUnit.
+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.
-Annotations are interpreted only in the package specification and are case-insensitive. We strongly recommend using lower-case annotations as described in this documentation.
+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.
-There are two distinct types of annotations, identified by their location in package:
-- Procedure level annotations - placed directly before a procedure (`--%test`, `--%beforeall`, `--%beforeeach` etc.).
-- Package level annotations - placed at any place in package except directly before procedure (`--%suite`, `--%suitepath` etc.).
+All of 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`
+
+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
+
+Example:
+ `--%suite(Stuff) -- we should name this ( correctly )` - represents `suite` annotation with text `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.
+
+There are two distinct types of annotations, identified by their location in package.
+- package annotations
+- procedure annotations
+
+### Procedure level annotations
+
+Annotation placed directly before a procedure (`--%test`, `--%beforeall`, `--%beforeeach` etc.).
+There **can not** be any empty lines or comments between annotation line and procedure line.
+There can be many annotations for a procedure.
+
+Valid procedure annotations example:
+```sql
+package test_package is
+ --%suite
+
+
+ --%test()
+ --%disabled
+ procedure my_first_procedure;
+
+ $if dbms_db_version.version >= 12 $then --This is ok - annotation before procedure
+ --%test()
+ procedure my_first_procedure;
+ $end
+
+ --A comment goes before annotations
+ --%test()
+ procedure my_first_procedure;
+end;
+```
+
+Invalid procedure annotations examples:
+```sql
+package test_package is
+ --%suite
+
+ --%test() --This is wrong as there is an empty line between procedure and annotation
+
+ procedure my_first_procedure;
+
+ --%test()
+ --This is wrong as there is a comment line between procedure and annotation
+ procedure proc1;
+
+ --%test() --This is wrong as there is a compiler directive between procedure and annotation
+ $if dbms_db_version.version >= 12 $then
+ procedure proc_12;
+ $end
+
+ --%test()
+ -- procedure another_proc;
+ /* The above is wrong as the procedure is commented out
+ and annotation is not procedure annotation anymore */
+
+end;
+```
+
+### Package level annotations
+
+Those annotations placed at any place in package except directly before procedure (`--%suite`, `--%suitepath` etc.).
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
+package test_package is
+
+ --%suite
+
+ --%suitepath(org.utplsql.example)
+
+ --%beforeall(some_package.some_procedure)
+
+ --%context
+
+ --%test()
+ procedure my_first_procedure;
+ --%endcontext
+end;
+```
+
+Invalid package annotations examples:
+```sql
+package test_package is
+ --%suite --This is wrong as suite annotation is not a procedure annotation
+ procedure irrelevant;
+
+ --%context --This is wrong as there is no empty line between package level annotation and procedure level annotation
+ --%test()
+ procedure my_first_procedure;
+
+end;
+```
## Supported annotations
@@ -21,9 +126,9 @@ We strongly recommend putting package level annotations at the very top of packa
| --- | --- | --- |
| `--%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. Provides description to a `context` when used within `context`. When used with `test` or `suite` annotation, overrides the `` provided with `suite`/`test`. |
+| `--%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 contant names, exception constant names, predefined Oracle exception names. |
+| `--%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. |
@@ -36,7 +141,8 @@ We strongly recommend putting package level annotations at the very top of packa
| `--%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 |
+| `--%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 |
@@ -997,9 +1103,9 @@ In most of the cases, the code to be tested is consisting of PLSQL packages cont
When creating test suites, it's quite common to maintain `one to one` relationship between test suite packages and tested code.
When it comes to test procedures themselves, it is best practice to have one test procedure for one tested behavior of the code that is tested.
-The relationship between test procedure and tested procedure/function will be therefore `many to one` in most of the cases.
+The relationship between test procedure and tested code will be therefore `many to one` or `many to many` in most of the cases.
-With this comes a challenge. How to group tests, related to one tested procedure, so that it is obvious that they relate to the same code.
+With this comes a challenge. How to group tests, related to one tested behavior, so that it is obvious that they relate to the same thing.
This is where utPLSQL contexts come handy.
@@ -1008,18 +1114,23 @@ 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:
-- start with the `--%context` annotation and ends with `--%endcontext`
-- can have a name provided as parameter for example `--%context(remove_rooms_by_name)`
-- when no name is provided for context, the context is names `context_N` where `N` is the number of the context in suite
-- can have their own `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures
-- `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures defined at suite level, propagate to context
-- test suite package can have multiple contexts in it
-- contexts cannot be nested
-
+- 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.
+- contexts can be nested, you can place a context inside another context
+- when no name is provided for context, the context is named `context_N` where `N` is the number of the context in suite or parent context.
+- context name must be unique within it's parent (suite / parent context)
+- if context name is not unique within it's parent, context and it's entire content is excluded from execution
+- context name should not contain spaces or special characters
+- context name cannot contain a `.` (full stop/period) character
+- suite/context can have multiple nested sibling contexts in it
+- contexts can have their own `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures
+- `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures defined at ancestor level, propagate to context
+- if `--%endcontext` is missing for a context, the context spans to the end of package specification
The below example illustrates usage of `--%context` for separating tests for individual procedures of package.
-Tested tables and code
+Sample tables and code
```sql
create table rooms (
room_key number primary key,
@@ -1078,8 +1189,8 @@ end;
Below test suite defines:
- `--%beforeall` outside of context, that will be executed before all tests
-- `--%context(remove_rooms_by_name)` to group tests for `remove_rooms_by_name` procedure
-- `--%context(add_rooms_content)` to group tests for `add_rooms_content` procedure
+- `--%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
create or replace package test_rooms_management is
@@ -1103,7 +1214,6 @@ create or replace package test_rooms_management is
--%endcontext
-
--%context(add_rooms_content)
--%displayname(Add content to a room)
@@ -1221,15 +1331,213 @@ Finished in .035261 seconds
5 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s)
```
+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
+create or replace package queue_spec as
+ --%suite(Queue specification)
+
+ --%context(A new queue)
+
+ --%test(Is empty)
+ procedure is_empty;
+ --%test(Preserves positive bounding capacity)
+ procedure positive_bounding_capacity;
+ --%test(Cannot be created with non positive bounding capacity)
+ procedure non_positive_bounding_cap;
+ --%endcontext
+ --%context(An empty queue)
+
+ --%test(Dequeues an empty value)
+ procedure deq_empty_value;
+ --%test(Remains empty when null enqueued)
+ procedure empty_with_null_enq;
+ --%test(Becomes non empty when non null value enqueued)
+ procedure non_empty_after_enq;
+ --%endcontext
+ --%context(A non empty queue)
+
+ --%context(that is not full)
+
+ --%test(Becomes longer when non null value enqueued)
+ procedure grow_on_enq_non_null;
+ --%test(Becomes full when enqueued up to capacity)
+ procedure full_on_enq_to_cap;
+ --%endcontext
+ --%context(that is full)
+
+ --%test(Ignores further enqueued values)
+ procedure full_ignore_enq;
+ --%test(Becomes non full when dequeued)
+ procedure non_full_on_deq;
+ --%endcontext
+
+ --%test(Dequeues values in order enqueued)
+ procedure dequeue_ordered;
+ --%test(Remains unchanged when null enqueued)
+ procedure no_change_on_null_enq;
+ --%endcontext
+end;
+```
+
+
+When such specification gets executed `ut.run('queue_spec'')` (without body created) you will see the nesting of tests within contexts.
+```
+Queue specification
+ An empty queue
+ Dequeues an empty value [.014 sec] (FAILED - 1)
+ Remains empty when null enqueued [.004 sec] (FAILED - 2)
+ Becomes non empty when non null value enqueued [.005 sec] (FAILED - 3)
+ A non empty queue
+ that is not full
+ Becomes longer when non null value enqueued [.005 sec] (FAILED - 4)
+ Becomes full when enqueued up to capacity [.005 sec] (FAILED - 5)
+ That is full
+ Ignores further enqueued values [.004 sec] (FAILED - 6)
+ Becomes non full when dequeued [.005 sec] (FAILED - 7)
+ Dequeues values in order enqueued [.006 sec] (FAILED - 8)
+ Remains unchanged when null enqueued [.004 sec] (FAILED - 9)
+ A new queue
+ Is empty [.007 sec] (FAILED - 10)
+ Preserves positive bounding capacity [.006 sec] (FAILED - 11)
+ Cannot be created with non positive bounding capacity [.005 sec] (FAILED - 12)
+Failures:
+ 1) deq_empty_value
+ ORA-04067: not executed, package body "UT3.QUEUE_SPEC" does not exist
+ ORA-06508: PL/SQL: could not find program unit being called: "UT3.QUEUE_SPEC"
+ ORA-06512: at line 6
+...
+Finished in .088573 seconds
+12 tests, 0 failed, 12 errored, 0 disabled, 0 warning(s)
+```
+
+Suite nesting allows for organizing tests into human-readable specification of behavior.
+
+### Name
+The `--%name` annotation is currently only used only for naming a context.
+If a context doesn't have explicit name specified, then the name is given automatically by framework.
+
+The automatic name will be `context_#n` where `n` is a context number within a suite/parent context.
+
+The `--%name` can be useful when you would like to run only a specific context or its items by `suitepath`.
+
+Consider the below example.
+
+```sql
+create or replace package queue_spec as
+ --%suite(Queue specification)
+
+ --%context(A new queue)
+
+ --%test(Cannot be created with non positive bounding capacity)
+ procedure non_positive_bounding_cap;
+ --%endcontext
+ --%context(An empty queue)
+
+ --%test(Becomes non empty when non null value enqueued)
+ procedure non_empty_after_enq;
+ --%endcontext
+ --%context(A non empty queue)
+
+ --%context(that is not full)
+
+ --%test(Becomes full when enqueued up to capacity)
+ procedure full_on_enq_to_cap;
+ --%endcontext
+ --%context(that is full)
+
+ --%test(Becomes non full when dequeued)
+ procedure non_full_on_deq;
+ --%endcontext
+
+ --%endcontext
+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 |
+
+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
+ exec ut.run(':queue_spec.context_#3.context_#1');
+```
+
+You can use `--%name` annotation to explicitly name contexts on suitepath.
+```sql
+create or replace package queue_spec as
+ --%suite(Queue specification)
+
+ --%context(A new queue)
+ --%name(a_new_queue)
+
+ --%test(Cannot be created with non positive bounding capacity)
+ procedure non_positive_bounding_cap;
+ --%endcontext
+ --%context(An empty queue)
+ --%name(an_empty_queue)
+
+ --%test(Becomes non empty when non null value enqueued)
+ procedure non_empty_after_enq;
+ --%endcontext
+ --%context(A non empty queue)
+ --%name(a_non_empty_queue)
+
+ --%context(that is not full)
+ --%name(that_is_not_full)
+
+ --%test(Becomes full when enqueued up to capacity)
+ procedure full_on_enq_to_cap;
+ --%endcontext
+ --%context(that is full)
+ --%name(that_is_full)
+
+ --%test(Becomes non full when dequeued)
+ procedure non_full_on_deq;
+ --%endcontext
+
+ --%endcontext
+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 |
+
+
+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
### Tags
-Tag is a label attached to the test or a suite path. It is used for identification and execution a group of tests / suites that share same tag.
+Tag is a label attached to the test or a suite. It is used for identification and execution of a group of tests / suites that share the same tag.
-It allows us to group a tests / suites using a various categorization and place a test / suite in multiple buckets. Same tests can be group with other tests based on the functionality , frequency, type of output etc.
+It allows for grouping of tests / suites using various categorization and place tests / suites in multiple buckets. Same tests can be grouped with other tests based on the functionality , frequency, type of output etc.
-e.q.
+e.g.
```sql
--%tags(batch,daily,csv)
@@ -1238,29 +1546,31 @@ e.q.
or
```sql
---%tags(api,online,json)
+--%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.
-Tags are defined as a coma separated list. When executing a test run with tag filter applied, framework will find all tests associated with given tags and execute them. Framework applies `OR` logic when resolving a tags so any tests / suites that match at least one tag will be included in the test run.
-
-When a suite gets tagged all of its children will automatically inherit a tag and get executed along the parent. Parent suit tests are not executed. but a suitepath hierarchy is kept.
+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 tag package.
+Sample test suite package with tags.
```sql
-create or replace package ut_sample_test IS
+create or replace package ut_sample_test is
--%suite(Sample Test Suite)
- --%tag(suite1)
+ --%tags(api)
--%test(Compare Ref Cursors)
- --%tag(test1,sample)
+ --%tags(complex,fast)
procedure ut_refcursors1;
--%test(Run equality test)
- --%tag(test2,sample)
+ --%tags(simple,fast)
procedure ut_test;
end ut_sample_test;
@@ -1287,30 +1597,47 @@ end ut_sample_test;
/
```
-Execution of the test is done by using a parameter `a_tags`
+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 => 'suite1'));
-select * from table(ut.run(a_tags => 'test1,test2'));
-select * from table(ut.run(a_tags => 'sample'));
+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`
-begin
- ut.run(a_path => 'ut_sample_test',a_tags => 'suite1');
-end;
-/
+```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`
-exec ut.run('ut_sample_test', a_tags => 'sample');
+```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`
+#### Tag naming convention
+Tags must follow the below naming convention:
-Tags should adhere to following rules:
+- tag is case sensitive
+- tag can contain special characters like `$#/\?-!` etc.
+- tag cannot be an empty string
+- 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
-- tags are case sensitive
-- tags cannot be an empty string
-- tags cannot contain spaces e.g. to create a multi-word `tag` please use underscores,dashes, dots etc. e.g. `test_of_batch`
-- tags with empty spaces will be ignored during execution
-- tags can contain special characters
+
+#### 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.
@@ -1331,8 +1658,9 @@ If you want to create tests for your application it is recommended to structure
* Payments recognition
* Payments set off
-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:
+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
create or replace package test_payment_recognition as
@@ -1367,8 +1695,8 @@ create or replace package test_payment_set_off as
end test_payment_set_off;
```
-When you execute tests for your application, the framework constructs a test suite for each test package. Then it combines suites into grouping suites by the `%suitepath` annotation value so that the fully qualified path to the `recognize_by_num` procedure is `USER:payments.test_payment_recognition.test_recognize_by_num`. If any of its expectations fails then the test is marked as failed, also the `test_payment_recognition` suite, the parent suite `payments` and the whole run is marked as failed.
-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.
+When you execute tests for your application, the framework constructs a test suite for each test package. Then it combines suites into grouping suites by the `--%suitepath` annotation value so that the fully qualified path to the `recognize_by_num` procedure is `USER:payments.test_payment_recognition.test_recognize_by_num`. If any of its expectations fails then the test is marked as failed, also the `test_payment_recognition` suite, the parent suite `payments` and the whole run is marked as failed.
+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
@@ -1384,9 +1712,10 @@ create or replace package payments as
end payments;
```
-A `%suitepath` can be provided in three ways:
+
+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 in all suites from suite1[.suite2][.suite3]...[.procedure] path. If schema is not provided, then the current schema is used. Example: `:all.rooms_tests`
+* [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.
diff --git a/docs/userguide/best-practices.md b/docs/userguide/best-practices.md
index ae116381f..3378cafe4 100644
--- a/docs/userguide/best-practices.md
+++ b/docs/userguide/best-practices.md
@@ -1,4 +1,4 @@
-
+
# Best Practices
diff --git a/docs/userguide/coverage.md b/docs/userguide/coverage.md
index 9b8873f47..203daffe0 100644
--- a/docs/userguide/coverage.md
+++ b/docs/userguide/coverage.md
@@ -1,4 +1,4 @@
-
+
# Coverage
utPLSQL comes with a built-in coverage reporting engine. The code coverage reporting is based on the DBMS_PROFILER package provided with Oracle database.
diff --git a/docs/userguide/exception-reporting.md b/docs/userguide/exception-reporting.md
index b023a50c8..3d01aafbd 100644
--- a/docs/userguide/exception-reporting.md
+++ b/docs/userguide/exception-reporting.md
@@ -1,4 +1,4 @@
-
+
# Exception handling and reporting
diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md
index 36d2cd79f..fe2369957 100644
--- a/docs/userguide/expectations.md
+++ b/docs/userguide/expectations.md
@@ -1,4 +1,4 @@
-
+
# 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.
diff --git a/docs/userguide/getting-started.md b/docs/userguide/getting-started.md
index 4adc482aa..aab93b2f8 100644
--- a/docs/userguide/getting-started.md
+++ b/docs/userguide/getting-started.md
@@ -1,4 +1,4 @@
-
+
# Getting started with TDD and utPLSQL
diff --git a/docs/userguide/install.md b/docs/userguide/install.md
index 289439ddf..3d6e23843 100644
--- a/docs/userguide/install.md
+++ b/docs/userguide/install.md
@@ -1,4 +1,4 @@
-
+
# Downloading latest version of utPLSQL
@@ -141,7 +141,7 @@ sqlplus sys/sys_pass@db as sysdba @install_headless_with_trigger.sql utp3 my_ver
**Note:**
>When installing utPLSQL into database with existing unit test packages, utPLSQL will not be able to already-existing unit test packages. When utPSLQL was installed with DDL trigger, you have to do one of:
>- Recompile existing Unit Test packages to make utPLSQL aware of their existence
->- Invoke `exec ut_runner.rebuild_annotation_cache(a_schema_name=> ... );` for every schema containing unit tests in your database
+>- Invoke `exec ut_runner.rebuild_annotation_cache(a_object_owner=> ... );` for every schema containing unit tests in your database
>
> Steps above are required to assure annotation cache is populated properly from existing objects. Rebuilding annotation cache might be faster than code recompilation.
diff --git a/docs/userguide/querying_suites.md b/docs/userguide/querying_suites.md
index 173e696c6..ceb1cbb13 100644
--- a/docs/userguide/querying_suites.md
+++ b/docs/userguide/querying_suites.md
@@ -1,4 +1,4 @@
-
+
# Qyerying for test suites
diff --git a/docs/userguide/reporters.md b/docs/userguide/reporters.md
index 379948e0d..798b2fd0b 100644
--- a/docs/userguide/reporters.md
+++ b/docs/userguide/reporters.md
@@ -1,4 +1,4 @@
-
+
utPLSQL provides the following reporting formats.
diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md
index 21bcc77fc..2c8b4ebdd 100644
--- a/docs/userguide/running-unit-tests.md
+++ b/docs/userguide/running-unit-tests.md
@@ -1,4 +1,4 @@
-
+
# Running tests
@@ -282,6 +282,27 @@ select * from table(ut.run('hr.test_apply_bonus', a_random_test_order_seed => 30
**Note**
>Random order seed must be a positive number within range of 1 .. 1 000 000 000.
+# 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;
+```
+```sql
+select * from table(ut.run('hr.test_apply_bonus', a_tags => 'suite1'))
+```
+
+You can also exclude specific tags by adding a `-` (dash) in front of the tag
+
+```sql
+select * from table(ut.run('hr.test_apply_bonus', a_tags => '-suite1'))
+```
+
# 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.
diff --git a/docs/userguide/upgrade.md b/docs/userguide/upgrade.md
index e4fe75b72..5e48ee239 100644
--- a/docs/userguide/upgrade.md
+++ b/docs/userguide/upgrade.md
@@ -1,4 +1,4 @@
-
+
# Upgrading from version 2
diff --git a/readme.md b/readme.md
index 315124db6..2ccf8ee68 100644
--- a/readme.md
+++ b/readme.md
@@ -2,8 +2,9 @@
----------
-[](https://github.com/utPLSQL/utPLSQL/releases)
[](https://www.apache.org/licenses/LICENSE-2.0)
+[](https://github.com/utPLSQL/utPLSQL/releases)
+[](https://github.com/utPLSQL/utPLSQL/releases)
[](http://utplsql-slack-invite.herokuapp.com/)
[](https://twitter.com/utPLSQL)
@@ -168,62 +169,6 @@ To sign up to the chat use [this link](http://utplsql-slack-invite.herokuapp.com
----------
[__Authors__](docs/about/authors.md)
-----------
-__Version 2 to Version 3 Comparison__
-
-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](http://utplsql-slack-invite.herokuapp.com/).
-
-
-| Feature | Version 2 | Version 3 |
-| -------------------------------------- | ---------------------- | ---------------------- |
-| Easy to install | Yes | Yes |
-| Documentation | Yes | Yes |
-| License | GPL v2 | Apache 2.0 |
-| **Tests Creation** | | |
-| Declarative test configuration | No | Yes - Annotations1|
-| Tests as Packages | Yes | Yes |
-| Multiple Tests in a single Package | Yes | Yes |
-| Optional Setup/Teardown | No | Yes |
-| Different Setup/Teardown
For Each Test in a Single Package| No | Yes - Annotations1 |
-| Suite Definition Storage | Tables | Package - Annotations1 |
-| Multiple Suites | Yes | Yes |
-| Suites can contain Suites | No | Yes |
-| Automatic Test detection | No | Yes - Annotations1|
-| Unconstrained naming of Test packages | No - prefixes | Yes - name not relevant|
-| Require Prefix on Test procedures | No - prefixes | Yes - name not relevant|
-| Auto Compilation of Tests | Yes | No (Let us know if you use this) |
-| Assertion Library | 30 assertions2 | 26 matchers (13 + 13 negated) |
-| Extendable assertions | No | Yes - custom matchers |
-| PLSQL Record Assertions | generated code through **utRecEq** Package | [possible on Oracle 12c+](https://oracle-base.com/articles/12c/using-the-table-operator-with-locally-defined-types-in-plsql-12cr1) using [cursor matchers](docs/userguide/expectations.md#comparing-cursors)|
-| Test Skeleton Generation | Yes | No (Let us know if you use this) |
-| **Test Execution3** | | |
-| Single Test Package Execution | Yes | Yes |
-| Single Test Procedure Execution | No | Yes |
-| Test Suite Execution | Yes | Yes |
-| Subset of Suite Execution | No | Yes |
-| Multiple Suite Execution | No | Yes |
-| Organizing Suites into hierarchies | No | Yes |
-| **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 |
-| Multiple Output Reporters can be used during test execution | No| Yes |
-| DBMS_OUTPUT | Yes | Yes (clean formatting) |
-| File | Yes (to db server only)| Yes (on client side) |
-| Stored in Table | Yes | No (can be added as custom reporter) |
-| XUnit format support | No | Yes |
-| HTML Format | Yes | No |
-| Custom Output reporter | Yes-needs configuration| Yes - no config needed |
-
-1 Annotations are specially formatted comments in your package specification. This enables *declarative* test configuration that is coupled with the source code. See Documentation for more details.
-
-2 **utAssert2** package - Contains 59 Assertions - 2 Not implemented = 57, 28 are duplicated only change on outcome_in parameter 57-28 = 29, **utPipe** package - Contains 1 Assertion 29 + 1 = 30
-
-3 Test execution comparison is in a single call so the results are combined. We know it was always possible to group in any way with multiple calls. But that may not be desired under a CI system where you want a single JUnit XML Output.
-
----------
__Project Directories__
@@ -236,7 +181,15 @@ __Project Directories__
* source - The installation code for utPLSQL
* tests - Tests for utPLSQL framework
------------
+----------
+
+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](http://utplsql-slack-invite.herokuapp.com/).
+
+
+# Version 2 to Version 3 Comparison
+
+[Version 2 to Version 3 Comparison](docs/compare_version2_to_3.md)
+
# Supporters
The utPLSQL project is community-driven and is not commercially motivated. Nonetheless, donations and other contributions are always welcome, and are detailed below.
@@ -249,3 +202,4 @@ The utPLSQL project is community-driven and is not commercially motivated. Nonet
+
diff --git a/sonar-project.properties b/sonar-project.properties
index b9220be6e..da061019b 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -2,7 +2,7 @@
sonar.projectKey=utPLSQL
# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=utPLSQL
-sonar.projectVersion=v3.1.8
+sonar.projectVersion=v3.1.9
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# Since SonarQube 4.2, this property is optional if sonar.modules is set.
diff --git a/source/check_sys_grants.sql b/source/check_sys_grants.sql
index a31e9a9e1..7b1644952 100644
--- a/source/check_sys_grants.sql
+++ b/source/check_sys_grants.sql
@@ -21,7 +21,7 @@ begin
(select privilege
from user_sys_privs
union all
- select replace(privilege,' ANY ') privilege
+ select replace(privilege,' ANY') privilege
from user_sys_privs)
);
if l_missing_grants is not null then
diff --git a/source/core/ut_expectation_processor.pkb b/source/core/ut_expectation_processor.pkb
index 0c2580220..d65d210c8 100644
--- a/source/core/ut_expectation_processor.pkb
+++ b/source/core/ut_expectation_processor.pkb
@@ -163,7 +163,7 @@ create or replace package body ut_expectation_processor as
end;
function cut_address_columns( a_stack varchar2 ) return varchar2 is
begin
- return regexp_replace( a_stack, '^(0x)?[0-9a-f]+\s+', '', 1, 0, 'm' );
+ return regexp_replace( a_stack, '^(0x)?[0-9a-f]+\s+', '', 1, 0, 'mi' );
end;
function cut_framework_stack( a_stack varchar2 ) return varchar2 is
begin
diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb
index 101ab2179..c7e968467 100644
--- a/source/core/ut_suite_builder.pkb
+++ b/source/core/ut_suite_builder.pkb
@@ -36,6 +36,7 @@ create or replace package body ut_suite_builder is
gc_throws constant t_annotation_name := 'throws';
gc_rollback constant t_annotation_name := 'rollback';
gc_context constant t_annotation_name := 'context';
+ gc_name constant t_annotation_name := 'name';
gc_endcontext constant t_annotation_name := 'endcontext';
type tt_annotations is table of t_annotation_name;
@@ -57,6 +58,7 @@ create or replace package body ut_suite_builder is
gc_throws,
gc_rollback,
gc_context,
+ gc_name,
gc_endcontext
);
@@ -321,7 +323,8 @@ create or replace package body ut_suite_builder is
a_procedure_name t_object_name := null
) is
l_annotation_pos binary_integer;
- l_tag_list ut_varchar2_list := ut_varchar2_list();
+ l_tags_list ut_varchar2_list := ut_varchar2_list();
+ l_tag_items ut_varchar2_list;
begin
l_annotation_pos := a_tags_ann_text.first;
while l_annotation_pos is not null loop
@@ -331,14 +334,25 @@ create or replace package body ut_suite_builder is
|| get_object_reference( a_suite, a_procedure_name, l_annotation_pos )
);
else
- l_tag_list := l_tag_list multiset union distinct ut_utils.trim_list_elements(
- ut_utils.string_to_table(a_tags_ann_text(l_annotation_pos),',')
- );
+ l_tag_items := ut_utils.trim_list_elements(ut_utils.string_to_table(a_tags_ann_text(l_annotation_pos),','));
+ if l_tag_items is not empty then
+ for i in 1 .. l_tag_items.count loop
+ if regexp_like(l_tag_items(i),'^[^-](\S)+$') then
+ l_tags_list.extend();
+ l_tags_list(l_tags_list.last) := l_tag_items(i);
+ else
+ a_suite.put_warning(
+ 'Invalid value "'||l_tag_items(i)||'" for "--%tags" annotation. See documentation for details on valid tag values. Annotation value ignored.'
+ || get_object_reference( a_suite, a_procedure_name, l_annotation_pos )
+ );
+ end if;
+ end loop;
+ end if;
end if;
l_annotation_pos := a_tags_ann_text.next(l_annotation_pos);
end loop;
- --remove empty strings from table list e.g. tag1,,tag2 and conver to rows
- a_list := ut_utils.convert_collection( ut_utils.filter_list(l_tag_list,ut_utils.gc_word_no_space) );
+ --remove empty strings from table list e.g. tag1,,tag2 and convert to rows
+ a_list := ut_utils.convert_collection( ut_utils.filter_list(set(l_tags_list),ut_utils.gc_word_no_space) );
end;
procedure set_seq_no(
@@ -625,7 +639,7 @@ create or replace package body ut_suite_builder is
a_suite.path := lower(coalesce(a_suite.path, a_suite.object_name));
end;
- procedure add_suite_tests(
+ procedure add_tests_to_items(
a_suite in out nocopy ut_suite,
a_annotations t_annotations_info,
a_suite_items in out nocopy ut_suite_items
@@ -735,59 +749,121 @@ create or replace package body ut_suite_builder is
return l_result;
end;
- procedure get_suite_contexts_items(
- a_suite in out nocopy ut_suite,
+ procedure get_context_items(
+ a_parent in out nocopy ut_suite,
a_annotations in out nocopy t_annotations_info,
- a_suite_items out nocopy ut_suite_items
+ a_suite_items out nocopy ut_suite_items,
+ a_parent_context_pos in integer := 0
) is
- l_context_pos t_annotation_position;
- l_end_context_pos t_annotation_position;
- l_context_name t_object_name;
- l_ctx_annotations t_annotations_info;
- l_context ut_suite_context;
- l_context_no binary_integer := 1;
- l_context_items ut_suite_items;
+ l_context_pos t_annotation_position;
+ l_next_context_pos t_annotation_position;
+ l_end_context_pos t_annotation_position;
+ l_ctx_annotations t_annotations_info;
+ l_context ut_suite_context;
+ l_context_no binary_integer := 1;
+ l_context_items ut_suite_items;
type tt_context_names is table of boolean index by t_object_name;
- l_context_names tt_context_names;
+ l_used_context_names tt_context_names;
+ l_context_name t_object_name;
+ l_default_context_name t_object_name;
+ function get_context_name(
+ a_parent in out nocopy ut_suite,
+ a_context_names in tt_annotation_texts,
+ a_start_position binary_integer,
+ a_end_position binary_integer
+ ) return varchar2 is
+ l_result t_annotation_name;
+ l_found boolean;
+ l_annotation_pos binary_integer;
+ begin
+ l_annotation_pos := a_context_names.first;
+ while l_annotation_pos is not null loop
+ if l_annotation_pos > a_start_position and l_annotation_pos < a_end_position then
+ if l_found then
+ add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos);
+ else
+ l_result := a_context_names(l_annotation_pos);
+ end if;
+ l_found := true;
+ end if;
+ l_annotation_pos := a_context_names.next(l_annotation_pos);
+ end loop;
+ return l_result;
+ end;
begin
a_suite_items := ut_suite_items();
if not a_annotations.by_name.exists(gc_context) then
return;
end if;
- l_context_pos := a_annotations.by_name( gc_context).first;
+ l_context_pos := a_annotations.by_name( gc_context).next(a_parent_context_pos);
while l_context_pos is not null loop
+ l_default_context_name := 'nested_context_#'||l_context_no;
+ l_context_name := null;
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
- exit when l_end_context_pos is null;
+ l_next_context_pos := a_annotations.by_name(gc_context).next(l_context_pos);
+ if a_annotations.by_name.exists(gc_name) then
+ l_context_name :=
+ get_context_name(
+ a_parent,
+ a_annotations.by_name( gc_name ),
+ l_context_pos,
+ least(
+ coalesce( l_end_context_pos, a_annotations.by_line.last ),
+ coalesce( l_next_context_pos, a_annotations.by_line.last )
+ )
+ );
+ end if;
+ if not regexp_like( l_context_name, '^(\w|[$#])+$' ) or l_context_name is null then
+ if not regexp_like( l_context_name, '^(\w|[$#])+$' ) then
+ a_parent.put_warning(
+ 'Invalid value "'||l_context_name||'" for context name.' ||
+ ' Context name ignored and fallback to auto-name "'||l_default_context_name||'" ' ||
+ get_object_reference( a_parent, null, l_context_pos )
+ );
+ end if;
+ l_context_name := l_default_context_name;
+ end if;
+ if l_used_context_names.exists(l_context_name) then
+ add_annotation_ignored_warning(
+ a_parent, gc_name,
+ 'Context name "'||l_context_name||'" already used in this scope. Name must be unique.' ||
+ ' Using fallback name '||l_default_context_name||'.', l_context_pos );
+ l_context_name := l_default_context_name;
+ end if;
+ l_used_context_names(l_context_name) := true;
- l_context_items := ut_suite_items();
- --create a sub-set of annotations to process as sub-suite (context)
- l_ctx_annotations := get_annotations_in_context( a_annotations, l_context_pos, l_end_context_pos);
+ 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.description := coalesce( a_annotations.by_line( l_context_pos ).text, l_context_name );
+ l_context.parse_time := a_annotations.parse_time;
- l_context_name := coalesce(
- l_ctx_annotations.by_line( l_context_pos ).text
- , gc_context||'_'||l_context_no
- );
- if l_context_names.exists(l_context_name) then
- add_annotation_ignored_warning( a_suite, 'context', 'Context name must be unique in a suite. Context and all of it''s content ignored.', l_context_pos );
+ --if nested context found
+ if l_next_context_pos < l_end_context_pos or l_end_context_pos is null then
+ get_context_items( l_context, a_annotations, l_context_items, l_context_pos );
+ l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
else
- l_context_names(l_context_name) := true;
+ l_context_items := ut_suite_items();
+ end if;
- l_context := ut_suite_context(a_suite.object_owner, a_suite.object_name, l_context_name, l_context_pos );
+ if l_end_context_pos is null then
+ a_parent.put_warning(
+ 'Missing "--%endcontext" annotation for a "--%context" annotation. The end of package is considered end of context.'|| get_object_reference( a_parent, null, l_context_pos )
+ );
+ l_end_context_pos := a_annotations.by_line.last;
+ end if;
- l_context.path := a_suite.path||'.'||l_context_name;
- l_context.description := l_ctx_annotations.by_line( l_context_pos ).text;
- l_context.parse_time := a_annotations.parse_time;
+ --create a sub-set of annotations to process as sub-suite (context)
+ l_ctx_annotations := get_annotations_in_context( a_annotations, l_context_pos, l_end_context_pos);
- warning_on_duplicate_annot( l_context, l_ctx_annotations.by_name, gc_context );
+ warning_on_duplicate_annot( l_context, l_ctx_annotations.by_name, gc_context );
- add_suite_tests( l_context, l_ctx_annotations, l_context_items );
- add_items_to_list(a_suite_items, l_context_items);
- a_suite_items.extend;
- a_suite_items(a_suite_items.last) := l_context;
- end if;
+ add_tests_to_items( l_context, l_ctx_annotations, l_context_items );
+ add_items_to_list(a_suite_items, l_context_items);
+ a_suite_items.extend;
+ a_suite_items(a_suite_items.last) := l_context;
-- remove annotations within context after processing them
delete_annotations_range(a_annotations, l_context_pos, l_end_context_pos);
@@ -798,27 +874,17 @@ create or replace package body ut_suite_builder is
end loop;
end;
- procedure warning_on_incomplete_context(
+ procedure warning_on_extra_endcontext(
a_suite in out nocopy ut_suite,
a_package_ann_index tt_annotations_by_name
) is
l_annotation_pos t_annotation_position;
begin
- if a_package_ann_index.exists(gc_context) then
- l_annotation_pos := a_package_ann_index(gc_context).first;
- while l_annotation_pos is not null loop
- add_annotation_ignored_warning(
- a_suite, gc_context, 'Invalid annotation %%%. Cannot find following "--%endcontext".',
- l_annotation_pos
- );
- l_annotation_pos := a_package_ann_index(gc_context).next(l_annotation_pos);
- end loop;
- end if;
if a_package_ann_index.exists(gc_endcontext) then
l_annotation_pos := a_package_ann_index(gc_endcontext).first;
while l_annotation_pos is not null loop
add_annotation_ignored_warning(
- a_suite, gc_endcontext, 'Invalid annotation %%%. Cannot find preceding "--%context".',
+ a_suite, gc_endcontext, 'Extra %%% annotation found. Cannot find corresponding "--%context".',
l_annotation_pos
);
l_annotation_pos := a_package_ann_index(gc_endcontext).next(l_annotation_pos);
@@ -889,12 +955,12 @@ create or replace package body ut_suite_builder is
warning_on_duplicate_annot( l_suite, l_annotations.by_name, gc_suite );
build_suitepath( l_suite, l_annotations );
- get_suite_contexts_items( l_suite, l_annotations, a_suite_items );
+ get_context_items( l_suite, l_annotations, a_suite_items );
--create suite tests and add
- add_suite_tests( l_suite, l_annotations, a_suite_items );
+ add_tests_to_items( l_suite, l_annotations, a_suite_items );
--by this time all contexts were consumed and l_annotations should not have any context/endcontext annotation in it.
- warning_on_incomplete_context( l_suite, l_annotations.by_name );
+ warning_on_extra_endcontext( l_suite, l_annotations.by_name );
a_suite_items.extend;
a_suite_items( a_suite_items.last) := l_suite;
diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb
index ef12e1def..f52d15bf7 100644
--- a/source/core/ut_suite_cache_manager.pkb
+++ b/source/core/ut_suite_cache_manager.pkb
@@ -30,8 +30,8 @@ create or replace package body ut_suite_cache_manager is
and ( {:path:}
and {:object_name:}
and {:procedure_name:}
+ )
)
- )
),
{:tags:}
suitepaths as (
@@ -106,8 +106,8 @@ create or replace package body ut_suite_cache_manager is
function get_path_sql(a_path in varchar2) return varchar2 is
begin
return case when a_path is not null then q'[
- :l_path||'.' like c.path || '.%' /*all children and self*/
- or ( c.path||'.' like :l_path || '.%' --all parents
+ :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;
@@ -129,22 +129,31 @@ create or replace package body ut_suite_cache_manager is
function get_tags_sql(a_tags_count in integer) return varchar2 is
begin
return case when a_tags_count > 0 then
- q'[filter_tags as (
+ q'[included_tags as (
select c.obj.path as path
from suite_items c
- where c.obj.tags multiset intersect :a_tag_list is not empty
- ),
+ 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 filter_tags t
- where t.path||'.' like c.obj.path || '.%' /*all children and self*/
- or c.obj.path||'.' like t.path || '.%' --all parents
+ 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_tag_list is null ),]'
+ 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;
end;
@@ -187,10 +196,23 @@ create or replace package body ut_suite_cache_manager is
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
@@ -216,7 +238,7 @@ create or replace package body ut_suite_cache_manager is
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_tags, a_random_seed;
+ 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;
return l_results;
end;
diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb
index 25bc32b34..1e4b63c7f 100644
--- a/source/core/ut_utils.pkb
+++ b/source/core/ut_utils.pkb
@@ -766,7 +766,7 @@ create or replace package body ut_utils is
/**
* Change string into unicode to match xmlgen format _00_
* https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adxdb/generation-of-XML-data-from-relational-data.html#GUID-5BE09A7D-80D8-4734-B9AF-4A61F27FA9B2
- * secion v3.1.8.3188
+ * secion v3.1.9.3268
*/
function char_to_xmlgen_unicode(a_character varchar2) return varchar2 is
begin
diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks
index 821c84dfe..f1a19efb9 100644
--- a/source/core/ut_utils.pks
+++ b/source/core/ut_utils.pks
@@ -21,7 +21,7 @@ create or replace package ut_utils authid definer is
*
*/
- gc_version constant varchar2(50) := 'v3.1.8.3188';
+ gc_version constant varchar2(50) := 'v3.1.9.3268';
subtype t_executable_type is varchar2(30);
gc_before_all constant t_executable_type := 'beforeall';
diff --git a/source/expectations/data_values/ut_compound_data_helper.pkb b/source/expectations/data_values/ut_compound_data_helper.pkb
index 575568641..d7c0aab76 100644
--- a/source/expectations/data_values/ut_compound_data_helper.pkb
+++ b/source/expectations/data_values/ut_compound_data_helper.pkb
@@ -28,46 +28,46 @@ create or replace package body ut_compound_data_helper is
with exp as (
select
ucd.*,
- {:duplicate_number:} dup_no
+ {:duplicate_number:} "UT3$_Dup#No"
from (
select
- ucd.item_data
- ,x.data_id data_id
- ,position + x.item_no item_no
+ ucd."UT3$_Item#Data"
+ ,x.data_id "UT3$_Data#Id"
+ ,ucd."UT3$_Position#" + x.item_no "UT3$_Item#No"
{:columns:}
from ut_compound_data_tmp x,
xmltable('/ROWSET/ROW' passing x.item_data columns
- item_data xmltype path '*'
- ,position for ordinality
+ "UT3$_Item#Data" xmltype path '*'
+ ,"UT3$_Position#" for ordinality
{:xml_to_columns:} ) ucd
- where data_id = :exp_guid
+ where x.data_id = :exp_guid
) ucd
)
, act as (
select
ucd.*,
- {:duplicate_number:} dup_no
+ {:duplicate_number:} "UT3$_Dup#No"
from (
select
- ucd.item_data
- ,x.data_id data_id
- ,position + x.item_no item_no
+ ucd."UT3$_Item#Data"
+ ,x.data_id "UT3$_Data#Id"
+ ,ucd."UT3$_Position#" + x.item_no "UT3$_Item#No"
{:columns:}
from ut_compound_data_tmp x,
xmltable('/ROWSET/ROW' passing x.item_data columns
- item_data xmltype path '*'
- ,position for ordinality
+ "UT3$_Item#Data" xmltype path '*'
+ ,"UT3$_Position#" for ordinality
{:xml_to_columns:} ) ucd
- where data_id = :act_guid
+ where x.data_id = :act_guid
) ucd
)
select
- a.item_data as act_item_data,
- a.data_id act_data_id,
- e.item_data as exp_item_data,
- e.data_id exp_data_id,
+ a."UT3$_Item#Data" as act_item_data,
+ a."UT3$_Data#Id" act_data_id,
+ e."UT3$_Item#Data" as exp_item_data,
+ e."UT3$_Data#Id" exp_data_id,
{:item_no:} as item_no,
- nvl(e.dup_no,a.dup_no) dup_no
+ nvl(e."UT3$_Dup#No",a."UT3$_Dup#No") dup_no
from act a {:join_type:} exp e on ( {:join_condition:} )
where {:where_condition:}]';
@@ -306,16 +306,16 @@ create or replace package body ut_compound_data_helper is
a_join_by_stmt := 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.dup_no = a.dup_no and '||ut_utils.table_to_clob(l_equal_list, ' and ');
+ a_join_by_stmt := ' e."UT3$_Dup#No" = a."UT3$_Dup#No" and '||ut_utils.table_to_clob(l_equal_list, ' and ');
else
-- Else join on rownumber
- a_join_by_stmt := 'a.item_no = e.item_no ';
+ a_join_by_stmt := 'a."UT3$_Item#No" = e."UT3$_Item#No" ';
end if;
a_not_equal_stmt := ut_utils.table_to_clob(l_not_equal_list, ' or ');
else
--Partition by piece when no data
ut_utils.append_to_clob(a_partition_stmt,' 1 ');
- a_join_by_stmt := 'a.item_no = e.item_no ';
+ a_join_by_stmt := 'a."UT3$_Item#No" = e."UT3$_Item#No" ';
end if;
end;
@@ -349,8 +349,8 @@ create or replace package body ut_compound_data_helper is
begin
return
case
- when a_unordered then 'row_number() over ( order by nvl(e.item_no,a.item_no))'
- else 'nvl(e.item_no,a.item_no) '
+ when a_unordered then 'row_number() over ( order by nvl(e."UT3$_Item#No",a."UT3$_Item#No"))'
+ else 'nvl(e."UT3$_Item#No",a."UT3$_Item#No") '
end;
end;
@@ -387,9 +387,9 @@ create or replace package body ut_compound_data_helper is
end if;
--If its inclusion we expect a actual set to fully match and have no extra elements over expected
if a_inclusion_type then
- ut_utils.append_to_clob(l_where_stmt,case when a_is_negated then ' 1 = 1 ' else ' ( a.data_id is null ) ' end);
+ ut_utils.append_to_clob(l_where_stmt,case when a_is_negated then ' 1 = 1 ' else ' ( a."UT3$_Data#Id" is null ) ' end);
else
- ut_utils.append_to_clob(l_where_stmt,' (a.data_id is null or e.data_id is null) ');
+ ut_utils.append_to_clob(l_where_stmt,' (a."UT3$_Data#Id" is null or e."UT3$_Data#Id" is null) ');
end if;
l_compare_sql := replace(l_compare_sql,'{:where_condition:}',l_where_stmt);
@@ -561,6 +561,8 @@ create or replace package body ut_compound_data_helper is
procedure cleanup_diff is
begin
g_diff_count := 0;
+ delete from ut_compound_data_diff_tmp;
+ delete from ut_json_data_diff_tmp;
end;
function get_rows_diff_count return integer is
diff --git a/test/ut3_tester/core/expectations/test_expectation_processor.pkb b/test/ut3_tester/core/expectations/test_expectation_processor.pkb
index ab413616e..c061e55d4 100644
--- a/test/ut3_tester/core/expectations/test_expectation_processor.pkb
+++ b/test/ut3_tester/core/expectations/test_expectation_processor.pkb
@@ -11,7 +11,7 @@ create or replace package body test_expectation_processor is
handle number name
34f88e4420 124 package body SCH_TEST.UT_EXPECTATION_PROCESSOR
353dfeb2f8 26 SCH_TEST.UT_EXPECTATION_RESULT
-cba249ce0 112 SCH_TEST.UT_EXPECTATION
+cba2493ce0 112 SCH_TEST.UT_EXPECTATION
3539881cf0 21 SCH_TEST.UT_EXPECTATION_NUMBER
351a608008 7 package body ]'||gc_user||q'[.TEST_EXPECTATION_PROCESSOR
351a608018 12 package body ]'||gc_user||q'[.TEST_EXPECTATION_PROCESSOR
@@ -24,7 +24,7 @@ cba249ce0 112 SCH_TEST.UT_EXPECTATION
34f8ab98f0 48 SCH_TEST.UT_SUITE_ITEM
34f8ab9b10 74 SCH_TEST.UT_SUITE
34f8ab98f0 48 SCH_TEST.UT_SUITE_ITEM
-cba24bfd0 75 SCH_TEST.UT_LOGICAL_SUITE
+cba24bfad0 75 SCH_TEST.UT_LOGICAL_SUITE
353dfecf30 59 SCH_TEST.UT_RUN
34f8ab98f0 48 SCH_TEST.UT_SUITE_ITEM
357f5421e8 77 package body SCH_TEST.UT_RUNNER
@@ -67,5 +67,39 @@ at "'||gc_user||'.TEST_EXPECTATION_PROCESSOR", line 24');
).to_be_like('at "UT3.UT_EXAMPLETEST", line 20 %');
end;
+ procedure who_call_expectation_win_stack is
+ l_stack_trace varchar2(4000);
+ l_source_line varchar2(4000);
+ begin
+ l_stack_trace := q'[----- PL/SQL Call Stack -----
+object line object
+handle number name
+00007FF8547B7D30 124 package body SCH_TEST.UT_EXPECTATION_PROCESSOR
+00007FF8547B7D30 26 SCH_TEST.UT_EXPECTATION_RESULT
+00007FF8547B7D30 112 SCH_TEST.UT_EXPECTATION
+00007FF8547B7D30 21 SCH_TEST.UT_EXPECTATION_NUMBER
+00007FF8547B7D30 7 package body SCOTT.TEST_BETWNSTR.BASIC_USAGE
+00007FF81FF207B0 345 type body SCOTT.TEST_BETWNSTR.BASIC_USAGE_TYP
+00007FF8544B21B8 6 anonymous block
+00007FF8267FBFC8 1721 package body SYS.DBMS_SQL.EXECUTE
+00007FF852BCFC68 142 type body UT3.UT_EXECUTABLE.DO_EXECUTE
+00007FF852BCFC68 44 type body UT3.UT_EXECUTABLE.DO_EXECUTE
+00007FF8512F9A90 74 type body UT3.UT_EXECUTABLE_TEST.DO_EXECUTE
+00007FF8512F9A90 38 type body UT3.UT_EXECUTABLE_TEST.DO_EXECUTE
+00007FF8231A2088 79 type body UT3.UT_TEST.DO_EXECUTE
+00007FF81FF207B0 49 type body UT3.UT_SUITE_ITEM.DO_EXECUTE
+00007FF852C83270 66 type body UT3.UT_SUITE.DO_EXECUTE
+00007FF82165F3B0 67 type body UT3.UT_RUN.DO_EXECUTE
+00007FF81FF207B0 49 type body UT3.UT_SUITE_ITEM.DO_EXECUTE
+00007FF8266285C0 172 package body UT3.UT_RUNNER.RUN
+00007FF854710538 134 package body UT3.UT.RUN_AUTONOMOUS
+00007FF854710538 488 package body UT3.UT.RUN
+00007FF854710538 623 package body UT3.UT.RUN
+00007FF81CFFA388 1 anonymous block]';
+ ut.expect(
+ ut3.ut_expectation_processor.who_called_expectation(l_stack_trace)
+ ).to_be_like('at "SCOTT.TEST_BETWNSTR.BASIC_USAGE", line 7 %');
+ end;
+
end;
/
diff --git a/test/ut3_tester/core/expectations/test_expectation_processor.pks b/test/ut3_tester/core/expectations/test_expectation_processor.pks
index 73cf60d11..6087eb045 100644
--- a/test/ut3_tester/core/expectations/test_expectation_processor.pks
+++ b/test/ut3_tester/core/expectations/test_expectation_processor.pks
@@ -14,6 +14,9 @@ create or replace package test_expectation_processor is
--%test(parses stack trace and returns objects and line that called expectation)
procedure who_called_expectation;
+ --%test(parses stack trace for Windows DB os - regression for #1000)
+ procedure who_call_expectation_win_stack;
+
--%endcontext
end;
diff --git a/test/ut3_tester/core/test_suite_builder.pkb b/test/ut3_tester/core/test_suite_builder.pkb
index b49ecb2da..a204a04e6 100644
--- a/test/ut3_tester/core/test_suite_builder.pkb
+++ b/test/ut3_tester/core/test_suite_builder.pkb
@@ -635,8 +635,8 @@ create or replace package body test_suite_builder is
ut3.ut_annotation(1, 'suite','Cool', null),
ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'),
ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'),
- ut3.ut_annotation(4, 'context','a_context', null),
- ut3.ut_annotation(5, 'displayname','A context', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(5, 'name','a_context', null),
ut3.ut_annotation(6, 'beforeall',null, 'context_setup'),
ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
ut3.ut_annotation(8, 'endcontext',null, null)
@@ -674,6 +674,100 @@ create or replace package body test_suite_builder is
);
end;
+ procedure nested_contexts is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation( 1, 'suite','Cool', null),
+ ut3.ut_annotation( 2, 'beforeall',null, 'suite_level_beforeall'),
+ ut3.ut_annotation( 3, 'test','In suite', 'suite_level_test'),
+ ut3.ut_annotation( 4, 'context','A context', null),
+ ut3.ut_annotation( 5, 'name','a_context', null),
+ ut3.ut_annotation( 6, 'beforeall',null, 'context_setup'),
+ ut3.ut_annotation( 7, 'test', 'First test in context', 'first_test_in_a_context'),
+ ut3.ut_annotation( 8, 'context','A nested context', null),
+ ut3.ut_annotation( 9, 'name','a_nested_context', null),
+ ut3.ut_annotation(10, 'beforeall',null, 'nested_context_setup'),
+ ut3.ut_annotation(11, 'test', 'Test in nested context', 'test_in_nested_context'),
+ ut3.ut_annotation(12, 'endcontext',null, null),
+ ut3.ut_annotation(13, 'context',null, null),
+ ut3.ut_annotation(14, 'name','nested_context_2', null),
+ ut3.ut_annotation(15, 'test', 'Test in nested context', 'test_in_nested_context_2'),
+ ut3.ut_annotation(16, 'context','a_nested_context_3', null),
+ ut3.ut_annotation(17, 'test', 'Test in nested context', 'test_in_nested_context_3'),
+ ut3.ut_annotation(18, 'endcontext',null, null),
+ ut3.ut_annotation(19, 'endcontext',null, null),
+ ut3.ut_annotation(20, 'test', 'Second test in context', 'second_test_in_a_context'),
+ ut3.ut_annotation(21, 'endcontext',null, null)
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '%' ||
+ '' ||
+ '%a_contextA contextsome_package.a_context' ||
+ '%' ||
+ '' ||
+ '%nested_context_2nested_context_2some_package.a_context.nested_context_2' ||
+ '%' ||
+ '' ||
+ '%nested_context_#1a_nested_context_3some_package.a_context.nested_context_2.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_nested_context_3Test in nested contextsome_package.a_context.nested_context_2.nested_context_#1.test_in_nested_context_3' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%' ||
+ '' ||
+ '%test_in_nested_context_2Test in nested contextsome_package.a_context.nested_context_2.test_in_nested_context_2' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%' ||
+ '' ||
+ '%a_nested_contextA nested contextsome_package.a_context.a_nested_context' ||
+ '%' ||
+ '' ||
+ '%test_in_nested_contextTest in nested contextsome_package.a_context.a_nested_context.test_in_nested_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagenested_context_setup' ||
+ '%' ||
+ '%' ||
+ '' ||
+ '%first_test_in_a_contextFirst test in contextsome_package.a_context.first_test_in_a_context' ||
+ '%' ||
+ '' ||
+ '%second_test_in_a_contextSecond test in contextsome_package.a_context.second_test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagecontext_setup' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagesuite_level_beforeall' ||
+ '%' ||
+ '' ||
+ ''||
+ '
'
+ );
+ end;
+
+
procedure before_after_in_context is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -682,7 +776,7 @@ create or replace package body test_suite_builder is
l_annotations := ut3.ut_annotations(
ut3.ut_annotation(1, 'suite', 'Cool', null),
ut3.ut_annotation(2, 'test', 'In suite', 'suite_level_test'),
- ut3.ut_annotation(3, 'context', 'a_context', null),
+ ut3.ut_annotation(3, 'context', 'A context', null),
ut3.ut_annotation(4, 'beforeall', 'context_beforeall', null),
ut3.ut_annotation(5, 'beforeeach', null, 'context_beforeeach'),
ut3.ut_annotation(6, 'test', 'In context', 'test_in_a_context'),
@@ -698,7 +792,7 @@ create or replace package body test_suite_builder is
'' ||
'%' ||
'%' ||
- '%a_context' ||
+ '%nested_context_#1A contextsome_package.nested_context_#1' ||
'%' ||
'%' ||
'%test_in_a_context' ||
@@ -734,7 +828,7 @@ create or replace package body test_suite_builder is
ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'),
ut3.ut_annotation(3, 'beforeeach',null, 'suite_level_beforeeach'),
ut3.ut_annotation(4, 'test','In suite', 'suite_level_test'),
- ut3.ut_annotation(5, 'context','a_context', null),
+ ut3.ut_annotation(5, 'context',null, null),
ut3.ut_annotation(6, 'test', 'In context', 'test_in_a_context'),
ut3.ut_annotation(7, 'endcontext',null, null),
ut3.ut_annotation(8, 'aftereach',null, 'suite_level_aftereach'),
@@ -748,7 +842,7 @@ create or replace package body test_suite_builder is
'' ||
'%' ||
'%' ||
- '%a_context' ||
+ '%nested_context_#1nested_context_#1some_package.nested_context_#1' ||
'%' ||
'%' ||
'%test_in_a_context' ||
@@ -783,33 +877,39 @@ create or replace package body test_suite_builder is
ut3.ut_annotation(1, 'suite','Cool', null),
ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'),
ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'),
- ut3.ut_annotation(4, 'context','A context', null),
- ut3.ut_annotation(5, 'beforeall',null, 'context_setup'),
+ ut3.ut_annotation(4, 'context','Some context', null),
+ ut3.ut_annotation(5, 'name','a_context', null),
+ ut3.ut_annotation(6, 'beforeall',null, 'context_setup'),
ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context')
);
--Act
l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
--Assert
ut.expect(l_actual).to_be_like(
- '%Invalid annotation "--\%context". Cannot find following "--\%endcontext". Annotation ignored.%at package "UT3_TESTER.SOME_PACKAGE", line 4%'
+ '%Missing "--\%endcontext" annotation for a "--\%context" annotation. The end of package is considered end of context.%at package "UT3_TESTER.SOME_PACKAGE", line 4%'
,'\'
);
ut.expect(l_actual).to_be_like(
''||
'' ||
- '%' ||
- '' ||
- '%suite_level_testIn suitesome_package.suite_level_test' ||
- '%' ||
- '' ||
- '%test_in_a_contextIn contextsome_package.test_in_a_context' ||
- '%' ||
- '' ||
- '' ||
- '%some_packagesuite_level_beforeall' ||
- '%some_packagecontext_setup' ||
- '%' ||
- '' ||
+ '%' ||
+ '%a_contextSome contextsome_package.a_context' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagecontext_setup' ||
+ '%' ||
+ '%' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagesuite_level_beforeall' ||
+ '%' ||
+ '' ||
''||
'
'
);
@@ -824,8 +924,8 @@ create or replace package body test_suite_builder is
ut3.ut_annotation(1, 'suite','Cool', null),
ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'),
ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'),
- ut3.ut_annotation(4, 'context','a_context', null),
- ut3.ut_annotation(5, 'displayname','A context', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(5, 'name','a_context', null),
ut3.ut_annotation(6, 'beforeall',null, 'context_setup'),
ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
ut3.ut_annotation(8, 'endcontext',null, null),
@@ -835,7 +935,7 @@ create or replace package body test_suite_builder is
l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
--Assert
ut.expect(l_actual).to_be_like(
- '%Invalid annotation "--\%endcontext". Cannot find preceding "--\%context". Annotation ignored.%at package "UT3_TESTER.SOME_PACKAGE", line 9%'
+ '%Extra "--\%endcontext" annotation found. Cannot find corresponding "--\%context". Annotation ignored.%at package "UT3_TESTER.SOME_PACKAGE", line 9%'
,'\'
);
ut.expect(l_actual).to_be_like(
@@ -850,7 +950,7 @@ create or replace package body test_suite_builder is
'%' ||
'' ||
'' ||
- '%some_packagecontext_setup' ||
+ '%some_packagecontext_setup' ||
'%' ||
'' ||
'' ||
@@ -859,7 +959,7 @@ create or replace package body test_suite_builder is
'%' ||
'' ||
'' ||
- '%some_packagesuite_level_beforeall' ||
+ '%some_packagesuite_level_beforeall' ||
'%' ||
'' ||
''||
@@ -867,7 +967,6 @@ create or replace package body test_suite_builder is
);
end;
- --%test(Gives warning when two contexts have the same name)
procedure duplicate_context_name is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -877,13 +976,13 @@ create or replace package body test_suite_builder is
ut3.ut_annotation(1, 'suite','Cool', null),
ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'),
ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'),
- ut3.ut_annotation(4, 'context','a_context', null),
- ut3.ut_annotation(5, 'displayname','A context', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(5, 'name','a_context', null),
ut3.ut_annotation(6, 'beforeall',null, 'context_setup'),
ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
ut3.ut_annotation(8, 'endcontext',null, null),
- ut3.ut_annotation(9, 'context','a_context', null),
- ut3.ut_annotation(10, 'displayname','A context', null),
+ ut3.ut_annotation(9, 'context','A context', null),
+ ut3.ut_annotation(10, 'name','a_context', null),
ut3.ut_annotation(11, 'beforeall',null, 'setup_in_duplicated_context'),
ut3.ut_annotation(12, 'test', 'In duplicated context', 'test_in_duplicated_context'),
ut3.ut_annotation(13, 'endcontext',null, null)
@@ -892,31 +991,43 @@ create or replace package body test_suite_builder is
l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
--Assert
ut.expect(l_actual).to_be_like(
- '%Context name must be unique in a suite. Context and all of it's content ignored.%at package "UT3_TESTER.SOME_PACKAGE", line 9%'
+ '%Context name "a_context" already used in this scope. Name must be unique. Using fallback name nested_context_#2.%%'
,'\'
);
ut.expect(l_actual).to_be_like(
''||
'' ||
'%' ||
- '' ||
- '%a_contextA contextsome_package.a_context' ||
- '%' ||
- '' ||
- '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' ||
- '%' ||
+ '' ||
+ '%nested_context_#2A contextsome_package.nested_context_#2' ||
+ '%' ||
+ '' ||
+ '%test_in_duplicated_contextIn duplicated contextsome_package.nested_context_#2.test_in_duplicated_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagesetup_in_duplicated_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '%a_contextA contextsome_package.a_context' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.a_context.test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '%some_packagecontext_setup' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
'' ||
'' ||
- '%some_packagecontext_setup' ||
- '%' ||
- '' ||
- '' ||
- '' ||
- '%suite_level_testIn suitesome_package.suite_level_test' ||
- '%' ||
- '' ||
- '' ||
- '%some_packagesuite_level_beforeall' ||
+ '%some_packagesuite_level_beforeall' ||
'%' ||
'' ||
''||
@@ -924,6 +1035,271 @@ create or replace package body test_suite_builder is
);
end;
+ procedure hard_stop_in_ctx_name is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ l_bad_name varchar2(100);
+ begin
+ --Arrange
+ l_bad_name := 'ctx_with_dot.in_it';
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(4, 'context',null, null),
+ ut3.ut_annotation(5, 'name',l_bad_name, null),
+ ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
+ ut3.ut_annotation(13, 'endcontext',null, null)
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "'||l_bad_name||'" for context name. Context name ignored and fallback to auto-name "nested_context_#1"%'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%nested_context_#1nested_context_#1some_package.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.nested_context_#1.test_in_a_context' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ ''||
+ '
'
+ );
+ end;
+
+ procedure name_with_spaces_invalid is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ l_bad_name varchar2(100);
+ begin
+ --Arrange
+ l_bad_name := 'context name with spaces';
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(4, 'context',null, null),
+ ut3.ut_annotation(5, 'name',l_bad_name, null),
+ ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
+ ut3.ut_annotation(13, 'endcontext',null, null)
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "'||l_bad_name||'" for context name. Context name ignored and fallback to auto-name "nested_context_#1"%'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%nested_context_#1nested_context_#1some_package.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.nested_context_#1.test_in_a_context' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ ''||
+ '
'
+ );
+ end;
+
+ procedure duplicate_name_annotation is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(5, 'name','a_context_name', null),
+ ut3.ut_annotation(6, 'name','a_newer_context_name', null),
+ ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
+ ut3.ut_annotation(8, 'endcontext',null, null),
+ ut3.ut_annotation(12, 'test', 'In suite', 'suite_level_test')
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%Duplicate annotation "--%name". Annotation ignored.%%'
+ ,'\'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%a_context_nameA contextsome_package.a_context_name' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.a_context_name.test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ ''||
+ '
'
+ );
+ end;
+
+ procedure name_outside_of_context is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(3, 'name','a_context_name', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
+ ut3.ut_annotation(8, 'endcontext',null, null),
+ ut3.ut_annotation(12, 'test', 'In suite', 'suite_level_test')
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%%'
+ ,'\'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%nested_context_#1A contextsome_package.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.nested_context_#1.test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ ''||
+ '
'
+ );
+ end;
+
+ procedure name_empty_value is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(5, 'name',null, null),
+ ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'),
+ ut3.ut_annotation(8, 'endcontext',null, null),
+ ut3.ut_annotation(12, 'test', 'In suite', 'suite_level_test')
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%%'
+ ,'\'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%nested_context_#1A contextsome_package.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_a_contextIn contextsome_package.nested_context_#1.test_in_a_context' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '%suite_level_testIn suitesome_package.suite_level_test' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ ''||
+ '
'
+ );
+ end;
+
+ procedure multiple_contexts is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(1, 'suite','Cool', null),
+ ut3.ut_annotation(4, 'context','A context', null),
+ ut3.ut_annotation(6, 'test', 'In context1', 'test_in_a_context1'),
+ ut3.ut_annotation(7, 'endcontext',null, null),
+ ut3.ut_annotation(8, 'context','A context', null),
+ ut3.ut_annotation(10, 'test', 'In context2', 'test_in_a_context2'),
+ ut3.ut_annotation(11, 'endcontext',null, null)
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%%'
+ ,'\'
+ );
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%' ||
+ '' ||
+ '%nested_context_#2A contextsome_package.nested_context_#2' ||
+ '%' ||
+ '' ||
+ '%test_in_a_context2In context2some_package.nested_context_#2.test_in_a_context2' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '%nested_context_#1A contextsome_package.nested_context_#1' ||
+ '%' ||
+ '' ||
+ '%test_in_a_context1In context1some_package.nested_context_#1.test_in_a_context1' ||
+ '%' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ '' ||
+ ''||
+ '
'
+ );
+ end;
+
procedure throws_value_empty is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1152,7 +1528,7 @@ create or replace package body test_suite_builder is
'%testtag%'||
'%%'
);
-
+
end;
procedure suite_tag_annotation is
@@ -1173,9 +1549,9 @@ create or replace package body test_suite_builder is
'%suitetag%'||
'%%'
);
-
+
end;
-
+
procedure test_tags_annotation is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1195,7 +1571,7 @@ create or replace package body test_suite_builder is
'%testtagtesttag2testtag3%'||
'%%'
);
-
+
end;
procedure suite_tags_annotation is
@@ -1216,7 +1592,7 @@ create or replace package body test_suite_builder is
'%suitetagsuitetag1suitetag2%'||
'%%'
);
-
+
end;
procedure test_2line_tags_annotation is
@@ -1239,7 +1615,7 @@ create or replace package body test_suite_builder is
'%testtagtesttag2%'||
'%%'
);
-
+
end;
procedure suite_2line_tags_annotation is
@@ -1261,7 +1637,7 @@ create or replace package body test_suite_builder is
'%suitetagsuitetag1%'||
'%%'
);
-
+
end;
procedure test_empty_tag is
@@ -1280,9 +1656,9 @@ create or replace package body test_suite_builder is
'%%"--%tags" annotation requires a tag value populated. Annotation ignored.%%'||
'%%'
);
-
+
end;
-
+
procedure suite_empty_tag is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1299,7 +1675,7 @@ create or replace package body test_suite_builder is
'%"--%tags" annotation requires a tag value populated. Annotation ignored.%%'||
'%%'
);
-
+
end;
procedure test_duplicate_tag is
@@ -1322,9 +1698,9 @@ create or replace package body test_suite_builder is
'%testtagtesttag1testtag2%'||
'%%'
);
-
+
end;
-
+
procedure suite_duplicate_tag is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1344,7 +1720,7 @@ create or replace package body test_suite_builder is
'%suitetagsuitetag1suitetag2%'||
'%%'
);
-
+
end;
procedure test_empty_tag_between is
@@ -1366,9 +1742,9 @@ create or replace package body test_suite_builder is
'%testtagtesttag1%'||
'%%'
);
-
+
end;
-
+
procedure suite_empty_tag_between is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1387,8 +1763,8 @@ create or replace package body test_suite_builder is
'%suitetagsuitetag1%'||
'%%'
);
-
- end;
+
+ end;
procedure test_special_char_tag is
l_actual clob;
@@ -1409,9 +1785,9 @@ create or replace package body test_suite_builder is
'%#?$%^&*!|\/@][%'||
'%%'
);
-
+
end;
-
+
procedure suite_special_char_tag is
l_actual clob;
l_annotations ut3.ut_annotations;
@@ -1431,7 +1807,67 @@ create or replace package body test_suite_builder is
'%%'
);
- end;
+ end;
+
+ procedure test_spaces_in_tag is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(2, 'suite','testsuite', null),
+ ut3.ut_annotation(3, 'tags',' good_tag , bad tag , good-tag ', null),
+ ut3.ut_annotation(8, 'test','Some test', 'test_procedure'),
+ ut3.ut_annotation(9, 'tags',' good_tag , bad tag , good-tag ', 'test_procedure')
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%good_taggood-tag%'||
+ 'good_taggood-tag%'
+ );
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "bad tag" for "--%tags" annotation.'||
+ ' See documentation for details on valid tag values. Annotation value ignored.' ||
+ '%at package "UT3_TESTER.SOME_PACKAGE", line 3%'
+ );
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "bad tag" for "--%tags" annotation.'||
+ ' See documentation for details on valid tag values. Annotation value ignored.' ||
+ '%at package "UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE", line 9%'
+ );
+ end;
+
+ procedure test_minus_in_tag is
+ l_actual clob;
+ l_annotations ut3.ut_annotations;
+ begin
+ --Arrange
+ l_annotations := ut3.ut_annotations(
+ ut3.ut_annotation(2, 'suite','testsuite', null),
+ ut3.ut_annotation(3, 'tags',' good_tag , -invalid_tag , good-tag ', null),
+ ut3.ut_annotation(8, 'test','Some test', 'test_procedure'),
+ ut3.ut_annotation(9, 'tags',' good_tag , -invalid_tag , good-tag ', 'test_procedure')
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ '%good_taggood-tag%'||
+ 'good_taggood-tag%'
+ );
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "-invalid_tag" for "--%tags" annotation.'||
+ ' See documentation for details on valid tag values. Annotation value ignored.' ||
+ '%at package "UT3_TESTER.SOME_PACKAGE", line 3%'
+ );
+ ut.expect(l_actual).to_be_like(
+ '%Invalid value "-invalid_tag" for "--%tags" annotation.'||
+ ' See documentation for details on valid tag values. Annotation value ignored.' ||
+ '%at package "UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE", line 9%'
+ );
+ end;
end test_suite_builder;
/
diff --git a/test/ut3_tester/core/test_suite_builder.pks b/test/ut3_tester/core/test_suite_builder.pks
index 721db153f..7a85cb946 100644
--- a/test/ut3_tester/core/test_suite_builder.pks
+++ b/test/ut3_tester/core/test_suite_builder.pks
@@ -2,221 +2,241 @@ create or replace package test_suite_builder is
--%suite(suite_builder)
--%suitepath(utplsql.ut3_tester.core)
- --%context(suite)
- --%displayname(--%suite annotation)
+ --%context(--%suite annotation)
- --%test(Sets suite name from package name and leaves description empty)
- procedure no_suite_description;
+ --%test(Sets suite name from package name and leaves description empty)
+ procedure no_suite_description;
- --%test(Sets suite description using first --%suite annotation)
- procedure suite_description_from_suite;
+ --%test(Sets suite description using first --%suite annotation)
+ procedure suite_description_from_suite;
- --%test(Gives warning if more than one --%suite annotation used)
- procedure suite_annot_duplicated;
+ --%test(Gives warning if more than one --%suite annotation used)
+ procedure suite_annot_duplicated;
--%endcontext
- --%context(displayname)
- --%displayname(--%displayname annotation)
+ --%context(--%displayname annotation)
- --%test(Overrides suite description using first --%displayname annotation)
- procedure suite_descr_from_displayname;
+ --%test(Overrides suite description using first --%displayname annotation)
+ procedure suite_descr_from_displayname;
- --%test(Gives warning if more than one --%displayname annotation used)
- procedure displayname_annot_duplicated;
+ --%test(Gives warning if more than one --%displayname annotation used)
+ procedure displayname_annot_duplicated;
- --%test(Gives warning if --%displayname annotation has no value)
- procedure displayname_annot_empty;
+ --%test(Gives warning if --%displayname annotation has no value)
+ procedure displayname_annot_empty;
--%endcontext
- --%context(test)
- --%displayname(--%test annotation)
+ --%context(--%test annotation)
- --%test(Creates a test item for procedure annotated with --%test annotation)
- procedure test_annotation;
+ --%test(Creates a test item for procedure annotated with --%test annotation)
+ procedure test_annotation;
- --%test(Gives warning if more than one --%test annotation used)
- procedure test_annot_duplicated;
+ --%test(Gives warning if more than one --%test annotation used)
+ procedure test_annot_duplicated;
- --%test(Is added to suite according to annotation order in package spec)
- procedure test_annotation_ordering;
+ --%test(Is added to suite according to annotation order in package spec)
+ procedure test_annotation_ordering;
--%endcontext
- --%context(suitepath)
- --%displayname(--%suitepath annotation)
+ --%context(--%suitepath annotation)
- --%test(Sets suite path using first --%suitepath annotation)
- procedure suitepath_from_non_empty_path;
+ --%test(Sets suite path using first --%suitepath annotation)
+ procedure suitepath_from_non_empty_path;
- --%test(Gives warning if more than one --%suitepath annotation used)
- procedure suitepath_annot_duplicated;
+ --%test(Gives warning if more than one --%suitepath annotation used)
+ procedure suitepath_annot_duplicated;
- --%test(Gives warning if --%suitepath annotation has no value)
- procedure suitepath_annot_empty;
+ --%test(Gives warning if --%suitepath annotation has no value)
+ procedure suitepath_annot_empty;
- --%test(Gives warning if --%suitepath annotation has invalid value)
- procedure suitepath_annot_invalid_path;
+ --%test(Gives warning if --%suitepath annotation has invalid value)
+ procedure suitepath_annot_invalid_path;
--%endcontext
- --%context(rollback)
- --%displayname(--%rollback annotation)
+ --%context--%rollback annotation)
- --%test(Sets rollback type using first --%rollback annotation)
- procedure rollback_type_valid;
+ --%test(Sets rollback type using first --%rollback annotation)
+ procedure rollback_type_valid;
- --%test(Gives warning if more than one --%rollback annotation used)
- procedure rollback_type_duplicated;
+ --%test(Gives warning if more than one --%rollback annotation used)
+ procedure rollback_type_duplicated;
- --%test(Gives warning if --%rollback annotation has no value)
- procedure rollback_type_empty;
+ --%test(Gives warning if --%rollback annotation has no value)
+ procedure rollback_type_empty;
- --%test(Gives warning if --%rollback annotation has invalid value)
- procedure rollback_type_invalid;
+ --%test(Gives warning if --%rollback annotation has invalid value)
+ procedure rollback_type_invalid;
--%endcontext
- --%context(before_after_all_each)
- --%displayname(--%before/after all/each annotations)
+ --%context(--%before/after all/each annotations)
- --%test(Supports multiple before/after all/each procedure level definitions)
- procedure multiple_before_after;
+ --%test(Supports multiple before/after all/each procedure level definitions)
+ procedure multiple_before_after;
- --%test(Supports multiple before/after all/each standalone level definitions)
- procedure multiple_standalone_bef_aft;
+ --%test(Supports multiple before/after all/each standalone level definitions)
+ procedure multiple_standalone_bef_aft;
- --%test(Supports mixing before/after all/each annotations on single procedure)
- procedure before_after_on_single_proc;
+ --%test(Supports mixing before/after all/each annotations on single procedure)
+ procedure before_after_on_single_proc;
- --%test(Supports mixed before/after all/each as standalone and procedure level definitions)
- procedure multiple_mixed_bef_aft;
+ --%test(Supports mixed before/after all/each as standalone and procedure level definitions)
+ procedure multiple_mixed_bef_aft;
- --%test(Gives warning if more than one --%beforeall annotation used on procedure)
- procedure beforeall_annot_duplicated;
+ --%test(Gives warning if more than one --%beforeall annotation used on procedure)
+ procedure beforeall_annot_duplicated;
- --%test(Gives warning if more than one --%beforeeach annotation used on procedure)
- procedure beforeeach_annot_duplicated;
+ --%test(Gives warning if more than one --%beforeeach annotation used on procedure)
+ procedure beforeeach_annot_duplicated;
- --%test(Gives warning if more than one --%afterall annotation used on procedure)
- procedure afterall_annot_duplicated;
+ --%test(Gives warning if more than one --%afterall annotation used on procedure)
+ procedure afterall_annot_duplicated;
- --%test(Gives warning if more than one --%aftereach annotation used on procedure)
- procedure aftereach_annot_duplicated;
+ --%test(Gives warning if more than one --%aftereach annotation used on procedure)
+ procedure aftereach_annot_duplicated;
- --%test(Gives warning on before/after all/each annotations mixed with test)
- procedure before_after_mixed_with_test;
+ --%test(Gives warning on before/after all/each annotations mixed with test)
+ procedure before_after_mixed_with_test;
--%endcontext
- --%context(context)
- --%displayname(--%context annotation)
+ --%context(--%context annotation)
- --%test(Creates nested suite for content between context/endcontext annotations)
- procedure suite_from_context;
+ --%test(Creates nested suite for content between context/endcontext annotations)
+ procedure suite_from_context;
- --%test(Associates before/after all/each to tests in context only)
- procedure before_after_in_context;
+ --%test(Creates nested contexts inside a context)
+ procedure nested_contexts;
- --%test(Propagates beforeeach/aftereach to context)
- procedure before_after_out_of_context;
+ --%test(Associates before/after all/each to tests in context only)
+ procedure before_after_in_context;
- --%test(Does not create context and gives warning when endcontext is missing)
- procedure context_without_endcontext;
+ --%test(Propagates beforeeach/aftereach to context)
+ procedure before_after_out_of_context;
- --%test(Gives warning if --%endcontext is missing a preceding --%context)
- procedure endcontext_without_context;
+ --%test(Gives warning when endcontext is missing)
+ procedure context_without_endcontext;
- --%test(Gives warning when two contexts have the same name and ignores duplicated context)
- procedure duplicate_context_name;
+ --%test(Gives warning if --%endcontext is missing a preceding --%context)
+ procedure endcontext_without_context;
+
+ --%test(Gives warning when two contexts have the same name and falls back to default context name)
+ procedure duplicate_context_name;
+
+ --%endcontext
+
+ --%context(--%name annotation)
+
+ --%test(Falls back to default context name and gives warning when context name contains "." character)
+ procedure hard_stop_in_ctx_name;
+
+ --%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 )
+ procedure duplicate_name_annotation;
+
+ --%test(Is ignored when used outside of context - no warning given)
+ procedure name_outside_of_context;
+
+ --%test(Is ignored when name value is empty)
+ procedure name_empty_value;
+
+ --%test(Is ignored when name value is empty)
+ procedure multiple_contexts;
--%endcontext
- --%context(throws)
- --%displayname(--%throws annotation)
+ --%context(--%throws annotation)
- --%test(Gives warning if --%throws annotation has no value)
- procedure throws_value_empty;
+ --%test(Gives warning if --%throws annotation has no value)
+ procedure throws_value_empty;
- --%test(Gives warning if --%throws annotation has invalid value)
- procedure throws_value_invalid;
+ --%test(Gives warning if --%throws annotation has invalid value)
+ procedure throws_value_invalid;
--%endcontext
- --%context(beforetest_aftertest)
- --%displayname(--%beforetest/aftertest annotation)
+ --%context(--%beforetest/aftertest annotation)
- --%test(Supports multiple occurrences of beforetest/aftertest for a test)
- procedure before_aftertest_multi;
+ --%test(Supports multiple occurrences of beforetest/aftertest for a test)
+ procedure before_aftertest_multi;
- --%test(Supports same procedure defined twice)
- procedure before_aftertest_twice;
+ --%test(Supports same procedure defined twice)
+ procedure before_aftertest_twice;
- --%test(Supports beforetest from external package)
- procedure before_aftertest_pkg_proc;
+ --%test(Supports beforetest from external package)
+ procedure before_aftertest_pkg_proc;
- --%test(Supports mix of procedure and package.procedure)
- procedure before_aftertest_mixed_syntax;
+ --%test(Supports mix of procedure and package.procedure)
+ procedure before_aftertest_mixed_syntax;
--%endcontext
- --%context(unknown_annotation)
- --%displayname(--%bad_annotation)
+ --%context(--%bad_annotation)
- --%test(Gives warning when unknown procedure level annotation passed)
- procedure test_bad_procedure_annotation;
+ --%test(Gives warning when unknown procedure level annotation passed)
+ procedure test_bad_procedure_annotation;
- --%test(Gives warning when unknown package level annotation passed)
- procedure test_bad_package_annotation;
+ --%test(Gives warning when unknown package level annotation passed)
+ procedure test_bad_package_annotation;
--%endcontext
- --%context(tags_annotation)
- --%displayname(--%tag_annotation)
+ --%context(--%tag_annotation)
- --%test(Build suite test with tag)
- procedure test_tag_annotation;
+ --%test(Build suite test with tag)
+ procedure test_tag_annotation;
- --%test(Build suite with tag)
- procedure suite_tag_annotation;
+ --%test(Build suite with tag)
+ procedure suite_tag_annotation;
- --%test(Build suite test with three tags)
- procedure test_tags_annotation;
-
- --%test(Build suite with three tags)
- procedure suite_tags_annotation;
+ --%test(Build suite test with three tags)
+ procedure test_tags_annotation;
- --%test(Build suite test with two line tag annotation)
- procedure test_2line_tags_annotation;
-
- --%test(Build suite with two line tag annotation)
- procedure suite_2line_tags_annotation;
+ --%test(Build suite with three tags)
+ procedure suite_tags_annotation;
- --%test(Build suite test with empty line tag annotation)
- procedure test_empty_tag;
-
- --%test(Build suite with empty line tag annotation)
- procedure suite_empty_tag;
-
- --%test(Build suite test with duplicate tag annotation)
- procedure test_duplicate_tag;
-
- --%test(Build suite with duplicate tag annotation)
- procedure suite_duplicate_tag;
+ --%test(Build suite test with two line tag annotation)
+ procedure test_2line_tags_annotation;
- --%test(Build suite test with empty between tag annotation)
- procedure test_empty_tag_between;
-
- --%test(Build suite with empty between tag annotation)
- procedure suite_empty_tag_between;
+ --%test(Build suite with two line tag annotation)
+ procedure suite_2line_tags_annotation;
+
+ --%test(Build suite test with empty line tag annotation)
+ procedure test_empty_tag;
+
+ --%test(Build suite with empty line tag annotation)
+ procedure suite_empty_tag;
+
+ --%test(Build suite test with duplicate tag annotation)
+ procedure test_duplicate_tag;
+
+ --%test(Build suite with duplicate tag annotation)
+ procedure suite_duplicate_tag;
+
+ --%test(Build suite test with empty between tag annotation)
+ procedure test_empty_tag_between;
+
+ --%test(Build suite with empty between tag annotation)
+ procedure suite_empty_tag_between;
+
+ --%test(Build suite test with special char tag annotation)
+ procedure test_special_char_tag;
+
+ --%test(Build suite with special char tag annotation)
+ procedure suite_special_char_tag;
+
+ --%test(Raise warning and ignore tag with spaces in tag name)
+ procedure test_spaces_in_tag;
+
+ --%test(Raise warning and ignore tag starting ith '-')
+ procedure test_minus_in_tag;
- --%test(Build suite test with special char tag annotation)
- procedure test_special_char_tag;
-
- --%test(Build suite with special char tag annotation)
- procedure suite_special_char_tag;
-
--%endcontext
end test_suite_builder;
diff --git a/test/ut3_tester/core/test_suite_manager.pkb b/test/ut3_tester/core/test_suite_manager.pkb
index 6f4237f9c..e693771b4 100644
--- a/test/ut3_tester/core/test_suite_manager.pkb
+++ b/test/ut3_tester/core/test_suite_manager.pkb
@@ -259,8 +259,8 @@ end test_package_3;]';
gv_glob_val number;
- --%context(some_context)
- --%displayname(Some context description)
+ --%context(Some context description)
+ --%name(some_context)
--%test
--%displayname(Test1 from test package 1)
diff --git a/test/ut3_tester_helper/coverage_helper.pkb b/test/ut3_tester_helper/coverage_helper.pkb
index 74a33da54..1db078a8b 100644
--- a/test/ut3_tester_helper/coverage_helper.pkb
+++ b/test/ut3_tester_helper/coverage_helper.pkb
@@ -20,12 +20,6 @@ create or replace package body coverage_helper is
return v_result;
end;
- procedure setup_mock_coverage_id is
- begin
- g_profiler_run_id := get_mock_proftab_run_id();
- ut3.ut_coverage.mock_coverage_id(g_profiler_run_id, ut3.ut_coverage.gc_proftab_coverage);
- end;
-
procedure setup_mock_coverage_ids(a_profiler_run_id integer, a_block_run_id integer) is
l_coverage_ids ut3.ut_coverage.tt_coverage_id_arr;
begin
@@ -34,6 +28,13 @@ create or replace package body coverage_helper is
ut3.ut_coverage.mock_coverage_id(l_coverage_ids);
end;
+ procedure setup_mock_coverage_id is
+ begin
+ g_profiler_run_id := get_mock_proftab_run_id();
+ g_block_run_id := get_mock_block_run_id();
+ setup_mock_coverage_ids(g_profiler_run_id, g_block_run_id);
+ end;
+
procedure setup_dummy_coverage is
pragma autonomous_transaction;
begin
diff --git a/test/ut3_user/api/test_ut_run.pkb b/test/ut3_user/api/test_ut_run.pkb
index 329b71357..feac3fe6e 100644
--- a/test/ut3_user/api/test_ut_run.pkb
+++ b/test/ut3_user/api/test_ut_run.pkb
@@ -688,7 +688,7 @@ Failures:%
select * bulk collect into l_results from table(ut3.ut.run('bad_annotations'));
l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results);
- ut.expect(l_actual).to_be_like('%Invalid annotation "--%context". Cannot find following "--%endcontext". Annotation ignored.%
+ ut.expect(l_actual).to_be_like('%Missing "--%endcontext" annotation for a "--%context" annotation. The end of package is considered end of context.%
%1 tests, 0 failed, 0 errored, 0 disabled, 1 warning(s)%');
end;
@@ -1021,6 +1021,48 @@ Failures:%
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_func_path_list is
+ l_results ut3.ut_varchar2_list;
+ begin
+ l_results := ut3_tester_helper.run_helper.run(ut3.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_exclude_run_func_path_list is
+ l_results ut3.ut_varchar2_list;
+ begin
+ l_results := ut3_tester_helper.run_helper.run(ut3.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_include_exclude_run_func is
+ l_results ut3.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 set_application_info is
begin
dbms_application_info.set_module( gc_module, gc_action );
@@ -1038,9 +1080,8 @@ Failures:%
--%beforeall
procedure before_suite;
- --%context(some_context)
-
- --%displayname(context description)
+ --%context(context description)
+ --%name(some_context)
--%beforeall
procedure before_context;
diff --git a/test/ut3_user/api/test_ut_run.pks b/test/ut3_user/api/test_ut_run.pks
index 38a6eee2c..ac2e50e25 100644
--- a/test/ut3_user/api/test_ut_run.pks
+++ b/test/ut3_user/api/test_ut_run.pks
@@ -175,59 +175,68 @@ create or replace package test_ut_run is
--%endcontext
--%context(run with tags)
-
--%displayname(Call ut.run with #tags)
- --%beforeall(create_ut3$user#_tests)
- --%afterall(drop_ut3$user#_tests)
-
- --%test(Execute test by tag ut_run)
- procedure test_run_by_one_tag;
-
- --%test( Execute suite by one tag)
- procedure suite_run_by_one_tag;
-
- --%test(Execute two tests by one tag)
- procedure two_test_run_by_one_tag;
-
- --%test(Execute all suites tests with tag)
- procedure all_suites_run_by_one_tag;
-
- --%test(Execute tests by passing two tags)
- procedure two_test_run_by_two_tags;
-
- --%test(Execute suite and all of its children)
- procedure suite_with_children_tag;
-
- --%test(Execute suite and parents)
- procedure suite_with_tag_parent;
-
- --%test(Execute test for non existing tag)
- procedure test_nonexists_tag;
-
- --%test(Execute test for duplicate list tags)
- procedure test_duplicate_tag;
-
- --%test(Execute suite test for duplicate list tags)
- procedure suite_duplicate_tag;
- --%test(Runs given package only with package name given as path and filter by tag)
- procedure run_proc_pkg_name_tag;
-
- --%test(Runs all from given package with package name given as path and coverage file list with tag)
- procedure run_pkg_name_file_list_tag;
-
- --%test(Runs tests from given paths with paths list and tag)
- procedure run_proc_path_list_tag;
-
- --%test(Runs all tests in current schema with default reporter when only tag is given)
- procedure tag_run_func_no_params;
+ --%beforeall(create_ut3$user#_tests)
+ --%afterall(drop_ut3$user#_tests)
+
+ --%test(Execute test by tag ut_run)
+ procedure test_run_by_one_tag;
+
+ --%test( Execute suite by one tag)
+ procedure suite_run_by_one_tag;
+
+ --%test(Execute two tests by one tag)
+ procedure two_test_run_by_one_tag;
+
+ --%test(Execute all suites tests with tag)
+ procedure all_suites_run_by_one_tag;
+
+ --%test(Execute tests by passing two tags)
+ procedure two_test_run_by_two_tags;
+
+ --%test(Execute suite and all of its children)
+ procedure suite_with_children_tag;
+
+ --%test(Execute suite and parents)
+ procedure suite_with_tag_parent;
+
+ --%test(Execute test for non existing tag)
+ procedure test_nonexists_tag;
+
+ --%test(Execute test for duplicate list tags)
+ procedure test_duplicate_tag;
+
+ --%test(Execute suite test for duplicate list tags)
+ procedure suite_duplicate_tag;
+
+ --%test(Runs given package only with package name given as path and filter by tag)
+ procedure run_proc_pkg_name_tag;
+
+ --%test(Runs all from given package with package name given as path and coverage file list with tag)
+ procedure run_pkg_name_file_list_tag;
+
+ --%test(Runs tests from given paths with paths list and tag)
+ procedure run_proc_path_list_tag;
+
+ --%test(Runs all tests in current schema with default reporter when only tag is given)
+ procedure tag_run_func_no_params;
+
+ --%test(Runs given package only with package name given as path and filter by tag)
+ procedure tag_run_func_pkg_name;
+
+ --%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 include/exclude tags)
+ procedure tag_inc_exc_run_func_path_list;
+
+ --%test(Runs tests from given path and excludes specific tags)
+ procedure tag_exclude_run_func_path_list;
- --%test(Runs given package only with package name given as path and filter by tag)
- procedure tag_run_func_pkg_name;
+ --%test(Runs tests from given tags and exclude tags)
+ procedure tag_include_exclude_run_func;
- --%test(Runs tests from given paths with paths list and a tag)
- procedure tag_run_func_path_list;
-
--%endcontext
--%context(ut3_info context)
diff --git a/test/ut3_user/expectations/test_expectations_cursor.pkb b/test/ut3_user/expectations/test_expectations_cursor.pkb
index dc439e7e0..1c6f46532 100644
--- a/test/ut3_user/expectations/test_expectations_cursor.pkb
+++ b/test/ut3_user/expectations/test_expectations_cursor.pkb
@@ -2825,5 +2825,50 @@ Check the query and data for errors.';
$end
end;
+ procedure compare_specific_column_names is
+ function get_cursor return sys_refcursor is
+ l_result sys_refcursor;
+ begin
+ open l_result for
+ select 'a' as item_data, rownum as data_id, rownum as item_no, rownum as dup_no, rownum as position from dual;
+ return l_result;
+ end;
+ begin
+ ut3.ut.expect(get_cursor()).to_equal(get_cursor());
+ ut3.ut.expect(get_cursor()).to_equal(get_cursor()).unordered();
+ ut3.ut.expect(get_cursor()).to_equal(get_cursor()).join_by('ITEM_DATA,DATA_ID,ITEM_NO,DUP_NO');
+ --Assert
+ ut.expect(ut3_tester_helper.main_helper.get_failed_expectations_num).to_equal(0);
+ end;
+
+ procedure multiple_cursor_expectations is
+ l_actual sys_refcursor;
+ l_expected sys_refcursor;
+ begin
+ open l_actual for select rownum rn from dual connect by level < 5;
+ open l_expected for select rownum rn from dual connect by level = 1;
+ ut3.ut.expect(l_actual).to_equal(l_expected);
+ open l_actual for select rownum rn from dual connect by level < 3;
+ open l_expected for select * from (select rownum rn from dual connect by level < 3) order by 1 desc;
+ ut3.ut.expect(l_actual).to_equal(l_expected);
+ ut.expect(ut3_tester_helper.main_helper.get_failed_expectations(1)).to_equal(
+'Actual: refcursor [ count = 4 ] was expected to equal: refcursor [ count = 1 ]
+Diff:
+Rows: [ 3 differences ]
+ Row No. 2 - Extra: 2
+ Row No. 3 - Extra: 3
+ Row No. 4 - Extra: 4'
+ );
+ ut.expect(ut3_tester_helper.main_helper.get_failed_expectations(2)).to_equal(
+'Actual: refcursor [ count = 2 ] was expected to equal: refcursor [ count = 2 ]
+Diff:
+Rows: [ 2 differences ]
+ Row No. 1 - Actual: 1
+ Row No. 1 - Expected: 2
+ Row No. 2 - Actual: 2
+ Row No. 2 - Expected: 1'
+ );
+ end;
+
end;
/
diff --git a/test/ut3_user/expectations/test_expectations_cursor.pks b/test/ut3_user/expectations/test_expectations_cursor.pks
index 82fc47f3c..d3dbeb9a7 100644
--- a/test/ut3_user/expectations/test_expectations_cursor.pks
+++ b/test/ut3_user/expectations/test_expectations_cursor.pks
@@ -462,8 +462,14 @@ create or replace package test_expectations_cursor is
--%test( Mixed column order exclusion )
procedure uc_columns_exclude;
- --%test(Compares cursors with long column names - Issue #952 )
+ --%test( Compares cursors with long column names - Issue #952 )
procedure compare_long_column_names;
+ --%test( Compares cursors with specific column names - Issue #997 )
+ procedure compare_specific_column_names;
+
+ --%test( Multiple failures reported correctly - Issue #998 )
+ procedure multiple_cursor_expectations;
+
end;
/
diff --git a/test/ut3_user/reporters.pkb b/test/ut3_user/reporters.pkb
index 6d6d573aa..9f571a589 100644
--- a/test/ut3_user/reporters.pkb
+++ b/test/ut3_user/reporters.pkb
@@ -14,8 +14,8 @@ as
--%beforeeach
procedure beforeeach;
- --%context(some_context)
- --%displayname(A description of some context)
+ --%context(A description of some context)
+ --%name(some_context)
--%test
--%beforetest(beforetest)
diff --git a/test/ut3_user/reporters/test_realtime_reporter.pkb b/test/ut3_user/reporters/test_realtime_reporter.pkb
index 8eb490a11..155925f0f 100644
--- a/test/ut3_user/reporters/test_realtime_reporter.pkb
+++ b/test/ut3_user/reporters/test_realtime_reporter.pkb
@@ -9,7 +9,8 @@ create or replace package body test_realtime_reporter as
--%suite(suite )
--%suitepath(realtime_reporting)
- --%context(test context)
+ --%context
+ --%name(test_context)
--%test(test 1 - OK)
procedure test_1_ok;
@@ -164,12 +165,12 @@ create or replace package body test_realtime_reporter as
select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting2.test_5' 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
- select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test context.test_1_ok' as item_id from dual union all
- select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test context.test_1_ok' as item_id from dual union all
- select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test context.test_2_nok' as item_id from dual union all
- select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test context.test_2_nok' as item_id from dual union all
- select 'post-suite' as event_type, 'realtime_reporting.check_realtime_reporting1.test context' 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
+ select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context.test_1_ok' as item_id from dual union all
+ select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context.test_1_ok' as item_id from dual union all
+ select 'pre-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context.test_2_nok' as item_id from dual union all
+ select 'post-test' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context.test_2_nok' as item_id from dual union all
+ select 'post-suite' as event_type, 'realtime_reporting.check_realtime_reporting1.test_context' as item_id from dual union all
select 'post-suite' as event_type, 'realtime_reporting.check_realtime_reporting1' as item_id from dual union all
select 'post-suite' as event_type, 'realtime_reporting' as item_id from dual union all
select 'post-run' as event_type, null as item_id from dual;
@@ -310,7 +311,7 @@ create or replace package body test_realtime_reporter as
into l_actual
from table(g_events) t
where t.event_doc.extract('/event[@type="post-test"]/test/@id').getstringval()
- = 'realtime_reporting.check_realtime_reporting1.test context.test_2_nok';
+ = 'realtime_reporting.check_realtime_reporting1.test_context.test_2_nok';
ut.expect(l_actual).to_equal(l_expected);
end single_failed_message;