diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index bef8aa7de..d1c495eb2 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1,130 +1,1080 @@ # 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 configuration files or tables are needed. 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. -There are two locations where annotations can be placed: -- Package level annotations can be placed at the very top of the package specification (`--%suite`, `--%suitepath` etc.) -- Procedure level annotations can be placed right before a procedure (`--%test`, `--%beforeall`, `--%beforeeach` etc.) +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.). -If procedure level annotation is not placed right before procedure, it is not considered an annotation for procedure. +We strongly recommend putting package level annotations at the very top of package except for the `--%context` annotations (described below) -Example of invalid procedure level annotations + +## Supported annotations + +| Annotation |Level| Description | +| --- | --- | --- | +| `--%suite()` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | +| `--%suitepath()` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | +| `--%displayname()` | Package/procedure | Human-readable and meaningful description of a suite/test. `%displayname(Name of the suite/test)`. The annotation is provided for flexibility and convenience only. It has exactly the same meaning as `` in `test` and `suite` annotations. If description is provided using both `suite`/`test` and `displayname`, then the one defined as last takes precedence. | +| `--%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 procedure must throw one of the exception numbers provided. If no valid numbers were provided as annotation parameters the annotation is ignored. Applicable to test procedures only. | +| `--%beforeall` | Procedure | Denotes that the annotated procedure 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. | +| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | +| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | +| `--%beforetest()` | Procedure | Denotes that mentioned procedure should be executed before the annotated `%test` procedure. | +| `--%aftertest()` | Procedure | Denotes that mentioned procedure 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 nested context (sub-suite) in a suite package | +| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | + +### Suite + +The `--%suite` annotation denotes PLSQL package as a unit test suite. +It accepts an optional description that will be visible when running the tests. +When description is not provided, package name is displayed on report. + +**Note** +>Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. +> +>Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. +Values for parameters should be provided without any quotation marks. +If the parameters are placed without brackets or with incomplete brackets, they will be ignored. +> +>Example: `--%suite(The name of suite without closing bracket` +>Example: `--%suite The name of suite without brackets` + + +Suite package without description. +```sql +create or replace package test_package as + --%suite +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +test_package + +Finished in .002415 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +Suite package with description. +```sql +create or replace package test_package as + --%suite(Tests for a package) +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + +Finished in .001646 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +When multiple `--%suite` annotations are specified in package, the first annotation will be used and a warning message will appear indicating duplicate annotation. +```sql +create or replace package test_package as + --%suite(Tests for a package) + --%suite(Bad annotation) +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + + +Warnings: + + 1) test_package + Duplicate annotation "--%suite". Annotation ignored. + at "TESTS_OWNER.TEST_PACKAGE", line 3 + +Finished in .003318 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 1 warning(s) +``` + +When `--%suite` annotation is bound to procedure, it is ignored and results in package not getting recognized as test suite. +```sql +create or replace package test_package as + --%suite(Tests for a package) + procedure some_proc; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +ORA-20204: Suite package TESTS_OWNER.test_package not found +ORA-06512: at "UT3.UT_RUNNER", line 106 +ORA-06512: at "UT3.UT", line 115 +ORA-06512: at "UT3.UT", line 306 +ORA-06512: at "UT3.UT", line 364 +ORA-06512: at line 1 +``` + + +### Test + +The `--%test` annotation denotes procedure withing test suite as a unit test. +It accepts an optional description that will be reported when the test is executed. +When description is not provided, procedure name is displayed on report. + + +If `--%test` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +Test procedure without description. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is begin null; end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + some_test [.003 sec] + +Finished in .004109 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +Test procedure with description. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.005 sec] + +Finished in .006828 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +When multiple `--%test` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%test(Duplicate description) + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.007 sec] + + +Warnings: + + 1) test_package + Duplicate annotation "--%test". Annotation ignored. + at "TESTS_OWNER.TEST_PACKAGE.SOME_TEST", line 5 + +Finished in .008815 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 1 warning(s) +``` + +### Disabled +Marks annotated suite package or test procedure as disabled. + +Disabling suite. +```sql +create or replace package test_package as + --%suite(Tests for a package) + --%disabled + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; +end; +/ +create or replace package body test_package as + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [0 sec] (DISABLED) + Description of another behavior [0 sec] (DISABLED) + +Finished in .001441 seconds +2 tests, 0 failed, 0 errored, 2 disabled, 0 warning(s) +``` + +Disabling individual test(s). +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + --%disabled + procedure other_test; +end; +/ +create or replace package body test_package as + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.004 sec] + Description of another behavior [0 sec] (DISABLED) + +Finished in .005868 seconds +2 tests, 0 failed, 0 errored, 1 disabled, 0 warning(s) +``` + +### Beforeall + +Marks annotated procedure to be executed before all test procedures in a suite. + +If `--%beforeall` raises an exception, suite content cannot be safely executed as the setup was not executed successfully for the suite. + +If `--%beforeall` raises an exception the following will happen: +- the `--%beforeall` procedures that follow the failed one, **will not be executed** +- all `--%test` procedures and their `--%beforeeach`, `--%aftereach`, `--%beforetest` and `--%aftertest` procedures within suite package **will not be executed** +- all `--%test` procedures **will be marked as failed** +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for other suite packages + +When multiple `--%beforeall` procedures are defined in a suite package, all of them will be executed before invoking any test. + +For multiple `--%beforeall` procedures order of execution is defined by annotation position in the package specification. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeall + procedure setup_stuff; + +end; +/ +create or replace package body test_package as + procedure setup_stuff is + begin + dbms_output.put_line('--- SETUP_STUFF invoked ---'); + end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + --- SETUP_STUFF invoked --- + Description of tesed behavior [.004 sec] + Description of another behavior [.003 sec] + +Finished in .012292 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +In the below example, procedure `another_setup` is invoked after `initial_setup`. +The `another_setup` still gets invoked before any test from that suite package is executed. + ```sql + create or replace package test_package as + --%suite(Tests for a package) + + --%beforeall + procedure initial_setup; + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeall + procedure another_setup; + + end; + / + create or replace package body test_package as + procedure another_setup is + begin + dbms_output.put_line('--- ANOTHER_SETUP invoked ---'); + end; + + procedure initial_setup is + begin + dbms_output.put_line('--- INITIAL_SETUP invoked ---'); + end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; + end; + / +``` + + ```sql + exec ut.run('test_package'); + ``` + ``` +Tests for a package + --- INITIAL_SETUP invoked --- + --- ANOTHER_SETUP invoked --- + Description of tesed behavior [.004 sec] + Description of another behavior [.004 sec] + +Finished in .016672 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) + ``` + +When multiple `--%beforeall` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. +When procedure is annotated as both `--%beforeall` and `--%test`, the procedure will become a test and a warning message will appear indicating invalid annotation combination. +```sql + create or replace package test_package as + --%suite(Tests for a package) + + --%beforeall + --%beforeall + procedure initial_setup; + + --%test(Description of tesed behavior) + --%beforeall + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + end; + / + create or replace package body test_package as + + procedure initial_setup is + begin + dbms_output.put_line('--- INITIAL_SETUP invoked ---'); + end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; + end; + / +``` + + ```sql + exec ut.run('test_package'); + ``` + ``` +Tests for a package + --- INITIAL_SETUP invoked --- + Description of tesed behavior [.003 sec] + Description of another behavior [.004 sec] + + +Warnings: + + 1) test_package + Duplicate annotation "--%beforeall". Annotation ignored. + at "UT3_TESTER.TEST_PACKAGE.INITIAL_SETUP", line 5 + 2) test_package + Annotation "--%beforeall" cannot be used with annotation: "--%test" + at "UT3_TESTER.TEST_PACKAGE.SOME_TEST", line 9 + +Finished in .012158 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 2 warning(s) +``` + + +### Afterall + +Marks annotated procedure to be executed after all test procedures in a suite. + +If `--%afterall` raises an exception the following will happen: +- a warning will be raised, indicating that `--%afterall` procedure has failed +- execution will continue uninterrupted for rest of the suite + +If `--%afterall` raises an exception, it can have negative impact on other tests, as the environment was not cleaned-up after the tests. +This however doesn't have direct impact on test execution within current suite, as the tests are already complete by the time `--%afterall` is called. + +When multiple `--%afterall` procedures are defined in a suite, all of them will be executed after invoking all tests from the suite. + +For multiple `--%afterall` procedures order of execution is defined by annotation position in the package specification. + +All rules defined for `--%beforeall` also apply for `--%afterall` annotation. See [beforeall](#Beforeall) for more details. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%afterall + procedure cleanup_stuff; + +end; +/ +create or replace package body test_package as + procedure cleanup_stuff is + begin + dbms_output.put_line('---CLEANUP_STUFF invoked ---'); + end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.003 sec] + Description of another behavior [.005 sec] + ---CLEANUP_STUFF invoked --- + +Finished in .014161 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +### Beforeeach + +Marks annotated procedure to be executed before each test procedure in a suite. + +The procedure annotated as `--%beforeeach` is getting executed before each test in a suite. +That means that the procedure will be executed as many times as there are test in suite package. + +If a test is marked as disabled the `--%beforeeach` procedure is not invoked for that test. + +If `--%beforeeach` raises an unhandled exception the following will happen: +- the following `--%beforeeach` as well as all `--%beforetest` for that test **will not be executed** +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +As a rule, the `--%beforeeach` execution gets aborted if preceding `--%beforeeach` failed. + +When multiple `--%beforeeach` procedures are defined in a suite, all of them will be executed before invoking each test. + +For multiple `--%beforeeach` procedures order of execution is defined by annotation position in the package specification. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeeach + procedure setup_for_test; + + --%beforeall + procedure setup_stuff; +end; +/ +create or replace package body test_package as + procedure setup_stuff is + begin + dbms_output.put_line('---SETUP_STUFF invoked ---'); + end; + + procedure setup_for_test is + begin + dbms_output.put_line('---SETUP_FOR_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + ---SETUP_STUFF invoked --- + Description of tesed behavior [.004 sec] + ---SETUP_FOR_TEST invoked --- + ---SOME_TEST invoked --- + Description of another behavior [.006 sec] + ---SETUP_FOR_TEST invoked --- + ---OTHER_TEST invoked --- + +Finished in .014683 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + + +### Aftereach + +Marks annotated procedure to be executed after each test procedure in a suite. + +The procedure annotated as `--%aftereach` is getting executed after each test in a suite. +That means that the procedure will be executed as many times as there are test in suite package. + +If a test is marked as disabled the `--%aftereach` procedure is not invoked for that test. + +If `--%aftereach` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +When multiple `--%aftereach` procedures are defined in a suite, all of them will be executed after invoking each test. + +For multiple `--%aftereach` procedures order of execution is defined by the annotation position in the package specification. + +As a rule, the `--%aftereach` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftereach` procedures have raised unhandled exceptions. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%aftereach + procedure cleanup_for_test; + + --%afterall + procedure cleanup_stuff; +end; +/ +create or replace package body test_package as + procedure cleanup_stuff is + begin + dbms_output.put_line('---CLEANUP_STUFF invoked ---'); + end; + + procedure cleanup_for_test is + begin + dbms_output.put_line('---CLEANUP_FOR_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` ```sql -create or replace package test_pkg is +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.006 sec] + ---SOME_TEST invoked --- + ---CLEANUP_FOR_TEST invoked --- + Description of another behavior [.006 sec] + ---OTHER_TEST invoked --- + ---CLEANUP_FOR_TEST invoked --- + ---CLEANUP_STUFF invoked --- + +Finished in .018115 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` - --%suite(Name of suite) +### Beforetest - --%test - -- this single-line comment makes the TEST annotation no longer associated with the procedure - procedure first_test; +Indicates a specific setup to be executed for a test. +Used alongside `--%test` annotation. Indicates procedure name to be executed before specific test. - --%test - --procedure some_test; /* This TEST annotation is not associated with any procedure*/ +The `--%beforetest` procedures are executed after invoking all `--%beforeeach` for a test. + +If a test is marked as disabled the `--%beforetest` procedure is not invoked for that test. + +If `--%beforetest` raises an unhandled exception the following will happen: +- the following `--%beforetest` for that test **will not be executed** +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite - --%test(Name of another test) - procedure another_test; +When multiple `--%beforetest` procedures are defined for a test, all of them will be executed before invoking the test. - --%test - /** - * this multi-line comment makes the TEST annotation no longer associated with the procedure - */ - procedure yet_another_test; -end test_pkg; +For multiple `--%beforetest` procedures order of execution is defined by annotation position in the package specification. + +As a rule, the `--%beforetest` execution gets aborted if preceding `--%beforeeach` or `--%beforetest` failed. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%beforetest(setup_for_a_test) + --%beforetest(another_setup_for_a_test) + procedure some_test; + + --%test(Description of another behavior) + --%beforetest(setup_for_a_test) + procedure other_test; + + procedure another_setup_for_a_test; + + procedure setup_for_a_test; + +end; +/ +create or replace package body test_package as + procedure setup_for_a_test is + begin + dbms_output.put_line('---SETUP_FOR_A_TEST invoked ---'); + end; + + procedure another_setup_for_a_test is + begin + dbms_output.put_line('---ANOTHER_SETUP_FOR_A_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` ``` -Procedure annotations are defined right before the procedure they reference, no empty lines are allowed, no comment lines can exist between annotation and the procedure. +Tests for a package + Description of tesed behavior [.011 sec] + ---SETUP_FOR_A_TEST invoked --- + ---ANOTHER_SETUP_FOR_A_TEST invoked --- + ---SOME_TEST invoked --- + Description of another behavior [.005 sec] + ---SETUP_FOR_A_TEST invoked --- + ---OTHER_TEST invoked --- + +Finished in .018446 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + + +### Aftertest +Indicates a specific cleanup to be executed for a test. +Used alongside `--%test` annotation. Indicates procedure name to be executed after specific test. -Package level annotations need to be separated by at least one empty line from the underlying procedure annotations. +The `--%aftertest` procedures are executed before invoking any `--%aftereach` for a test. + +If a test is marked as disabled the `--%aftertest` procedure is not invoked for that test. + +If `--%aftertest` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the following `--%aftertest` and all `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +When multiple `--%aftertest` procedures are defined for a test, all of them will be executed before invoking the test. + +For multiple `--%aftertest` procedures order of execution is defined by annotation position in the package specification. + +As a rule, the `--%aftertest` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftertest` procedures have raised unhandled exceptions. -Example of invalid package level annotation. ```sql -create or replace package test_pkg is - --%suite(Name of suite) - --%test - procedure first_test; -end test_pkg; +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%aftertest(cleanup_for_a_test) + --%aftertest(another_cleanup_for_a_test) + procedure some_test; + + --%test(Description of another behavior) + --%aftertest(cleanup_for_a_test) + procedure other_test; + + procedure another_cleanup_for_a_test; + + procedure cleanup_for_a_test; + +end; +/ +create or replace package body test_package as + procedure cleanup_for_a_test is + begin + dbms_output.put_line('---CLEANUP_FOR_A_TEST invoked ---'); + end; + + procedure another_cleanup_for_a_test is + begin + dbms_output.put_line('---ANOTHER_CLEANUP_FOR_A_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.01 sec] + ---SOME_TEST invoked --- + ---CLEANUP_FOR_A_TEST invoked --- + ---ANOTHER_CLEANUP_FOR_A_TEST invoked --- + Description of another behavior [.006 sec] + ---OTHER_TEST invoked --- + ---CLEANUP_FOR_A_TEST invoked --- + +Finished in .018691 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` -If a package specification contains the `--%suite` annotation, it is treated as a test package and is processed by the framework. +### Context -Some annotations accept parameters like `--%suite`, `--%test` and `--%displayname`. The parameters for annotations need to be placed in brackets. -Values for parameters should be provided without any quotation marks. -If the parameters are placed without brackets or with incomplete brackets, they will be ignored. -Example: `--%suite(The name of suite without closing bracket` +In most of the cases, the code to be tested is consisting of PLSQL packages containing procedures and functions. +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. + +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. + +This is where utPLSQL contexts come handy. -# Example of an annotated test package +Contexts allow for creating sub-suites within a suite package and they allow for grouping of tests that are somehow related. + +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 a 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 + +The below example illustrates usage of `--%context` for separating tests for individual procedures of package. + +Tested tables and code ```sql -create or replace package test_pkg is +create table rooms ( + room_key number primary key, + name varchar2(100) not null +); + +create table room_contents ( + contents_key number primary key, + room_key number not null, + name varchar2(100) not null, + create_date timestamp default current_timestamp not null, + constraint fk_rooms foreign key (room_key) references rooms (room_key) +); + +create or replace package rooms_management is + + procedure remove_rooms_by_name( a_name rooms.name%type ); + + procedure add_rooms_content( + a_room_name rooms.name%type, + a_content_name room_contents.name%type + ); + +end; +/ + +create or replace package body rooms_management is + procedure remove_rooms_by_name( a_name rooms.name%type ) is + begin + if a_name is null then + raise program_error; + end if; + delete from rooms where name like a_name; + end; + + procedure add_rooms_content( + a_room_name rooms.name%type, + a_content_name room_contents.name%type + ) is + l_room_key rooms.room_key%type; + begin + + select room_key into l_room_key + from rooms where name = a_room_name; + + insert into room_contents + (contents_key, room_key, name) + select nvl(max(contents_key)+1, 1) as contents_key, + l_room_key, + a_content_name + from room_contents; + end; +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 - --%suite(Name of suite) - --%suitepath(all.globaltests) +```sql +create or replace package test_rooms_management is + --%suite(Rooms management) + --%beforeall - procedure global_setup; + procedure setup_rooms; - --%afterall - procedure global_cleanup; + + --%context(remove_rooms_by_name) + + --%test(Removes a room without content in it) + procedure remove_empty_room; - /* Such comments are allowed */ + --%test(Raises exception when null room name given) + --%throws(-6501) + procedure null_room_name; - --%test - --%displayname(Name of a test) - --%throws(-20145,-20146,-20189,-20563) - procedure some_test; + --%endcontext + + + --%context(add_rooms_content) - --%test(Name of another test) - --%beforetest(setup_another_test) - --%aftertest(cleanup_another_test) - procedure another_test; + --%test(Fails when room name is not valid) + --%throws(-1403) + procedure fails_on_room_name_invalid; - --%test - --%displayname(Name of test) - --%disabled - procedure disabled_test; + --%test(Fails when content name is null) + --%throws(-1400) + procedure fails_on_content_null; - --%test(Name of test) - --%rollback(manual) - procedure no_transaction_control_test; + --%test(Adds a content to existing room) + procedure add_content_success; - procedure setup_another_test; + --%endcontext - procedure cleanup_another_test; +end; +/ - --%beforeeach - procedure test_setup; +create or replace package body test_rooms_management is - --%aftereach - procedure test_cleanup; + procedure setup_rooms is + begin + insert all + into rooms values(1, 'Dining Room') + into rooms values(2, 'Living Room') + into rooms values(3, 'Bathroom') + select 1 from dual; + + insert all + into room_contents values(1, 1, 'Table', sysdate) + into room_contents values(3, 1, 'Chair', sysdate) + into room_contents values(4, 2, 'Sofa', sysdate) + into room_contents values(5, 2, 'Lamp', sysdate) + select 1 from dual; + + dbms_output.put_line('---SETUP_ROOMS invoked ---'); + end; + + procedure remove_empty_room is + l_rooms_not_named_b sys_refcursor; + l_remaining_rooms sys_refcursor; + begin + open l_rooms_not_named_b for select * from rooms where name not like 'B%'; + + remove_rooms_by_name('B%'); + + open l_remaining_rooms for select * from rooms; + ut.expect( l_remaining_rooms ).to_equal(l_rooms_not_named_b); + end; + + procedure room_with_content is + begin + remove_rooms_by_name('Living Room'); + end; + + procedure null_room_name is + begin + remove_rooms_by_name(NULL); + end; + + procedure fails_on_room_name_invalid is + begin + add_rooms_content('bad room name','Chair'); + end; + + procedure fails_on_content_null is + begin + --Act + add_rooms_content('Dining Room',null); + --Assert by --%throws annotation + end; + + procedure add_content_success is + l_expected room_contents.name%type; + l_actual room_contents.name%type; + begin + --Arrange + l_expected := 'Table'; + + --Act + add_rooms_content( 'Dining Room', l_expected ); + --Assert + select name into l_actual from room_contents + where contents_key = (select max(contents_key) from room_contents); + + ut.expect( l_actual ).to_equal( l_expected ); + end; -end test_pkg; +end; +/ ``` -# Supported annotations +When te tests are executed +```sql +exec ut.run('test_package'); +``` +The following report is displayed +``` +Rooms management + ---SETUP_ROOMS invoked --- + remove_rooms_by_name + Removes a room without content in it [.015 sec] + Raises exception when null room name given [.002 sec] + add_rooms_content + Fails when room name is not valid [.003 sec] + Fails when content name is null [.003 sec] + Adds a content to existing room [.003 sec] + +Finished in .035261 seconds +5 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` -| Annotation |Level| Description | -| --- | --- | --- | -| `--%suite()` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | -| `--%suitepath()` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | -| `--%displayname()` | Package/procedure | Human-readable and meaningful description of a suite/test. `%displayname(Name of the suite/test)`. The annotation is provided for flexibility and convenience only. It has exactly the same meaning as `` in `test` and `suite` annotations. If description is provided using both `suite`/`test` and `displayname`, then the one defined as last takes precedence. | -| `--%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 procedure must throw one of the exception numbers provided. If no valid numbers were provided as annotation parameters the annotation is ignored. Applicable to test procedures only. | -| `--%beforeall` | Procedure | Denotes that the annotated procedure 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. | -| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | -| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | -| `--%beforetest()` | Procedure | Denotes that mentioned procedure should be executed before the annotated `%test` procedure. | -| `--%aftertest()` | Procedure | Denotes that mentioned procedure 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. | -# Suitepath concept +### Suitepath -It is very likely that the application for which you are going to introduce tests consists of many different packages or procedures/functions. Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedure in a single package or even packages themselves might relate to a common module. +It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. +Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedures in a single package and packages might be grouped into modules and modules into subject areas. + +As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and also allows for nesting modules. Let's say you have a complex insurance application that deals with policies, claims and payments. The payment module contains several packages for payment recognition, charging, planning etc. The payment recognition module among others contains a complex `recognize_payment` procedure that associates received money to the policies. @@ -135,7 +1085,6 @@ If you want to create tests for your application it is recommended to structure * Payment tests * Payments recognition * Payments set off - * Payouts 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: @@ -148,8 +1097,7 @@ create or replace package test_payment_recognition as --%test(Recognize payment by policy number) procedure test_recognize_by_num; - --%test - --%displayname(Recognize payment by payment purpose) + --%test(Recognize payment by payment purpose) procedure test_recognize_by_purpose; --%test(Recognize payment by customer) @@ -165,12 +1113,11 @@ create or replace package test_payment_set_off as --%suite(Payment set off tests) --%suitepath(payments) - --%test(Set off creation test) + --%test(Creates set off) procedure test_create_set_off; - --%test - --%displayname(Set off annulation test) - procedure test_annulate_set_off; + --%test(Cancels set off) + procedure test_cancel_set_off; end test_payment_set_off; ``` @@ -197,7 +1144,8 @@ A `%suitepath` can be provided in three ways: * [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.]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. -# Using automatic rollback in tests + +### Rollback By default, changes performed by every setup, cleanup and test procedure are isolated by savepoints. This solution is suitable for use-cases where the code that is being tested as well as the unit tests themselves do not use transaction control (commit/rollback) or DDL commands. @@ -221,62 +1169,15 @@ Doing so allows your tests to use the framework's automatic transaction control When you are testing code that performs explicit or implicit commits, you may set the test procedure to run as an autonomous transaction with `pragma autonomous_transaction`. Keep in mind that when your test runs as autonomous transaction it will not see the data prepared in a setup procedure unless the setup procedure committed the changes. -# Order of execution - -When processing the test suite `test_pkg` defined in [Example of annotated test package](#example), the order of execution will be as follows. - -``` - create a savepoint 'beforeall' - execute global_setup - - create savepoint 'beforeeach' - execute test_setup - execute some_test - execute test_cleanup - rollback to savepoint 'beforeeach' - - create savepoint 'beforeeach' - execute test_setup - execute setup_another_test - execute another_test - execute cleanup_another_test - execute test_cleanup - rollback to savepoint 'beforeeach' - - mark disabled_test as disabled - - execute test_setup - execute no_transaction_control_test - execute test_cleanup - - execute global_cleanup - rollback to savepoint 'beforeall' - -``` - -# Annotation cache - -utPLSQL needs to scan the source of package specifications to identify and parse annotations. -To improve framework startup time, especially when dealing with database users owning large amounts of packages, the framework has a built-in persistent cache for annotations. - -The annotation cache is checked for staleness and refreshed automatically on every run. The initial startup of utPLSQL for a schema will take longer than consecutive executions. - -If you are in a situation where your database is controlled via CI/CD server and is refreshed/wiped before each run of your tests, consider building the annotation cache upfront and taking a snapshot of the database after the cache has been refreshed. +**Note** +> The `--%suitepath` annotation, when used, must be provided with a value of path. +> The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. +> +> Example: `--%suitepath(org.utplsql.core.utils)` +> -To build the annotation cache without actually invoking any tests, call `ut_runner.rebuild_annotation_cache(a_object_owner)` for every unit test owner for which you want to have the annotation cache prebuilt. -Example: -```sql -exec ut_runner.rebuild_annotation_cache('HR'); -``` - -To purge the annotation cache call `ut_runner.purge_cache(a_object_owner, a_object_type)`. -Both parameters are optional and if not provided, all owners/object_types will be purged. -Example: -```sql -exec ut_runner.purge_cache('HR', 'PACKAGE'); -``` -# Throws annotation +### Throws The `--%throws` annotation allows you to specify a list of exception numbers that can be expected from a test. @@ -363,3 +1264,156 @@ Failures: Finished in .038692 seconds 4 tests, 3 failed, 0 errored, 0 disabled, 0 warning(s) ``` + +## Order of execution + +```sql +create or replace package test_employee_pkg is + + --%suite(Employee management) + --%suitepath(com.my_company.hr) + --%rollback(auto) + + --%beforeall + procedure setup_employees; + + --%beforeall + procedure setup_departments; + + --%afterall + procedure cleanup_log_table; + + --%context(add_employee) + + --%beforeeach + procedure setup_for_add_employees; + + --%test(Raises exception when employee already exists) + --%throws(-20145) + procedure add_existing_employee; + + --%test(Inserts employee to emp table) + procedure add_employee; + + --%endcontext + + + --%context(remove_employee) + + --%beforeall + procedure setup_for_remove_employee; + + --%test(Removed employee from emp table) + procedure del_employee; + + --%endcontext + + --%test(Test without context) + --%beforetest(setup_another_test) + --%aftertest(cleanup_another_test) + procedure some_test; + + --%test(Name of test) + --%disabled + procedure disabled_test; + + --%test(Name of test) + --%rollback(manual) + procedure no_transaction_control_test; + + procedure setup_another_test; + + procedure cleanup_another_test; + + --%beforeeach + procedure set_session_context; + + --%aftereach + procedure cleanup_session_context; + +end test_employee_pkg; +``` + +When processing the test suite `test_employee_pkg` defined in [Example of annotated test package](#example), the order of execution will be as follows. + +``` + create a savepoint 'before-suite' + execute setup_employees (--%beforeall) + execute setup_departments (--%beforeall) + + create a savepoint 'before-context' + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute setup_for_add_employees (--%beforeeach from context) + execute add_existing_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + create savepoint 'before-test' (--%suite) + execute test_setup (--%beforeeach) + execute setup_for_add_employees (--%beforeeach from context) + execute add_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + rollback to savepoint 'before-context' + + create a savepoint 'before-context' + execute setup_for_remove_employee (--%beforeall from context) + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute add_existing_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + rollback to savepoint 'before-context' + + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute some_test (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute setup_another_test (--%beforetest) + execute another_test (--%test) + execute cleanup_another_test (--%aftertest) + execute test_cleanup (--%beforeeach) + rollback to savepoint 'before-test' + + mark disabled_test as disabled (--%test --%disabled) + + execute test_setup (--%beforeeach) + execute no_transaction_control_test (--%test) + execute test_cleanup (--%aftertest) + + execute global_cleanup (--%afterall) + rollback to savepoint 'before-suite' +``` + +**Note** +>utPLSQL does not guarantee ordering of tests in suite. On contrary utPLSQL might give random order of tests/contexts in suite. +> +>Order of execution within multiple occurrences of `before`/`after` procedures is determined by the order of annotations in specific block (context/suite) of package specification. + + +## Annotation cache + +utPLSQL needs to scan the source of package specifications to identify and parse annotations. +To improve framework startup time, especially when dealing with database users owning large amounts of packages, the framework has a built-in persistent cache for annotations. + +The annotation cache is checked for staleness and refreshed automatically on every run. The initial startup of utPLSQL for a schema will take longer than consecutive executions. + +If you are in a situation where your database is controlled via CI/CD server and is refreshed/wiped before each run of your tests, consider building the annotation cache upfront and taking a snapshot of the database after the cache has been refreshed. + +To build the annotation cache without actually invoking any tests, call `ut_runner.rebuild_annotation_cache(a_object_owner)` for every unit test owner for which you want to have the annotation cache prebuilt. +Example: +```sql +exec ut_runner.rebuild_annotation_cache('HR'); +``` + +To purge the annotation cache call `ut_runner.purge_cache(a_object_owner, a_object_type)`. +Both parameters are optional and if not provided, all owners/object_types will be purged. +Example: +```sql +exec ut_runner.purge_cache('HR', 'PACKAGE'); +``` + diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 6e49c8cc1..ae1176be5 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -780,24 +780,23 @@ Since NULL is neither *true* nor *false*, both expectations will report failure. The matrix below illustrates the data types supported by different matchers. -| | be_between | be_empty | be_false | be_greater_than | be_greater_or_equal | be_less_or_equal | be_less_than | be_like | be_not_null | be_null | be_true | equal | have_count | match | -|:------------------------------|:----------:|:--------:|:--------:|:---------------:|:-------------------:|:----------------:|:------------:|:-------:|:-----------:|:-------:|:-------:|:-----:|:----------:|:-----:| -| blob | | | | | | | | | X | X | | X | | | -| boolean | | | X | | | | | | X | X | X | X | | | -| clob | | | | | | | | X | X | X | | X | | X | -| date | X | | | X | X | X | X | | X | X | | X | | | -| number | X | | | X | X | X | X | | X | X | | X | | | -| timestamp | X | | | X | X | X | X | | X | X | | X | | | -| timestamp with timezone | X | | | X | X | X | X | | X | X | | X | | | -| timestamp with local timezone | X | | | X | X | X | X | | X | X | | X | | | -| varchar2 | X | | | | | | | X | X | X | | X | | X | -| interval year to month | X | | | X | X | X | X | | X | X | | X | | | -| interval day to second | X | | | X | X | X | X | | X | X | | X | | | -| refcursor | | X | | | | | | | X | X | | X | X | | -| nested table (as anydata) | | X | | | | | | | X | X | | X | X | | -| varray (as anydata) | | X | | | | | | | X | X | | X | X | | -| object (as anydata) | | | | | | | | | X | X | | X | | | - +| Matcher |blob |boolean|clob |date |number|timestamp|timestamp
with
timezone|timestamp
with
local
timezone|varchar2|interval
year
to
month|interval
day
to
second|cursor|nested
table
/ varray|object| +|:----------------------|:---:|:-----:|:---:|:---:|:----:|:-------:|:---------------------------:|:------------------------------------:|:------:|:-----------------------------:|:-----------------------------:|:----:|:-------------------------:|:----:| +|**be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**be_false** | | X | | | | | | | | | | | | | +|**be_true** | | X | | | | | | | | | | | | | +|**be_greater_than** | | | | X | X | X | X | X | | X | X | | | | +|**be_greater_or_equal**| | | | X | X | X | X | X | | X | X | | | | +|**be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | +|**be_less_than** | | | | X | X | X | X | X | | X | X | | | | +|**be_between** | | | | X | X | X | X | X | X | X | X | | | | +|**equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**match** | | | X | | | | | | X | | | | | | +|**be_like** | | | X | | | | | | X | | | | | | +|**be_empty** | | | | | | | | | | | | X | X | | +|**have_count** | | | | | | | | | | | | X | X | | + diff --git a/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql b/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql index d512c0856..1b9e94bfb 100644 --- a/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql +++ b/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql @@ -13,42 +13,39 @@ set echo off @@ut_custom_reporter.tpb declare - suite1 ut_logical_suite; - suite2 ut_logical_suite; - suite_complex ut_logical_suite; - l_reporter ut_output_reporter_base; - l_listener ut_event_listener; - l_run ut_run; + l_parent_suite ut_logical_suite; + l_suite ut_suite; + l_test ut_test; + l_reporter ut_output_reporter_base; + l_run ut_run; begin - suite1 := ut_logical_suite(a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Test Suite 1', a_path => null); - - suite1.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); - - suite2 := ut_logical_suite(a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Test Suite 2', a_path => null); - - suite2.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); - - suite_complex := ut_logical_suite( a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Complex Test Suite', a_path => null); - suite_complex.items := ut_suite_items(suite1, suite2); + ut_event_manager.initialize(); + l_parent_suite := ut_logical_suite( a_object_owner=>null, a_object_name => null, a_name => 'complex_test_suite', a_path => null); + + l_suite := ut_suite(user, 'ut_exampletest'); + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + + l_suite.add_item(l_test); + l_parent_suite.add_item(l_suite); + + + l_suite := ut_suite(user, 'ut_exampletest2'); + l_test := ut_test(user, 'UT_EXAMPLETEST2','UT_EXAMPLETEST'); + l_test.before_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','TEARDOWN',ut_utils.gc_after_test)); + + l_suite.add_item(l_test); + l_parent_suite.add_item(l_suite); -- provide a reporter to process results l_reporter := ut_custom_reporter(a_tab_size => 2); - l_listener := ut_event_listener(ut_reporters(l_reporter)); - l_run := ut_run(ut_suite_items(suite_complex)); - l_run.do_execute(l_listener); + ut_event_manager.add_listener(l_reporter); + l_run := ut_run(ut_suite_items(l_parent_suite)); + l_run.do_execute(); + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_reporter.lines_to_dbms_output(); end; / @@ -56,3 +53,4 @@ end; drop type ut_custom_reporter; drop package ut_exampletest; drop package ut_exampletest2; +exec dbms_session.reset_package; diff --git a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql index 8d9ecad0c..aea43e8aa 100644 --- a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql +++ b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql @@ -1,14 +1,21 @@ --Shows that even a very large package specification can be parsed quite quickly --Clear Screen -Set Serveroutput On Size Unlimited format truncated +set serveroutput on set echo off --install the example unit test packages @@tst_pkg_huge.pks declare l_suites ut_suite_items; + l_items ut_suite_items; begin l_suites := ut_suite_manager.configure_execution_by_path(ut_varchar2_list(USER||'.TST_PKG_HUGE')); + l_items := treat( + treat( treat( l_suites( 1 ) as ut_logical_suite ).items( 1 ) as ut_logical_suite ).items( 1 ) + as ut_logical_suite + ).items; + dbms_output.put_line('Created '||l_items.count||' tests in suite'); + dbms_output.put_line(' Last test name='||l_items(l_items.last).name); end; / diff --git a/examples/developer_examples/RunExampleTestSuite.sql b/examples/developer_examples/RunExampleTestSuite.sql index 8d077e663..a0a470e46 100644 --- a/examples/developer_examples/RunExampleTestSuite.sql +++ b/examples/developer_examples/RunExampleTestSuite.sql @@ -11,43 +11,37 @@ set echo off @@ut_exampletest2.pkb declare - suite ut_logical_suite; - listener ut_event_listener := ut_event_listener(ut_reporters()); - test_item ut_test; - expectation ut_expectation_result; + l_suite ut_logical_suite; + l_test ut_test; + l_expectation ut_expectation_result; begin - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + l_suite := ut_suite(user, 'ut_exampletest'); + l_suite.description := 'Test Suite Name'; + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + l_test := ut_test(user, 'UT_EXAMPLETEST2','ut_exAmpletest'); + l_test.description := 'Another example test'; + l_test.before_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','TEARDOWN',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); - - suite.do_execute(listener); + l_suite.do_execute(); -- No reporter used in this example so outputing the results manually. - for test_idx in suite.items.first .. suite.items.last loop - test_item := treat(suite.items(test_idx) as ut_test); + for test_idx in l_suite.items.first .. l_suite.items.last loop + l_test := treat(l_suite.items(test_idx) as ut_test); dbms_output.put_line('---------------------------------------------------'); - dbms_output.put_line('Test:' || test_item.item.form_name); - dbms_output.put_line('Result: ' || ut_utils.test_result_to_char(test_item.result)); + dbms_output.put_line('Test:' || l_test.item.form_name); + dbms_output.put_line('Result: ' || ut_utils.test_result_to_char(l_test.result)); dbms_output.put_line('expectation Results:'); - for i in 1 .. test_item.failed_expectations.count loop - expectation := test_item.failed_expectations(i); - dbms_output.put_line(i || ' - result: ' || ut_utils.test_result_to_char(expectation.result)); - dbms_output.put_line(i || ' - Message: ' || expectation.message); + for i in 1 .. l_test.failed_expectations.count loop + l_expectation := l_test.failed_expectations(i); + dbms_output.put_line(i || ' - result: ' || ut_utils.test_result_to_char(l_expectation.result)); + dbms_output.put_line(i || ' - Message: ' || l_expectation.message); end loop; end loop; dbms_output.put_line('---------------------------------------------------'); diff --git a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql index d86c862c9..4f9172510 100644 --- a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql +++ b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql @@ -15,31 +15,23 @@ declare suite ut_logical_suite; l_doc_reporter ut_output_reporter_base := ut_documentation_reporter(); l_tc_reporter ut_output_reporter_base := ut_teamcity_reporter(); - l_listener ut_event_listener := ut_event_listener(ut_reporters(l_doc_reporter, l_tc_reporter)); l_run ut_run; begin - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + ut_event_manager.initialize(); + ut_event_manager.add_listener(l_doc_reporter); + ut_event_manager.add_listener(l_tc_reporter); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + suite := ut_suite(user, 'ut_exampletest'); + suite.description := 'Test Suite Name'; - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); + suite.add_item(ut_test(user,'ut_exampletest','ut_exAmpletest')); + suite.add_item(ut_test(user, 'UT_EXAMPLETEST2','UT_EXAMPLETEST')); -- provide a reporter to process results l_run := ut_run(ut_suite_items(suite)); - l_run.do_execute(l_listener); + l_run.do_execute(); + + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_doc_reporter.lines_to_dbms_output(0,0); l_tc_reporter.lines_to_dbms_output(0,0); end; diff --git a/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql b/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql index 6658228d4..59fe15662 100644 --- a/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql +++ b/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql @@ -14,37 +14,34 @@ set echo off @@ut_custom_reporter.tpb declare - suite ut_logical_suite; + l_suite ut_logical_suite; + l_test ut_test; l_reporter ut_output_reporter_base; - l_listener ut_event_listener; l_run ut_run; begin + ut_event_manager.initialize(); -- Install ut_custom_reporter first from example folder - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + l_suite := ut_suite(user, 'ut_exampletest'); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); + l_test := ut_test(user, 'UT_EXAMPLETEST2','ut_exAmpletest'); + l_test.description := 'Another example test'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','TEARDOWN',ut_utils.gc_after_test)); + l_suite.add_item(l_test); -- provide a reporter to process results tabbing each hierarcy level by tab_size l_reporter := ut_custom_reporter(a_tab_size => 2); - l_listener := ut_event_listener(ut_reporters(l_reporter)); - l_run := ut_run(ut_suite_items(suite)); - l_run.do_execute(l_listener); + ut_event_manager.add_listener(l_reporter); + l_run := ut_run(ut_suite_items(l_suite)); + l_run.do_execute(); + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_reporter.lines_to_dbms_output(0,0); end; / @@ -54,3 +51,4 @@ end; drop type ut_custom_reporter; drop package ut_exampletest; drop package ut_exampletest2; +exec dbms_session.reset_package; diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 2caecead9..a6af0c924 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -318,7 +318,6 @@ begin 'source/core/types/ut_suite_item.tpb', 'source/core/types/ut_suite_item.tps', 'source/core/types/ut_suite_items.tps', - 'source/core/types/ut_suite_item_base.tps', 'source/core/types/ut_test.tpb', 'source/core/types/ut_test.tps', 'source/core/types/ut_varchar2_list.tps', @@ -440,32 +439,32 @@ begin --run for the first time to gather coverage and timings on reporters too l_reporter := ut_coverage_html_reporter(a_project_name => 'utPLSQL v3'); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); l_reporter := ut_coverage_sonar_reporter(); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); l_reporter := ut_coveralls_reporter(); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); ut_coverage.coverage_stop_develop(); --run for the second time to get the coverage report l_reporter := ut_coverage_html_reporter(a_project_name => 'utPLSQL v3'); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); :html_reporter_id := l_reporter.get_reporter_id; l_reporter := ut_coverage_sonar_reporter(); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); :sonar_reporter_id := l_reporter.get_reporter_id; l_reporter := ut_coveralls_reporter(); l_reporter.after_calling_run(l_test_run); - l_reporter.finalize(); + l_reporter.on_finalize(l_test_run); :coveralls_reporter_id := l_reporter.get_reporter_id; end; / diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql index eda2506e8..7de3aab68 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql @@ -30,8 +30,8 @@ begin l_test_proc := treat(l_test2_suite.items(1) as ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; @@ -44,8 +44,8 @@ begin dbms_output.put_line(q'[ut.expect(l_test2_suite.name).to_equal('test_package_2');=]'||l_test2_suite.name); dbms_output.put_line(q'[ut.expect(l_test2_suite.items.count).to_equal(1);=]'||l_test2_suite.items.count); dbms_output.put_line(q'[ut.expect(l_test_proc.name).to_equal('test2');=]'||l_test_proc.name); - dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test is not null)); - dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test is not null)); + dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test_list.count())); + dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test_list.count())); end if; end; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql index f44f9f636..d476c064d 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql @@ -30,8 +30,8 @@ begin l_test_proc := treat(l_test2_suite.items(1) as ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; @@ -44,8 +44,8 @@ begin dbms_output.put_line(q'[ut.expect(l_test2_suite.name).to_equal('test_package_2');=]'||l_test2_suite.name); dbms_output.put_line(q'[ut.expect(l_test2_suite.items.count).to_equal(1);=]'||l_test2_suite.items.count); dbms_output.put_line(q'[ut.expect(l_test_proc.name).to_equal('test2');=]'||l_test_proc.name); - dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test is not null)); - dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test is not null)); + dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test_list.count())); + dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test_list.count())); end if; end; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql index 6851655ed..969524f5e 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql @@ -25,16 +25,16 @@ begin ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(1) as ut_test).disabled_flag).to_equal(0); ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(2) as ut_test).disabled_flag).to_equal(0); -- temporary behavior. diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql index ce9eae41d..c918529b5 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql @@ -25,16 +25,16 @@ begin ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(1) as ut_test).disabled_flag).to_equal(0); ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(2) as ut_test).disabled_flag).to_equal(0); -- temporary behavior. diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql index a93915900..58958881f 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql @@ -27,8 +27,8 @@ begin ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql index ec63c7582..009078767 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql @@ -27,8 +27,8 @@ begin ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql index f3bba15e6..986b497a0 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql @@ -25,8 +25,8 @@ begin l_test_proc := treat(l_test1_suite.items(1) as ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql index 54482c953..f1b536e21 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql @@ -25,8 +25,8 @@ begin l_test_proc := treat(l_test1_suite.items(1) as ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.gc_success then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.AfterEachExecuted.sql b/old_tests/ut_test/ut_test.AfterEachExecuted.sql index 860858709..e35564b7b 100644 --- a/old_tests/ut_test/ut_test.AfterEachExecuted.sql +++ b/old_tests/ut_test/ut_test.AfterEachExecuted.sql @@ -5,12 +5,11 @@ declare simple_test ut_test := ut_test( a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' - ,a_after_each_proc_name => 'aftereach' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'aftereach', ut_utils.gc_after_each)); --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_success and ut_example_tests.g_char2 = 'F' then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql index 8b61c36c6..4d12cd513 100644 --- a/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql @@ -3,16 +3,15 @@ PROMPT Does not execute test and reports error when test aftereach procedure nam --Arrange declare simple_test ut_test := ut_test( - a_after_each_proc_name => 'invalid setup name' - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_exampletest' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_after_each)); ut_example_tests.g_char := 'x'; ut_example_tests.g_char2 := 'x'; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_char2 = 'x' and simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql b/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql index 0f8733d64..2700e1a10 100644 --- a/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql @@ -3,14 +3,13 @@ PROMPT Does not invoke aftereach procedure when aftereach procedure name for a t --Arrange declare simple_test ut_test := ut_test( - a_after_each_proc_name => null - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', '', ut_utils.gc_after_each)); --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_char2 = 'a' then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.BeforeEachExecuted.sql b/old_tests/ut_test/ut_test.BeforeEachExecuted.sql index 81dd76c5c..5015d69fd 100644 --- a/old_tests/ut_test/ut_test.BeforeEachExecuted.sql +++ b/old_tests/ut_test/ut_test.BeforeEachExecuted.sql @@ -3,15 +3,14 @@ PROMPT Invoke beforeeach procedure --Arrange declare simple_test ut_test := ut_test( - a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' - ,a_before_each_proc_name => 'beforeeach' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'beforeeach', ut_utils.gc_before_each)); ut_example_tests.g_number2 := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_number2 = 1 then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql index febed25ec..f03b76f08 100644 --- a/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql @@ -3,15 +3,14 @@ PROMPT Does not execute test and reports error when test beforeeach procedure na --Arrange declare simple_test ut_test := ut_test( - a_before_each_proc_name => 'invalid setup name' - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_exampletest' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_before_each)); ut_example_tests.g_char2 := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error and ut_example_tests.g_char2 is null then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql b/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql index 1bff47ebe..e717b128f 100644 --- a/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql @@ -3,15 +3,14 @@ PROMPT Does not invoke setup procedure when beforeeach procedure name for a test --Arrange declare simple_test ut_test := ut_test( - a_before_each_proc_name => null - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', '', ut_utils.gc_before_each)); ut_example_tests.g_number2 := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_number2 is null then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.IgnoreTollbackToSavepointException.sql b/old_tests/ut_test/ut_test.IgnoreTollbackToSavepointException.sql index 6d980f976..94ad60c3d 100644 --- a/old_tests/ut_test/ut_test.IgnoreTollbackToSavepointException.sql +++ b/old_tests/ut_test/ut_test.IgnoreTollbackToSavepointException.sql @@ -2,11 +2,11 @@ PROMPT Checks that rollback exception does not make run to fail --Arrange declare - simple_test ut_test := ut_test(a_object_name => 'ut_example_tests', a_name => 'ut_commit_test',a_rollback_type => ut_utils.gc_rollback_auto); - listener ut_event_listener := ut_event_listener(ut_reporters()); + simple_test ut_test := ut_test(a_object_name => 'ut_example_tests', a_name => 'ut_commit_test'); begin + simple_test.rollback_type := ut_utils.gc_rollback_auto; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_success then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.OwnerNameInvalid.sql b/old_tests/ut_test/ut_test.OwnerNameInvalid.sql index 2606a28dc..ba19e0767 100644 --- a/old_tests/ut_test/ut_test.OwnerNameInvalid.sql +++ b/old_tests/ut_test/ut_test.OwnerNameInvalid.sql @@ -3,10 +3,9 @@ PROMPT Reports error when test owner name for a test is invalid --Arrange declare simple_test ut_test := ut_test( a_object_owner => 'invalid owner name', a_object_name => 'ut_example_tests', a_name => 'ut_passing_test'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then diff --git a/old_tests/ut_test/ut_test.OwnerNameNull.sql b/old_tests/ut_test/ut_test.OwnerNameNull.sql index 59be60951..68acf9144 100644 --- a/old_tests/ut_test/ut_test.OwnerNameNull.sql +++ b/old_tests/ut_test/ut_test.OwnerNameNull.sql @@ -3,10 +3,9 @@ PROMPT Executes test in current schema when test owner name for a test is null --Arrange declare simple_test ut_test:= ut_test(a_object_owner => null, a_object_name => 'ut_example_tests', a_name => 'ut_passing_test'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_char = 'a' then :test_result := simple_test.result; diff --git a/old_tests/ut_test/ut_test.PackageInInvalidState.sql b/old_tests/ut_test/ut_test.PackageInInvalidState.sql index 2c7e66749..31056dcfd 100644 --- a/old_tests/ut_test/ut_test.PackageInInvalidState.sql +++ b/old_tests/ut_test/ut_test.PackageInInvalidState.sql @@ -9,10 +9,9 @@ set termout on declare simple_test ut_test := ut_test(a_object_name => 'invalid_package', a_name => 'ut_exampletest'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.PackageNameInvalid.sql b/old_tests/ut_test/ut_test.PackageNameInvalid.sql index 663d397dc..246af45db 100644 --- a/old_tests/ut_test/ut_test.PackageNameInvalid.sql +++ b/old_tests/ut_test/ut_test.PackageNameInvalid.sql @@ -3,10 +3,9 @@ PROMPT Reports error when unit test package name for a test is invalid --Arrange declare simple_test ut_test := ut_test(a_object_name => 'invalid test package name', a_name => 'ut_passing_test'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.PackageNameNull.sql b/old_tests/ut_test/ut_test.PackageNameNull.sql index 4e59117a6..f4420b6b0 100644 --- a/old_tests/ut_test/ut_test.PackageNameNull.sql +++ b/old_tests/ut_test/ut_test.PackageNameNull.sql @@ -3,10 +3,9 @@ PROMPT Reports error when unit test package name for a test is null --Arrange declare simple_test ut_test := ut_test(a_object_name => null, a_name => 'ut_passing_test'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.ProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.ProcedureNameInvalid.sql index 2a0072b87..a66f0deff 100644 --- a/old_tests/ut_test/ut_test.ProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.ProcedureNameInvalid.sql @@ -3,10 +3,9 @@ PROMPT Reports error when test procedure name for a test is invalid --Arrange declare simple_test ut_test := ut_test(a_object_name => 'ut_example_tests' ,a_name => 'invalid procedure name'); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.ProcedureNameNull.sql b/old_tests/ut_test/ut_test.ProcedureNameNull.sql index b841f2e62..1e702ab2c 100644 --- a/old_tests/ut_test/ut_test.ProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.ProcedureNameNull.sql @@ -3,10 +3,9 @@ PROMPT Reports error when test procedure name for a test is null --Arrange declare simple_test ut_test := ut_test(a_object_name => 'ut_example_tests', a_name => null); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql b/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql index 6085b319b..e928443f3 100644 --- a/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql +++ b/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql @@ -5,13 +5,12 @@ declare simple_test ut_test := ut_test( a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' - ,a_before_test_proc_name => 'setup' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'setup', ut_utils.gc_before_test)); ut_example_tests.g_number := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_number = 1 then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql index a006e1015..3f6fab022 100644 --- a/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql @@ -3,15 +3,14 @@ PROMPT Does not execute test and reports error when test setup procedure name fo --Arrange declare simple_test ut_test := ut_test( - a_before_test_proc_name => 'invalid setup name' - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_exampletest' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_before_test)); ut_example_tests.g_char := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_error and ut_example_tests.g_char is null then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql b/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql index 5e2ddc365..0622a2295 100644 --- a/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql @@ -3,15 +3,14 @@ PROMPT Does not invoke setup procedure when setup procedure name for a test is n --Arrange declare simple_test ut_test := ut_test( - a_before_test_proc_name => null - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', null, ut_utils.gc_before_test)); ut_example_tests.g_number := null; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_number is null then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql b/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql index b26542892..55c0026c1 100644 --- a/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql +++ b/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql @@ -5,12 +5,11 @@ declare simple_test ut_test := ut_test( a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' - ,a_after_test_proc_name => 'teardown' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'teardown', ut_utils.gc_after_test)); --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if simple_test.result = ut_utils.gc_success and ut_example_tests.g_char is null then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql index 47004b606..86bdff66a 100644 --- a/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql @@ -3,15 +3,14 @@ PROMPT Does not execute test and reports error when test teardown procedure name --Arrange declare simple_test ut_test := ut_test( - a_after_test_proc_name => 'invalid setup name' - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_exampletest' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_after_test)); ut_example_tests.g_char := 'x'; --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_char = 'x' and simple_test.result = ut_utils.gc_error then :test_result := ut_utils.gc_success; diff --git a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql index 4c44e3fb3..a1837d20c 100644 --- a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql @@ -3,19 +3,18 @@ PROMPT Does not invoke teardown procedure when teardown procedure name for a tes --Arrange declare simple_test ut_test := ut_test( - a_after_test_proc_name => null - ,a_object_name => 'ut_example_tests' + a_object_name => 'ut_example_tests' ,a_name => 'ut_passing_test' ); - listener ut_event_listener := ut_event_listener(ut_reporters()); begin + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', null, ut_utils.gc_after_test)); --Act - simple_test.do_execute(listener); + simple_test.do_execute(); --Assert if ut_example_tests.g_char = 'a' then :test_result := ut_utils.gc_success; else - dbms_output.put_line('expected: ut_example_tests.g_char = ''a'', got: '||ut_example_tests.g_char ); + dbms_output.put_line('expected: ut_example_tests.g_char = ''a'', got: '''||ut_example_tests.g_char||'''' ); end if; end; / diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index fbdb97e1f..78db64c77 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -42,10 +42,10 @@ create or replace package body ut_runner is return l_result; end; - procedure finish_run(a_listener in out ut_event_listener) is + procedure finish_run(a_run ut_run) is begin ut_utils.cleanup_temp_tables; - a_listener.fire_on_event(ut_utils.gc_finalize); + ut_event_manager.trigger_event(ut_utils.gc_finalize, a_run); ut_metadata.reset_source_definition_cache; ut_utils.read_cache_to_dbms_output(); ut_coverage_helper.cleanup_tmp_table(); @@ -78,21 +78,23 @@ create or replace package body ut_runner is a_coverage_schemes ut_varchar2_list := null, a_source_file_mappings ut_file_mappings := null, a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, a_fail_on_errors boolean default false ) is - l_items_to_run ut_run; - l_listener ut_event_listener; + l_run ut_run; l_coverage_schema_names ut_varchar2_rows; l_exclude_object_names ut_object_names := ut_object_names(); l_include_object_names ut_object_names; begin + ut_event_manager.initialize(); begin ut_expectation_processor.reset_invalidation_exception(); ut_utils.save_dbms_output_to_cache(); ut_console_reporter_base.set_color_enabled(a_color_console); if a_reporters is null or a_reporters.count = 0 then - l_listener := ut_event_listener(ut_reporters(ut_documentation_reporter())); + ut_event_manager.add_listener(ut_documentation_reporter()); else - l_listener := ut_event_listener(a_reporters); + for i in 1 .. a_reporters.count loop + ut_event_manager.add_listener(a_reporters(i)); + end loop; end if; if a_coverage_schemes is not empty then @@ -109,7 +111,7 @@ create or replace package body ut_runner is l_include_object_names := to_ut_object_list(a_include_objects, l_coverage_schema_names); - l_items_to_run := ut_run( + l_run := ut_run( ut_suite_manager.configure_execution_by_path(a_paths), a_paths, l_coverage_schema_names, @@ -118,19 +120,19 @@ create or replace package body ut_runner is set(a_source_file_mappings), set(a_test_file_mappings) ); - l_items_to_run.do_execute(l_listener); + l_run.do_execute(); - finish_run(l_listener); + finish_run(l_run); rollback; exception when others then - finish_run(l_listener); + finish_run(l_run); dbms_output.put_line(dbms_utility.format_error_backtrace); dbms_output.put_line(dbms_utility.format_error_stack); rollback; raise; end; - if a_fail_on_errors and l_items_to_run.result in (ut_utils.gc_failure, ut_utils.gc_error) then + if a_fail_on_errors and l_run.result in (ut_utils.gc_failure, ut_utils.gc_error) then raise_application_error(ut_utils.gc_some_tests_failed, 'Some tests failed'); end if; end; @@ -200,7 +202,5 @@ create or replace package body ut_runner is close l_cursor; end; - - end ut_runner; / diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 47f31cb32..84efbaa91 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -22,7 +22,6 @@ create or replace package body ut_annotation_parser as type tt_comment_list is table of varchar2(32767) index by pls_integer; gc_annotation_qualifier constant varchar2(1) := '%'; - gc_multiline_comment_pattern constant varchar2(50) := '/\*.*?\*/'; gc_annot_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*-- *('||gc_annotation_qualifier||'.*?)$'; -- chr(09) is a tab character gc_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}'; gc_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}'; @@ -32,13 +31,6 @@ create or replace package body ut_annotation_parser as gc_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || gc_regexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?'; - function delete_multiline_comments(a_source in clob) return clob is - begin - return regexp_replace(srcstr => a_source - ,pattern => gc_multiline_comment_pattern - ,modifier => 'n'); - end; - procedure add_annotation( a_annotations in out nocopy ut_annotations, a_position positiven, @@ -138,16 +130,14 @@ create or replace package body ut_annotation_parser as -- parse the comment block for the syntactically correct annotations and store them as an array add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); - l_annot_proc_ind := regexp_instr(srcstr => a_source - ,pattern => ';' - ,occurrence => 1 - ,position => l_annot_proc_ind + length(l_annot_proc_block) ); + l_annot_proc_ind := instr(a_source, ';', l_annot_proc_ind + length(l_annot_proc_block) ); end loop; end add_procedure_annotations; function extract_and_replace_comments(a_source in out nocopy clob) return tt_comment_list is l_comments tt_comment_list; - l_comment_pos pls_integer; + l_comment_pos binary_integer; + l_comment_line binary_integer; l_comment_replacer varchar2(50); l_source clob := a_source; begin @@ -165,14 +155,15 @@ create or replace package body ut_annotation_parser as -- position index is shifted by 1 because gc_annot_comment_pattern contains ^ as first sign -- but after instr index already points to the char on that line l_comment_pos := l_comment_pos-1; - l_comments(l_comments.count + 1) := trim(regexp_substr(srcstr => a_source + l_comment_line := regexp_count(substr(a_source,1,l_comment_pos),chr(10),1,'m')+1; + l_comments(l_comment_line) := trim(regexp_substr(srcstr => a_source ,pattern => gc_annot_comment_pattern ,occurrence => 1 ,position => l_comment_pos ,modifier => 'm' ,subexpression => 2)); - l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comments.count); + l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comment_line); l_source := regexp_replace(srcstr => a_source ,pattern => gc_annot_comment_pattern @@ -203,7 +194,7 @@ create or replace package body ut_annotation_parser as l_comment_index positive; begin - l_source := delete_multiline_comments(l_source); + l_source := ut_utils.replace_multiline_comments(l_source); -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing -- this call modifies l_source diff --git a/source/core/coverage/ut_coverage_reporter_base.tpb b/source/core/coverage/ut_coverage_reporter_base.tpb index 7cde025e2..b675c07f1 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tpb +++ b/source/core/coverage/ut_coverage_reporter_base.tpb @@ -22,65 +22,65 @@ create or replace type body ut_coverage_reporter_base is ut_coverage.coverage_start(a_coverage_options => a_run.coverage_options); end; - overriding final member procedure before_calling_before_all(self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite) is + overriding final member procedure before_calling_before_all(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_before_all (self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite) is + overriding final member procedure after_calling_before_all (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_before_each(self in out nocopy ut_coverage_reporter_base, a_suite in ut_test) is + overriding final member procedure before_calling_before_each(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_before_each (self in out nocopy ut_coverage_reporter_base, a_suite in ut_test) is + overriding final member procedure after_calling_before_each (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_before_test(self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure before_calling_before_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_before_test (self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure after_calling_before_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_after_test(self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure before_calling_after_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_after_test (self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure after_calling_after_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_after_each(self in out nocopy ut_coverage_reporter_base, a_suite in ut_test) is + overriding final member procedure before_calling_after_each(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_after_each (self in out nocopy ut_coverage_reporter_base, a_suite in ut_test) is + overriding final member procedure after_calling_after_each (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_after_all(self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite) is + overriding final member procedure before_calling_after_all(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_after_all (self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite) is + overriding final member procedure after_calling_after_all (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; diff --git a/source/core/coverage/ut_coverage_reporter_base.tps b/source/core/coverage/ut_coverage_reporter_base.tps index b7ef2a68f..087f4edff 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tps +++ b/source/core/coverage/ut_coverage_reporter_base.tps @@ -17,26 +17,26 @@ create or replace type ut_coverage_reporter_base under ut_output_reporter_base( */ overriding final member procedure before_calling_run(self in out nocopy ut_coverage_reporter_base, a_run ut_run), - overriding final member procedure before_calling_before_all(self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite), - overriding final member procedure after_calling_before_all (self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite), + overriding final member procedure before_calling_before_all(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_before_all (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_before_each(self in out nocopy ut_coverage_reporter_base, a_suite in ut_test), - overriding final member procedure after_calling_before_each (self in out nocopy ut_coverage_reporter_base, a_suite in ut_test), + overriding final member procedure before_calling_before_each(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_before_each (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_before_test(self in out nocopy ut_coverage_reporter_base, a_test in ut_test), - overriding final member procedure after_calling_before_test (self in out nocopy ut_coverage_reporter_base, a_test in ut_test), + overriding final member procedure before_calling_before_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_before_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_test in ut_test), - overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_test in ut_test), + overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_after_test(self in out nocopy ut_coverage_reporter_base, a_test in ut_test), - overriding final member procedure after_calling_after_test (self in out nocopy ut_coverage_reporter_base, a_test in ut_test), + overriding final member procedure before_calling_after_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_after_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_after_each(self in out nocopy ut_coverage_reporter_base, a_suite in ut_test), - overriding final member procedure after_calling_after_each (self in out nocopy ut_coverage_reporter_base, a_suite in ut_test), + overriding final member procedure before_calling_after_each(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_after_each (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_after_all(self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite), - overriding final member procedure after_calling_after_all (self in out nocopy ut_coverage_reporter_base, a_suite in ut_logical_suite) + overriding final member procedure before_calling_after_all(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_after_all (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) ) not final not instantiable diff --git a/source/core/types/ut_event_listener_base.tps b/source/core/events/ut_event_item.tps similarity index 51% rename from source/core/types/ut_event_listener_base.tps rename to source/core/events/ut_event_item.tps index d157b9458..caa0423dd 100644 --- a/source/core/types/ut_event_listener_base.tps +++ b/source/core/events/ut_event_item.tps @@ -1,4 +1,4 @@ -create or replace type ut_event_listener_base authid current_user as object( +create or replace type ut_event_item authid current_user as object ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -15,10 +15,12 @@ create or replace type ut_event_listener_base authid current_user as object( See the License for the specific language governing permissions and limitations under the License. */ - name varchar2(250), - member procedure fire_before_event(self in out nocopy ut_event_listener_base, a_event_name varchar2, a_item ut_suite_item_base), - member procedure fire_after_event(self in out nocopy ut_event_listener_base, a_event_name varchar2, a_item ut_suite_item_base), - member procedure fire_on_event(self in out nocopy ut_event_listener_base, a_event_timing varchar2, a_event_name varchar2, a_item ut_suite_item_base), - member procedure fire_on_event(self in out nocopy ut_event_listener_base, a_event_name varchar2) + + /** + * Object type is a pre-declaration to be referenced by ut_event_listener_base + * The true abstract type is ut_suite_item + */ + self_type varchar2(250 byte) + ) not final not instantiable / diff --git a/source/core/events/ut_event_listener.tps b/source/core/events/ut_event_listener.tps new file mode 100644 index 000000000..9711c8d79 --- /dev/null +++ b/source/core/events/ut_event_listener.tps @@ -0,0 +1,35 @@ +create or replace type ut_event_listener authid definer as object ( + /* + utPLSQL - Version 3 + Copyright 2016 - 2017 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + /** + * Object type is a pre-declaration to be referenced by ut_event_listener_base + * The true abstract type is ut_suite_item + */ + self_type varchar2(250 byte), + + /** + * Returns the list of events that are supported by particular implementation of the reporter + */ + not instantiable member function get_supported_events return ut_varchar2_list, + + /** + * Executes an action for a given event name + */ + not instantiable member procedure on_event( self in out nocopy ut_event_listener, a_event_name varchar2, a_event_item ut_event_item) +) not final not instantiable +/ diff --git a/source/core/events/ut_event_manager.pkb b/source/core/events/ut_event_manager.pkb new file mode 100644 index 000000000..312ddc9c3 --- /dev/null +++ b/source/core/events/ut_event_manager.pkb @@ -0,0 +1,79 @@ +create or replace package body ut_event_manager as + /* + utPLSQL - Version 3 + Copyright 2016 - 2017 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + type t_listeners is table of ut_event_listener; + subtype t_listener_number is binary_integer; + type t_listener_numbers is table of boolean index by t_listener_number; + type t_events_listeners is table of t_listener_numbers index by t_event_name; + + g_event_listeners_index t_events_listeners; + g_listeners t_listeners; + + procedure initialize is + begin + g_event_listeners_index.delete; + g_listeners := t_listeners(); + end; + + procedure trigger_event( a_event_name t_event_name, a_event_object ut_event_item ) is + begin + if a_event_name is not null and g_event_listeners_index.exists(a_event_name) + and g_event_listeners_index(a_event_name) is not null + then + for listener_number in 1 .. g_event_listeners_index(a_event_name).count loop + g_listeners(listener_number).on_event(a_event_name, a_event_object); + end loop; + end if; + end; + + procedure add_event( a_event_name t_event_name, a_listener_pos binary_integer ) is + begin + g_event_listeners_index(a_event_name)(a_listener_pos) := true; + end; + + procedure add_events( a_event_names ut_varchar2_list, a_listener_pos binary_integer ) is + begin + for i in 1 .. a_event_names.count loop + add_event(a_event_names(i), a_listener_pos); + end loop; + end; + + function add_listener( a_listener ut_event_listener ) return t_listener_number is + begin + if g_listeners is null then + g_listeners := t_listeners(); + end if; + g_listeners.extend; + g_listeners(g_listeners.last) := a_listener; + return g_listeners.last; + end; + + procedure add_listener( a_listener ut_event_listener ) is + l_event_names ut_varchar2_list; + begin + if a_listener is not null then + l_event_names := a_listener.get_supported_events(); + if l_event_names is not empty then + add_events( l_event_names, add_listener(a_listener ) ); + end if; + end if; + + end; + +end; +/ diff --git a/source/core/events/ut_event_manager.pks b/source/core/events/ut_event_manager.pks new file mode 100644 index 000000000..4e690082c --- /dev/null +++ b/source/core/events/ut_event_manager.pks @@ -0,0 +1,27 @@ +create or replace package ut_event_manager authid current_user as + /* + utPLSQL - Version 3 + Copyright 2016 - 2017 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + subtype t_event_name is varchar2(250); + + procedure trigger_event( a_event_name t_event_name, a_event_object ut_event_item ); + + procedure initialize; + + procedure add_listener( a_listener ut_event_listener ); + +end; +/ diff --git a/source/core/types/ut_event_listener.tpb b/source/core/types/ut_event_listener.tpb deleted file mode 100644 index 501c40689..000000000 --- a/source/core/types/ut_event_listener.tpb +++ /dev/null @@ -1,97 +0,0 @@ -create or replace type body ut_event_listener is - /* - utPLSQL - Version 3 - Copyright 2016 - 2017 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - constructor function ut_event_listener(self in out nocopy ut_event_listener, a_reporters ut_reporters) return self as result is - begin - reporters := a_reporters; - return; - end; - - overriding member procedure fire_before_event(self in out nocopy ut_event_listener, a_event_name varchar2, a_item ut_suite_item_base) is - begin - self.fire_on_event('before', a_event_name, a_item); - end; - - overriding member procedure fire_after_event(self in out nocopy ut_event_listener, a_event_name varchar2, a_item ut_suite_item_base) is - begin - self.fire_on_event('after', a_event_name, a_item); - end; - - overriding member procedure fire_on_event(self in out nocopy ut_event_listener, a_event_timing varchar2, a_event_name varchar2, a_item ut_suite_item_base) is - begin - for i in 1..self.reporters.count loop - if a_event_timing = 'before' then - if a_event_name = ut_utils.gc_run then - self.reporters(i).before_calling_run(treat(a_item as ut_run)); - elsif a_event_name = ut_utils.gc_suite then - self.reporters(i).before_calling_suite(treat(a_item as ut_logical_suite)); - elsif a_event_name = ut_utils.gc_before_all then - self.reporters(i).before_calling_before_all(treat(a_item as ut_logical_suite)); - elsif a_event_name = ut_utils.gc_before_each then - self.reporters(i).before_calling_before_each(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_test then - self.reporters(i).before_calling_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_before_test then - self.reporters(i).before_calling_before_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_test_execute then - self.reporters(i).before_calling_test_execute(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_test then - self.reporters(i).before_calling_after_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_each then - self.reporters(i).before_calling_after_each(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_all then - self.reporters(i).before_calling_after_all(treat(a_item as ut_logical_suite)); - end if; - elsif a_event_timing = 'after' then - if a_event_name = ut_utils.gc_run then - self.reporters(i).after_calling_run(treat(a_item as ut_run)); - elsif a_event_name = ut_utils.gc_suite then - self.reporters(i).after_calling_suite(treat(a_item as ut_logical_suite)); - elsif a_event_name = ut_utils.gc_before_all then - self.reporters(i).after_calling_before_all(treat(a_item as ut_logical_suite)); - elsif a_event_name = ut_utils.gc_before_each then - self.reporters(i).after_calling_before_each(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_test then - self.reporters(i).after_calling_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_before_test then - self.reporters(i).after_calling_before_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_test_execute then - self.reporters(i).after_calling_test_execute(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_test then - self.reporters(i).after_calling_after_test(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_each then - self.reporters(i).after_calling_after_each(treat(a_item as ut_test)); - elsif a_event_name = ut_utils.gc_after_all then - self.reporters(i).after_calling_after_all(treat(a_item as ut_logical_suite)); - end if; - end if; - end loop; - - end fire_on_event; - - overriding member procedure fire_on_event(self in out nocopy ut_event_listener, a_event_name varchar2) is - begin - for i in 1..self.reporters.count loop - if a_event_name = ut_utils.gc_finalize then - self.reporters(i).finalize(); - end if; - end loop; - end fire_on_event; - -end; -/ diff --git a/source/core/types/ut_event_listener.tps b/source/core/types/ut_event_listener.tps deleted file mode 100644 index d9a063362..000000000 --- a/source/core/types/ut_event_listener.tps +++ /dev/null @@ -1,25 +0,0 @@ -create or replace type ut_event_listener under ut_event_listener_base( - /* - utPLSQL - Version 3 - Copyright 2016 - 2017 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - reporters ut_reporters, - constructor function ut_event_listener(self in out nocopy ut_event_listener, a_reporters ut_reporters) return self as result, - overriding member procedure fire_before_event(self in out nocopy ut_event_listener, a_event_name varchar2, a_item ut_suite_item_base), - overriding member procedure fire_after_event(self in out nocopy ut_event_listener, a_event_name varchar2, a_item ut_suite_item_base), - overriding member procedure fire_on_event(self in out nocopy ut_event_listener, a_event_timing varchar2, a_event_name varchar2, a_item ut_suite_item_base), - overriding member procedure fire_on_event(self in out nocopy ut_event_listener, a_event_name varchar2) -) -/ diff --git a/source/core/types/ut_executable.tpb b/source/core/types/ut_executable.tpb index e67ad8a64..e51440408 100644 --- a/source/core/types/ut_executable.tpb +++ b/source/core/types/ut_executable.tpb @@ -17,22 +17,18 @@ create or replace type body ut_executable is */ constructor function ut_executable( - self in out nocopy ut_executable, a_context ut_suite_item, + self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result is begin + self.self_type := $$plsql_unit; self.associated_event_name := a_associated_event_name; - self.owner_name := a_context.object_owner; - self.object_name := a_context.object_name; + self.owner_name := a_owner; + self.object_name := a_package; self.procedure_name := a_procedure_name; return; end; - member function is_defined return boolean is - begin - return self.procedure_name is not null and self.object_name is not null; - end; - member function is_valid(self in out nocopy ut_executable) return boolean is l_result boolean := false; l_message_part varchar2(4000) := 'Call params for ' || self.associated_event_name || ' are not valid: '; @@ -45,7 +41,7 @@ create or replace type body ut_executable is elsif self.procedure_name is null then self.error_stack := l_message_part || 'procedure is not defined'; elsif not ut_metadata.procedure_exists(self.owner_name, self.object_name, self.procedure_name) then - self.error_stack := l_message_part || 'package missing procedure ' + self.error_stack := l_message_part || 'procedure does not exist ' || upper(self.owner_name || '.' || self.object_name || '.' ||self.procedure_name); else l_result := true; @@ -59,13 +55,13 @@ create or replace type body ut_executable is return ut_metadata.form_name(owner_name, object_name, procedure_name); end; - member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) is + member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_item, a_listener); + l_completed_without_errors := self.do_execute(a_item); end do_execute; - member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) return boolean is + member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean is l_statement varchar2(4000); l_status number; l_cursor_number number; @@ -91,17 +87,16 @@ create or replace type body ut_executable is end loop; end save_dbms_output; begin - if self.is_defined() then - l_start_transaction_id := dbms_transaction.local_transaction_id(true); - - -- report to application_info - ut_utils.set_client_info(self.procedure_name); + l_start_transaction_id := dbms_transaction.local_transaction_id(true); - --listener - before call to executable - a_listener.fire_before_event(self.associated_event_name, a_item); + -- report to application_info + ut_utils.set_client_info(self.procedure_name); - ut_metadata.do_resolve(a_owner => self.owner_name, a_object => self.object_name, a_procedure_name => self.procedure_name); + --listener - before call to executable + ut_event_manager.trigger_event('before_'||self.associated_event_name, self); + l_completed_without_errors := self.is_valid(); + if l_completed_without_errors then l_statement := 'declare' || chr(10) || ' l_error_stack varchar2(32767);' || chr(10) || @@ -137,15 +132,16 @@ create or replace type body ut_executable is if self.error_stack like '%ORA-04068%' or self.error_stack like '%ORA-04061%' then ut_expectation_processor.set_invalidation_exception(); end if; - --listener - after call to executable - a_listener.fire_after_event(self.associated_event_name, a_item); + end if; - l_end_transaction_id := dbms_transaction.local_transaction_id(); - if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then - a_item.add_transaction_invalidator(self.form_name()); - end if; - ut_utils.set_client_info(null); + --listener - after call to executable + ut_event_manager.trigger_event('after_'||self.associated_event_name, self); + + l_end_transaction_id := dbms_transaction.local_transaction_id(); + if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then + a_item.add_transaction_invalidator(self.form_name()); end if; + ut_utils.set_client_info(null); return l_completed_without_errors; end do_execute; diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps index b7b97510b..79813c391 100644 --- a/source/core/types/ut_executable.tps +++ b/source/core/types/ut_executable.tps @@ -1,4 +1,4 @@ -create or replace type ut_executable authid current_user as object( +create or replace type ut_executable under ut_event_item( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -25,17 +25,16 @@ create or replace type ut_executable authid current_user as object( error_backtrace varchar2(4000), error_stack varchar2(4000), serveroutput clob, - constructor function ut_executable( self in out nocopy ut_executable, a_context ut_suite_item, a_procedure_name varchar2, a_associated_event_name varchar2) return self as result, + constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2) return self as result, member function is_valid(self in out nocopy ut_executable) return boolean, - member function is_defined return boolean, member function form_name return varchar2, - member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item), /** * executes the defines executable * returns true if executed without exceptions * returns false if exceptions were raised */ - member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) return boolean, + member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean, member function get_error_stack_trace return varchar2 ) not final / diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb index 0c57a3898..9aeaa29c6 100644 --- a/source/core/types/ut_executable_test.tpb +++ b/source/core/types/ut_executable_test.tpb @@ -1,28 +1,29 @@ create or replace type body ut_executable_test as constructor function ut_executable_test( - self in out nocopy ut_executable_test, a_context ut_suite_item, + self in out nocopy ut_executable_test, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result is begin + self.self_type := $$plsql_unit; self.associated_event_name := a_associated_event_name; - self.owner_name := a_context.object_owner; - self.object_name := a_context.object_name; + self.owner_name := a_owner; + self.object_name := a_package; self.procedure_name := a_procedure_name; return; end; member procedure do_execute( - self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, + a_expected_error_codes in ut_integer_list ) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_item, a_listener, a_expected_error_codes); + l_completed_without_errors := self.do_execute(a_item, a_expected_error_codes); end do_execute; member function do_execute( - self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, + a_expected_error_codes in ut_integer_list ) return boolean is l_expected_except_message varchar2(4000); @@ -53,7 +54,7 @@ create or replace type body ut_executable_test as end; begin --Create a ut_executable object and call do_execute after that get the data to know the test's execution result - self.do_execute(a_item, a_listener); + self.do_execute(a_item); if a_expected_error_codes is not null and a_expected_error_codes is not empty then l_expected_except_message := failed_expec_errnum_message(a_expected_error_codes); diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps index c5502b636..af3cb1923 100644 --- a/source/core/types/ut_executable_test.tps +++ b/source/core/types/ut_executable_test.tps @@ -16,18 +16,18 @@ create or replace type ut_executable_test authid current_user under ut_executabl limitations under the License. */ constructor function ut_executable_test( - self in out nocopy ut_executable_test, a_context ut_suite_item, + self in out nocopy ut_executable_test, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result, member procedure do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_integer_list ), member function do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_integer_list ) return boolean ) final; diff --git a/source/core/types/ut_executables.tps b/source/core/types/ut_executables.tps new file mode 100644 index 000000000..f74d3f7ca --- /dev/null +++ b/source/core/types/ut_executables.tps @@ -0,0 +1,19 @@ +create or replace type ut_executables as + /* + utPLSQL - Version 3 + Copyright 2016 - 2017 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + table of ut_executable +/ diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 94b2ee66f..17cadd449 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -17,11 +17,13 @@ create or replace type body ut_logical_suite as */ constructor function ut_logical_suite( - self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, a_path varchar2 + self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_path varchar2 ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, ut_utils.gc_rollback_auto, false); + self.init(a_object_owner, a_object_name, a_name); + self.path := a_path; + self.disabled_flag := ut_utils.boolean_to_int(false); self.items := ut_suite_items(); return; end; @@ -37,37 +39,45 @@ create or replace type body ut_logical_suite as self.items(self.items.last) := a_item; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) is + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite) is begin - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop - self.items(i).mark_as_skipped(a_listener); + self.items(i).mark_as_skipped(); end loop; self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); self.calc_execution_result(); end; - overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer) is + begin + self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + for i in 1 .. self.items.count loop + self.items(i).set_rollback_type(self.rollback_type); + end loop; + end; + + overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean is l_suite_savepoint varchar2(30); l_item_savepoint varchar2(30); l_completed_without_errors boolean; begin ut_utils.debug_log('ut_logical_suite.execute'); - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop -- execute the item (test or suite) - self.items(i).do_execute(a_listener); + self.items(i).do_execute(); end loop; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); return l_completed_without_errors; end; @@ -88,18 +98,18 @@ create or replace type body ut_logical_suite as self.result := l_result; end; - overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2) is + overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_logical_suite.fail'); - a_listener.fire_before_event(ut_utils.gc_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop -- execute the item (test or suite) - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); end; overriding member function get_error_stack_traces return ut_varchar2_list is diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index f26e68856..e9f260035 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -22,14 +22,15 @@ create or replace type ut_logical_suite under ut_suite_item ( items ut_suite_items, constructor function ut_logical_suite( - self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, a_path varchar2 + self in out nocopy ut_logical_suite, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_path varchar2 ) return self as result, member function is_valid(self in out nocopy ut_logical_suite) return boolean, member procedure add_item(self in out nocopy ut_logical_suite, a_item ut_suite_item), - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base), - overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite), + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), + overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), - overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2), overriding member function get_error_stack_traces return ut_varchar2_list, overriding member function get_serveroutputs return clob, overriding member function get_transaction_invalidators return ut_varchar2_list diff --git a/source/core/types/ut_output_reporter_base.tpb b/source/core/types/ut_output_reporter_base.tpb index c65d6c879..d83f72792 100644 --- a/source/core/types/ut_output_reporter_base.tpb +++ b/source/core/types/ut_output_reporter_base.tpb @@ -74,7 +74,7 @@ create or replace type body ut_output_reporter_base is end if; end; - overriding final member procedure finalize(self in out nocopy ut_output_reporter_base) is + overriding final member procedure on_finalize(self in out nocopy ut_output_reporter_base, a_run in ut_run) is begin self.output_buffer.close(); end; diff --git a/source/core/types/ut_output_reporter_base.tps b/source/core/types/ut_output_reporter_base.tps index 8684bf78a..776711fad 100644 --- a/source/core/types/ut_output_reporter_base.tps +++ b/source/core/types/ut_output_reporter_base.tps @@ -28,7 +28,7 @@ create or replace type ut_output_reporter_base under ut_reporter_base( final member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_varchar2_rows pipelined, final member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor, final member procedure lines_to_dbms_output(self in ut_output_reporter_base, a_initial_timeout natural := null, a_timeout_sec natural := null), - overriding final member procedure finalize(self in out nocopy ut_output_reporter_base) + overriding final member procedure on_finalize(self in out nocopy ut_output_reporter_base, a_run in ut_run) ) not final not instantiable / diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb index c5a48996a..d86118bdd 100644 --- a/source/core/types/ut_reporter_base.tpb +++ b/source/core/types/ut_reporter_base.tpb @@ -50,20 +50,20 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -74,29 +74,29 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -107,20 +107,20 @@ create or replace type body ut_reporter_base is end; --suite hooks continued - member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -136,5 +136,80 @@ create or replace type body ut_reporter_base is null; end; + overriding member function get_supported_events return ut_varchar2_list is + begin + return ut_varchar2_list( + ut_utils.gc_before_run, + ut_utils.gc_before_suite, + ut_utils.gc_before_test, + ut_utils.gc_before_before_all, + ut_utils.gc_before_before_each, + ut_utils.gc_before_before_test, + ut_utils.gc_before_test_execute, + ut_utils.gc_before_after_test, + ut_utils.gc_before_after_each, + ut_utils.gc_before_after_all, + ut_utils.gc_after_run, + ut_utils.gc_after_suite, + ut_utils.gc_after_test, + ut_utils.gc_after_before_all, + ut_utils.gc_after_before_each, + ut_utils.gc_after_before_test, + ut_utils.gc_after_test_execute, + ut_utils.gc_after_after_test, + ut_utils.gc_after_after_each, + ut_utils.gc_after_after_all, + ut_utils.gc_finalize + ); + end; + + overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is + begin + case a_event_name + when ut_utils.gc_before_run + then self.before_calling_run(treat(a_event_item as ut_run)); + when ut_utils.gc_before_suite + then self.before_calling_suite(treat(a_event_item as ut_logical_suite)); + when ut_utils.gc_before_before_all + then self.before_calling_before_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_before_each + then self.before_calling_before_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_test + then self.before_calling_test(treat(a_event_item as ut_test)); + when ut_utils.gc_before_before_test + then self.before_calling_before_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_test_execute + then self.before_calling_test_execute(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_after_test + then self.before_calling_after_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_after_each + then self.before_calling_after_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_after_all + then self.before_calling_after_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_run + then self.after_calling_run(treat(a_event_item as ut_run)); + when ut_utils.gc_after_suite + then self.after_calling_suite(treat(a_event_item as ut_logical_suite)); + when ut_utils.gc_after_before_all + then self.after_calling_before_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_before_each + then self.after_calling_before_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_test + then self.after_calling_test(treat(a_event_item as ut_test)); + when ut_utils.gc_after_before_test + then self.after_calling_before_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_test_execute + then self.after_calling_test_execute(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_after_test + then self.after_calling_after_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_after_each + then self.after_calling_after_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_after_all + then self.after_calling_after_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_finalize + then self.on_finalize(treat(a_event_item as ut_run)); + end case; + end; + end; / diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps index a1ee6be25..2b5f8d853 100644 --- a/source/core/types/ut_reporter_base.tps +++ b/source/core/types/ut_reporter_base.tps @@ -1,4 +1,4 @@ -create or replace type ut_reporter_base authid current_user as object( +create or replace type ut_reporter_base under ut_event_listener ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -15,7 +15,6 @@ create or replace type ut_reporter_base authid current_user as object( See the License for the specific language governing permissions and limitations under the License. */ - self_type varchar2(250), id raw(32), final member procedure init(self in out nocopy ut_reporter_base, a_self_type varchar2), member procedure set_reporter_id(self in out nocopy ut_reporter_base, a_reporter_id raw), @@ -28,32 +27,32 @@ create or replace type ut_reporter_base authid current_user as object( -- suite hooks member procedure before_calling_suite(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), + member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_suite in ut_test), - member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_suite in ut_test), + member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_executable in ut_executable), -- test hooks member procedure before_calling_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_test(self in out nocopy ut_reporter_base, a_test in ut_test), --suite hooks continued - member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_suite in ut_test), - member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_suite in ut_test), + member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), + member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_suite(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), @@ -63,7 +62,17 @@ create or replace type ut_reporter_base authid current_user as object( -- This method is executed when reporter is getting finalized -- it differs from after_calling_run, as it is getting called, even when the run fails -- This way, you may close all open outputs, files, connections etc. that need closing before the run finishes - not instantiable member procedure finalize(self in out nocopy ut_reporter_base) + not instantiable member procedure on_finalize(self in out nocopy ut_reporter_base, a_run in ut_run), + + /** + * Returns the list of events that are supported by particular implementation of the reporter + */ + overriding member function get_supported_events return ut_varchar2_list, + + /** + * Delegates execution of event into individual reporting procedures + */ + overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) ) not final not instantiable diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index c7b2056cf..ccbd847fc 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -44,32 +44,31 @@ create or replace type body ut_run as return; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) is + overriding member procedure mark_as_skipped(self in out nocopy ut_run) is begin null; end; - overriding member function do_execute(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_run) return boolean is l_completed_without_errors boolean; begin ut_utils.debug_log('ut_run.execute'); - a_listener.fire_before_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_utils.gc_before_run, self); self.start_time := current_timestamp; -- clear anything that might stay in the session's cache ut_expectation_processor.clear_expectations; for i in 1 .. self.items.count loop - l_completed_without_errors := self.items(i).do_execute(a_listener); + l_completed_without_errors := self.items(i).do_execute(); end loop; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_run, self); - a_listener.fire_on_event(ut_utils.gc_finalize); + ut_event_manager.trigger_event(ut_utils.gc_after_run, self); return l_completed_without_errors; end; @@ -90,21 +89,21 @@ create or replace type body ut_run as self.result := l_result; end; - overriding member procedure mark_as_errored(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2) is + overriding member procedure mark_as_errored(self in out nocopy ut_run, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_run.fail'); - a_listener.fire_before_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_utils.gc_before_run, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_utils.gc_after_run, self); end; overriding member function get_error_stack_traces return ut_varchar2_list is diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index 02d77a93f..59bde318f 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -1,4 +1,4 @@ -create or replace type ut_run force under ut_suite_item ( +create or replace type ut_run under ut_suite_item ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -33,10 +33,10 @@ create or replace type ut_run force under ut_suite_item ( a_project_file_mappings ut_file_mappings := null, a_test_file_mappings ut_file_mappings := null ) return self as result, - overriding member procedure mark_as_skipped(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base), - overriding member function do_execute(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member procedure mark_as_skipped(self in out nocopy ut_run), + overriding member function do_execute(self in out nocopy ut_run) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_run), - overriding member procedure mark_as_errored(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + overriding member procedure mark_as_errored(self in out nocopy ut_run, a_error_stack_trace varchar2), overriding member function get_error_stack_traces return ut_varchar2_list, overriding member function get_serveroutputs return clob ) diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index ab9d7a94e..7b5525664 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -17,36 +17,25 @@ create or replace type body ut_suite as */ constructor function ut_suite ( - self in out nocopy ut_suite , a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_path varchar2, a_description varchar2 := null, - a_rollback_type integer := null, a_disabled_flag boolean := false, a_before_all_proc_name varchar2 := null, - a_after_all_proc_name varchar2 := null + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_suite_name varchar2 := null ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, a_rollback_type, a_disabled_flag); - self.before_all := ut_executable(self, a_before_all_proc_name, ut_utils.gc_before_all); + self.init(a_object_owner, a_object_name, nvl(a_suite_name, a_object_name)); self.items := ut_suite_items(); - self.after_all := ut_executable(self, a_after_all_proc_name, ut_utils.gc_after_all); + before_all_list := ut_executables(); + after_all_list := ut_executables(); return; end; - overriding member function is_valid(self in out nocopy ut_suite) return boolean is - l_is_valid boolean; - begin - l_is_valid := - ( not self.before_all.is_defined() or self.before_all.is_valid() ) and - ( not self.after_all.is_defined() or self.after_all.is_valid() ); - return l_is_valid; - end; - - overriding member function do_execute(self in out nocopy ut_suite, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_suite) return boolean is l_suite_savepoint varchar2(30); - l_suite_step_without_errors boolean; + l_no_errors boolean; procedure propagate_error(a_error_stack_trace varchar2) is begin for i in 1..self.items.count loop - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; end; begin @@ -55,58 +44,69 @@ create or replace type body ut_suite as ut_utils.set_action(self.object_name); if self.get_disabled_flag() then - self.mark_as_skipped(a_listener); + self.mark_as_skipped(); else - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; - if self.is_valid() then - l_suite_savepoint := self.create_savepoint_if_needed(); + l_suite_savepoint := self.create_savepoint_if_needed(); - --includes listener calls for before and after actions - l_suite_step_without_errors := self.before_all.do_execute(self, a_listener); - - if l_suite_step_without_errors then - for i in 1 .. self.items.count loop - self.items(i).do_execute(a_listener); - end loop; - else - propagate_error(self.before_all.get_error_stack_trace()); + --includes listener calls for before and after actions + l_no_errors := true; + for i in 1 .. self.before_all_list.count loop + l_no_errors := self.before_all_list(i).do_execute(self); + if not l_no_errors then + propagate_error(self.before_all_list(i).get_error_stack_trace()); + exit; end if; + end loop; - l_suite_step_without_errors := self.after_all.do_execute(self, a_listener); - if not l_suite_step_without_errors then - self.put_warning(self.after_all.get_error_stack_trace()); + if l_no_errors then + for i in 1 .. self.items.count loop + self.items(i).do_execute(); + end loop; + end if; + + for i in 1 .. after_all_list.count loop + l_no_errors := self.after_all_list(i).do_execute(self); + if not l_no_errors then + self.put_warning(self.after_all_list(i).get_error_stack_trace()); end if; + end loop; - self.rollback_to_savepoint(l_suite_savepoint); + self.rollback_to_savepoint(l_suite_savepoint); - else - propagate_error(ut_utils.table_to_clob(self.get_error_stack_traces())); - end if; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); end if; ut_utils.set_action(null); - return l_suite_step_without_errors; + return l_no_errors; end; overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list is l_stack_traces ut_varchar2_list := ut_varchar2_list(); begin - ut_utils.append_to_list(l_stack_traces, self.before_all.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_all.get_error_stack_trace()); + for i in 1 .. before_all_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_all_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. after_all_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_all_list(i).get_error_stack_trace()); + end loop; return l_stack_traces; end; overriding member function get_serveroutputs return clob is l_outputs clob; begin - ut_utils.append_to_clob(l_outputs, self.before_all.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_all.serveroutput ); + for i in 1 .. before_all_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_all_list(i).serveroutput); + end loop; + for i in 1 .. after_all_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_all_list(i).serveroutput); + end loop; return l_outputs; end; diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index 37995add9..c80316172 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -19,20 +19,17 @@ create or replace type ut_suite under ut_logical_suite ( * The procedure to be invoked before all of the items of the suite (executed once) * Procedure exists within the package of the suite */ - before_all ut_executable, + before_all_list ut_executables, /** * The procedure to be invoked after all of the items of the suite (executed once) * Procedure exists within the package of the suite */ - after_all ut_executable, + after_all_list ut_executables, constructor function ut_suite ( - self in out nocopy ut_suite , a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_path varchar2, a_description varchar2 := null, - a_rollback_type integer := null, a_disabled_flag boolean := false, a_before_all_proc_name varchar2 := null, - a_after_all_proc_name varchar2 := null + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_suite_name varchar2 := null ) return self as result, - overriding member function is_valid(self in out nocopy ut_suite) return boolean, - overriding member function do_execute(self in out nocopy ut_suite , a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member function do_execute(self in out nocopy ut_suite) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, overriding member function get_serveroutputs return clob ) diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index 2ddce3abc..5325be9de 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -16,18 +16,15 @@ create or replace type body ut_suite_item as limitations under the License. */ - member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2, a_path varchar2, a_rollback_type integer, a_disabled_flag boolean) is + member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2) is begin self.object_owner := a_object_owner; self.object_name := lower(trim(a_object_name)); self.name := lower(trim(a_name)); - self.description := a_description; - self.path := nvl(lower(trim(a_path)), self.object_name); - self.rollback_type := a_rollback_type; - self.disabled_flag := ut_utils.boolean_to_int(a_disabled_flag); self.results_count := ut_results_counter(); self.warnings := ut_varchar2_list(); self.transaction_invalidators := ut_varchar2_list(); + self.disabled_flag := ut_utils.boolean_to_int(false); end; member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean) is @@ -40,16 +37,26 @@ create or replace type body ut_suite_item as return ut_utils.int_to_boolean(self.disabled_flag); end; - final member procedure do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) is + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer) is + begin + self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + end; + + member function get_rollback_type return integer is + begin + return nvl(self.rollback_type, ut_utils.gc_rollback_default); + end; + +final member procedure do_execute(self in out nocopy ut_suite_item) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_listener); + l_completed_without_errors := self.do_execute(); end; member function create_savepoint_if_needed return varchar2 is l_savepoint varchar2(30); begin - if self.rollback_type = ut_utils.gc_rollback_auto then + if get_rollback_type() = ut_utils.gc_rollback_auto then l_savepoint := ut_utils.gen_savepoint_name(); execute immediate 'savepoint ' || l_savepoint; end if; @@ -60,7 +67,7 @@ create or replace type body ut_suite_item as ex_savepoint_not_exists exception; pragma exception_init(ex_savepoint_not_exists, -1086); begin - if self.rollback_type = ut_utils.gc_rollback_auto and a_savepoint is not null then + if get_rollback_type() = ut_utils.gc_rollback_auto and a_savepoint is not null then execute immediate 'rollback to ' || a_savepoint; end if; exception diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 7ebeb9c5c..6a36d88ee 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -1,4 +1,4 @@ -create or replace type ut_suite_item force under ut_suite_item_base ( +create or replace type ut_suite_item force under ut_event_item ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -16,14 +16,48 @@ create or replace type ut_suite_item force under ut_suite_item_base ( limitations under the License. */ + /** + * owner of the database object (package) + */ + object_owner varchar2(4000 byte), + /** + * name of the database object (package) + */ + object_name varchar2(4000 byte), + /** + * Name of the object (suite, sub-suite, test) + */ + name varchar2(4000 byte), + /** + * Description fo the suite item (as given by the annotation) + */ + description varchar2(4000 byte), + + /** + * Full path of the invocation of the item (including the items name itself) + */ + path varchar2(4000 byte), + /** + * The type of the rollback behavior + */ + rollback_type integer(1), + /** + * Indicates if the test is to be disabled by execution + */ + disabled_flag integer(1), + --execution result fields + start_time timestamp with time zone, + end_time timestamp with time zone, + result integer(1), + warnings ut_varchar2_list, results_count ut_results_counter, transaction_invalidators ut_varchar2_list, - member procedure init( - self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, - a_description varchar2, a_path varchar2, a_rollback_type integer, a_disabled_flag boolean), + member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2), member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean), member function get_disabled_flag return boolean, - not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), + member function get_rollback_type return integer, member function create_savepoint_if_needed return varchar2, member procedure rollback_to_savepoint(self in out nocopy ut_suite_item, a_savepoint varchar2), member function get_transaction_invalidators return ut_varchar2_list, @@ -33,10 +67,10 @@ create or replace type ut_suite_item force under ut_suite_item_base ( */ member function execution_time return number, - not instantiable member function do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) return boolean, - final member procedure do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + not instantiable member function do_execute(self in out nocopy ut_suite_item) return boolean, + final member procedure do_execute(self in out nocopy ut_suite_item), not instantiable member procedure calc_execution_result(self in out nocopy ut_suite_item), - not instantiable member procedure mark_as_errored(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + not instantiable member procedure mark_as_errored(self in out nocopy ut_suite_item, a_error_stack_trace varchar2), not instantiable member function get_error_stack_traces return ut_varchar2_list, not instantiable member function get_serveroutputs return clob, member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2) diff --git a/source/core/types/ut_suite_item_base.tps b/source/core/types/ut_suite_item_base.tps deleted file mode 100644 index 0802e85f2..000000000 --- a/source/core/types/ut_suite_item_base.tps +++ /dev/null @@ -1,61 +0,0 @@ -create or replace type ut_suite_item_base authid current_user as object ( - /* - utPLSQL - Version 3 - Copyright 2016 - 2017 utPLSQL Project - - Licensed under the Apache License, Version 2.0 (the "License"): - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - /** - * Object type is a pre-declaration to be referenced by ut_event_listener_base - * The true abstract type is ut_suite_item - */ - self_type varchar2(250 byte), - /** - * owner of the database object (package) - */ - object_owner varchar2(4000 byte), - /** - * name of the database object (package) - */ - object_name varchar2(4000 byte), - /** - * Name of the object (suite, sub-suite, test) - */ - name varchar2(4000 byte), - /** - * Description fo the suite item (as given by the annotation) - */ - description varchar2(4000 byte), - - /** - * Full path of the invocation of the item (including the items name itself) - */ - path varchar2(4000 byte), - /** - * The type of the rollback behavior - */ - rollback_type integer(1), - /** - * Indicates if the test is to be disabled by execution - */ - disabled_flag integer(1), - --execution result fields - start_time timestamp with time zone, - end_time timestamp with time zone, - result integer(1), - warnings ut_varchar2_list - -) -not final not instantiable -/ diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index d66706433..88250fac2 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -17,99 +17,83 @@ create or replace type body ut_test as */ constructor function ut_test( - self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, - a_path varchar2 := null, a_rollback_type integer := null, a_disabled_flag boolean := false, - a_before_each_proc_name varchar2 := null, a_before_test_proc_name varchar2 := null, - a_after_test_proc_name varchar2 := null, a_after_each_proc_name varchar2 := null, + self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_expected_error_codes ut_integer_list := null ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, a_rollback_type, a_disabled_flag); - self.before_each := ut_executable(self, a_before_each_proc_name, ut_utils.gc_before_each); - self.before_test := ut_executable(self, a_before_test_proc_name, ut_utils.gc_before_test); - self.item := ut_executable_test(self, a_name, ut_utils.gc_test_execute); - self.after_test := ut_executable(self, a_after_test_proc_name, ut_utils.gc_after_test); - self.after_each := ut_executable(self, a_after_each_proc_name, ut_utils.gc_after_each); - self.all_expectations := ut_expectation_results(); - self.failed_expectations := ut_expectation_results(); + self.init(a_object_owner, a_object_name, a_name); + self.item := ut_executable_test(a_object_owner, a_object_name, a_name, ut_utils.gc_test_execute); + self.before_each_list := ut_executables(); + self.before_test_list := ut_executables(); + self.after_test_list := ut_executables(); + self.after_each_list := ut_executables(); + self.all_expectations := ut_expectation_results(); + self.failed_expectations := ut_expectation_results(); self.expected_error_codes := a_expected_error_codes; return; end; - member procedure set_beforeeach(self in out nocopy ut_test, a_before_each_proc_name varchar2) is + overriding member procedure mark_as_skipped(self in out nocopy ut_test) is begin - self.before_each := ut_executable(self, a_before_each_proc_name, ut_utils.gc_before_each); - end; - - member procedure set_aftereach(self in out nocopy ut_test, a_after_each_proc_name varchar2) is - begin - self.after_each := ut_executable(self, a_after_each_proc_name, ut_utils.gc_after_each); - end; - - member function is_valid(self in out nocopy ut_test) return boolean is - l_is_valid boolean; - begin - l_is_valid := - ( not self.before_each.is_defined() or self.before_each.is_valid() ) and - ( not self.before_test.is_defined() or self.before_test.is_valid() ) and - ( self.item.is_valid() ) and - ( not self.after_test.is_defined() or self.after_test.is_valid() ) and - ( not self.after_each.is_defined() or self.after_each.is_valid() ); - return l_is_valid; - end; - - overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) is - begin - a_listener.fire_before_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; self.result := ut_utils.gc_disabled; ut_utils.debug_log('ut_test.execute - disabled'); self.results_count.set_counter_values(self.result); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end; - overriding member function do_execute(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) return boolean is - l_completed_without_errors boolean; - l_savepoint varchar2(30); + overriding member function do_execute(self in out nocopy ut_test) return boolean is + l_no_errors boolean; + l_savepoint varchar2(30); begin ut_utils.debug_log('ut_test.execute'); if self.get_disabled_flag() then - mark_as_skipped(a_listener); + mark_as_skipped(); else - a_listener.fire_before_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; - if self.is_valid() then - l_savepoint := self.create_savepoint_if_needed(); + l_savepoint := self.create_savepoint_if_needed(); - --includes listener calls for before and after actions - l_completed_without_errors := self.before_each.do_execute(self, a_listener); + --includes listener calls for before and after actions + l_no_errors := true; + for i in 1 .. self.before_each_list.count loop + l_no_errors := self.before_each_list(i).do_execute(self); + exit when not l_no_errors; + end loop; - if l_completed_without_errors then - l_completed_without_errors := self.before_test.do_execute(self, a_listener); + if l_no_errors then + for i in 1 .. self.before_test_list.count loop + l_no_errors := self.before_test_list(i).do_execute(self); + exit when not l_no_errors; + end loop; - if l_completed_without_errors then - -- execute the test - self.item.do_execute(self, a_listener, self.expected_error_codes); + if l_no_errors then + -- execute the test + self.item.do_execute(self, self.expected_error_codes); - end if; - -- perform cleanup regardless of the test or setup failure - self.after_test.do_execute(self, a_listener); end if; - - self.after_each.do_execute(self, a_listener); - self.rollback_to_savepoint(l_savepoint); + -- perform cleanup regardless of the test or setup failure + for i in 1 .. self.after_test_list.count loop + self.after_test_list(i).do_execute(self); + end loop; end if; + for i in 1 .. self.after_each_list.count loop + self.after_each_list(i).do_execute(self); + end loop; + self.rollback_to_savepoint(l_savepoint); + self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end if; - return l_completed_without_errors; + return l_no_errors; end; overriding member procedure calc_execution_result(self in out nocopy ut_test) is @@ -130,36 +114,52 @@ create or replace type body ut_test as ut_expectation_processor.clear_expectations(); end; - overriding member procedure mark_as_errored(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2) is + overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_test.fail'); - a_listener.fire_before_event(ut_utils.gc_test, self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; self.parent_error_stack_trace := a_error_stack_trace; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_test, self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end; overriding member function get_error_stack_traces(self ut_test) return ut_varchar2_list is l_stack_traces ut_varchar2_list := ut_varchar2_list(); begin ut_utils.append_to_list(l_stack_traces, self.parent_error_stack_trace); - ut_utils.append_to_list(l_stack_traces, self.before_each.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.before_test.get_error_stack_trace()); + for i in 1 .. before_each_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_each_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. before_test_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_test_list(i).get_error_stack_trace()); + end loop; ut_utils.append_to_list(l_stack_traces, self.item.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_test.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_each.get_error_stack_trace()); + for i in 1 .. after_test_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_test_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. after_each_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_each_list(i).get_error_stack_trace()); + end loop; return l_stack_traces; end; overriding member function get_serveroutputs return clob is l_outputs clob; begin - ut_utils.append_to_clob(l_outputs, self.before_each.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.before_test.serveroutput ); + for i in 1 .. before_each_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_each_list(i).serveroutput); + end loop; + for i in 1 .. before_test_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_test_list(i).serveroutput); + end loop; ut_utils.append_to_clob(l_outputs, self.item.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_test.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_each.serveroutput ); + for i in 1 .. after_test_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_test_list(i).serveroutput); + end loop; + for i in 1 .. after_each_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_each_list(i).serveroutput); + end loop; return l_outputs; end; end; diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index c295f524a..e8ecd3cb4 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -16,29 +16,25 @@ create or replace type ut_test under ut_suite_item ( limitations under the License. */ /* - * The procedure to be invoked before invoking the test and before_test procedure. - * Procedure exists within the same package as the test itself and is similar for all tests in the suite + * Procedures to be invoked before invoking the test and before_test_list procedures. */ - before_each ut_executable, + before_each_list ut_executables, /** - * The procedure to be invoked before invoking the test - * Procedure exists within the same package as the test itself + * Procedures to be invoked before invoking the test */ - before_test ut_executable, + before_test_list ut_executables, /** * The Test procedure to be executed */ item ut_executable_test, /** - * The procedure to be invoked after invoking the test - * Procedure exists within the same package as the test itself + * Procedures to be invoked after invoking the test */ - after_test ut_executable, + after_test_list ut_executables, /* - * The procedure to be invoked after invoking the test and after_test procedure. - * Procedure exists within the same package as the test itself and is similar for all tests in the suite + * Procedures to be invoked after invoking the test and after_test_list procedures. */ - after_each ut_executable, + after_each_list ut_executables, /** * The list of all expectations results as well as database errors encountered while invoking * the test procedure and the before_test/after_test blocks @@ -60,19 +56,13 @@ create or replace type ut_test under ut_suite_item ( */ expected_error_codes ut_integer_list, constructor function ut_test( - self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, - a_path varchar2 := null, a_rollback_type integer := null, a_disabled_flag boolean := false, - a_before_each_proc_name varchar2 := null, a_before_test_proc_name varchar2 := null, - a_after_test_proc_name varchar2 := null, a_after_each_proc_name varchar2 := null, + self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_expected_error_codes ut_integer_list := null ) return self as result, - member function is_valid(self in out nocopy ut_test) return boolean, - member procedure set_beforeeach(self in out nocopy ut_test, a_before_each_proc_name varchar2), - member procedure set_aftereach(self in out nocopy ut_test, a_after_each_proc_name varchar2), - overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base), - overriding member function do_execute(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member procedure mark_as_skipped(self in out nocopy ut_test), + overriding member function do_execute(self in out nocopy ut_test) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_test), - overriding member procedure mark_as_errored(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2), overriding member function get_error_stack_traces(self ut_test) return ut_varchar2_list, overriding member function get_serveroutputs return clob ) diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index 9414845ea..e39e0e33b 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -16,157 +16,657 @@ create or replace package body ut_suite_builder is limitations under the License. */ - ------------------ + subtype t_annotation_text is varchar2(4000); + subtype t_annotation_name is varchar2(4000); + subtype t_object_name is varchar2(500); + subtype t_annotation_position is binary_integer; + + gc_suite constant t_annotation_name := 'suite'; + gc_suitepath constant t_annotation_name := 'suitepath'; + gc_test constant t_annotation_name := 'test'; + gc_disabled constant t_annotation_name := 'disabled'; + gc_displayname constant t_annotation_name := 'displayname'; + gc_beforeall constant t_annotation_name := 'beforeall'; + gc_beforeeach constant t_annotation_name := 'beforeeach'; + gc_beforetest constant t_annotation_name := 'beforetest'; + gc_afterall constant t_annotation_name := 'afterall'; + gc_aftereach constant t_annotation_name := 'aftereach'; + gc_aftertest constant t_annotation_name := 'aftertest'; + gc_throws constant t_annotation_name := 'throws'; + gc_rollback constant t_annotation_name := 'rollback'; + gc_context constant t_annotation_name := 'context'; + gc_endcontext constant t_annotation_name := 'endcontext'; + + gc_placeholder constant varchar2(3) := '\\%'; + + --list of annotation texts for a given annotation indexed by annotation position: + --This would hold: ('some', 'other') for a single annotation name recurring in a single procedure example + -- --%beforetest(some) + -- --%beforetest(other) + -- --%test(some test with two before test procedures) + -- procedure some_test ... + -- when you'd like to have two beforetest procedures executed in a single test + type tt_annotation_texts is table of t_annotation_text index by t_annotation_position; + + type tt_procedure_annotations is table of tt_annotation_texts index by t_annotation_name; + + --holds information about + -- package level annotation + -- or + -- procedure and annotations associated with the procedure + type t_package_annotation is record( + text t_annotation_text, + name t_annotation_name, + procedure_name t_object_name, + procedure_annotations tt_procedure_annotations + ); + + --holds a list of package and procedure level annotations indexed (order) by position. + --procedure level annotations are grouped under procedure name + type tt_package_annotations is table of t_package_annotation index by t_annotation_position; + + --holds all annotations for object + type t_package_annotations_info is record( + owner t_object_name, + name t_object_name, + annotations tt_package_annotations + ); + + --list of all package level annotation positions for a given annotaion name + type tt_package_annot_positions is table of boolean index by t_annotation_position; + --index used to lookup package level annotations by package-level annotation name + type tt_annotations_index is table of tt_package_annot_positions index by t_annotation_name; + + + ----------------------------------------------- + --- Conversion of annotations for processing + ----------------------------------------------- + function get_procedure_annotations(a_annotations ut_annotations, a_index in out nocopy binary_integer) return tt_procedure_annotations is + l_result tt_procedure_annotations; + l_index binary_integer := a_index; + l_annotation_pos binary_integer; + function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is + begin + return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); + end; + begin + loop + l_annotation_pos := a_annotations(a_index).position; + l_result(a_annotations(a_index).name)(l_annotation_pos) := a_annotations(a_index).text; + exit when is_last_annotation_for_proc(a_annotations, a_index); + a_index := a_annotations.next(a_index); + end loop; + return l_result; + end; - function create_suite(a_object ut_annotated_object) return ut_logical_suite is - l_is_suite boolean := false; - l_is_test boolean := false; - l_suite_disabled boolean := false; - l_test_disabled boolean := false; - l_suite_items ut_suite_items := ut_suite_items(); - l_suite_name varchar2(4000); + function convert_package_annotations(a_object ut_annotated_object) return t_package_annotations_info is + l_result t_package_annotations_info; + l_annotation_no binary_integer; + l_annotation_pos binary_integer; + begin + l_result.owner := a_object.object_owner; + l_result.name := a_object.object_name; + l_annotation_no := a_object.annotations.first; + while l_annotation_no is not null loop + l_annotation_pos := a_object.annotations(l_annotation_no).position; + if a_object.annotations(l_annotation_no).subobject_name is null then + l_result.annotations(l_annotation_pos).name := a_object.annotations(l_annotation_no).name; + l_result.annotations(l_annotation_pos).text := a_object.annotations(l_annotation_no).text; + else + l_result.annotations(l_annotation_pos).procedure_name := a_object.annotations(l_annotation_no).subobject_name; + l_result.annotations(l_annotation_pos).procedure_annotations := get_procedure_annotations(a_object.annotations, l_annotation_no); + end if; + l_annotation_no := a_object.annotations.next(l_annotation_no); + end loop; + return l_result; + end; - l_default_setup_proc varchar2(250 char); - l_default_teardown_proc varchar2(250 char); - l_suite_setup_proc varchar2(250 char); - l_suite_teardown_proc varchar2(250 char); - l_suite_path varchar2(4000 char); + function build_annotation_index(a_annotations tt_package_annotations ) return tt_annotations_index is + l_result tt_annotations_index; + l_annotation_pos binary_integer; + begin + l_annotation_pos := a_annotations.first; + while l_annotation_pos is not null loop + if a_annotations( l_annotation_pos ).name is not null then + l_result(a_annotations( l_annotation_pos ).name)( l_annotation_pos ) := true; + end if; + l_annotation_pos := a_annotations.next( l_annotation_pos ); + end loop; + return l_result; + end; - l_proc_name varchar2(250 char); + procedure delete_from_annotation_index( + a_index in out nocopy tt_annotations_index, + a_start_pos t_annotation_position, + a_end_pos t_annotation_position + ) is + l_idx t_annotation_name; + begin + l_idx := a_index.first; + while l_idx is not null loop + a_index( l_idx ).delete( a_start_pos, a_end_pos); + if a_index( l_idx ).count = 0 then + a_index.delete( l_idx ); + end if; + l_idx := a_index.next( l_idx ); + end loop; + end; - l_suite ut_logical_suite; - l_test ut_test; + ----------------------------------------------- + -- Processing annotations + ----------------------------------------------- + + procedure add_annotation_warning( + a_suite in out nocopy ut_suite_item, + a_annotation t_annotation_name, + a_message varchar2, + a_line_no binary_integer, + a_procedure_name t_object_name := null + ) is + l_object_name varchar2(1000); + begin + l_object_name := upper( a_suite.object_owner || '.' || a_suite.object_name ); + if a_procedure_name is not null then + l_object_name := l_object_name || upper( '.' || a_procedure_name ); + end if; + a_suite.put_warning( + replace(a_message,'%%%','"--%'||a_annotation||'"') || ' Annotation ignored.' + || chr( 10 ) || 'at "' || l_object_name || '", line ' || a_line_no + ); + end; - l_suite_rollback integer; + function get_rollback_type(a_rollback_type_name varchar2) return ut_utils.t_rollback_type is + l_rollback_type ut_utils.t_rollback_type; + begin + l_rollback_type := + case lower(a_rollback_type_name) + when 'manual' then ut_utils.gc_rollback_manual + when 'auto' then ut_utils.gc_rollback_auto + end; + return l_rollback_type; + end; - l_beforetest_procedure varchar2(250 char); - l_aftertest_procedure varchar2(250 char); + function build_exception_numbers_list(a_annotation_text in varchar2) return ut_integer_list is + l_throws_list ut_varchar2_list; + l_exception_number_list ut_integer_list := ut_integer_list(); + l_regexp_for_excep_nums varchar2(30) := '^-?[[:digit:]]{1,5}$'; + begin + /*the a_expected_error_codes is converted to a ut_varchar2_list after that is trimmed and filtered to left only valid exception numbers*/ + l_throws_list := ut_utils.string_to_table(a_annotation_text, ',', 'Y'); + l_throws_list := ut_utils.filter_list( ut_utils.trim_list_elements(l_throws_list), l_regexp_for_excep_nums); + l_exception_number_list.extend(l_throws_list.count); + for i in 1 .. l_throws_list.count loop + l_exception_number_list(i) := l_throws_list(i); + end loop; + return l_exception_number_list; + end; - l_expected_error_codes ut_integer_list; + procedure add_to_throws_numbers_list( + a_list in out nocopy ut_integer_list, + a_throws_ann_text tt_annotation_texts + ) is + l_annotation_pos binary_integer; + begin + a_list := ut_integer_list(); + l_annotation_pos := a_throws_ann_text.first; + while l_annotation_pos is not null loop + a_list := a_list multiset union build_exception_numbers_list( a_throws_ann_text(l_annotation_pos)); + l_annotation_pos := a_throws_ann_text.next(l_annotation_pos); + end loop; + end; - l_rollback_type integer; - l_displayname varchar2(4000); - function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is + procedure add_to_list( + a_executables in out nocopy ut_executables, + a_owner varchar2, + a_package_name varchar2, + a_procedure_name varchar2, + a_executable_type ut_utils.t_executable_type + ) is begin - return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); - end; + if a_executables is null then + a_executables := ut_executables(); + end if; + a_executables.extend; + a_executables(a_executables.last) := ut_executable(a_owner, a_package_name, a_procedure_name, a_executable_type); + end; - function build_exception_numbers_list(a_annotation_text in varchar2) return ut_integer_list is - l_throws_list ut_varchar2_list; - l_exception_number_list ut_integer_list := ut_integer_list(); - l_regexp_for_excep_nums varchar2(30) := '^-?[[:digit:]]{1,5}$'; + procedure add_all_to_list( + a_executables in out nocopy ut_executables, + a_owner varchar2, + a_package_name varchar2, + a_annotation_texts tt_annotation_texts, + a_event_name ut_utils.t_event_name + ) is + l_annotation_pos binary_integer; begin - /*the a_expected_error_codes is converted to a ut_varchar2_list after that is trimmed and filtered to left only valid exception numbers*/ - l_throws_list := ut_utils.string_to_table(a_annotation_text, ',', 'Y'); - l_throws_list := ut_utils.filter_list( ut_utils.trim_list_elements(l_throws_list), l_regexp_for_excep_nums); - l_exception_number_list.extend(l_throws_list.count); - for i in 1 .. l_throws_list.count loop - l_exception_number_list(i) := l_throws_list(i); + l_annotation_pos := a_annotation_texts.first; + while l_annotation_pos is not null loop + add_to_list(a_executables, a_owner, a_package_name, a_annotation_texts(l_annotation_pos), a_event_name ); + l_annotation_pos := a_annotation_texts.next( l_annotation_pos); end loop; - return l_exception_number_list; end; + + procedure warning_on_duplicate_annot( + a_suite in out nocopy ut_suite_item, + a_annotations tt_annotations_index, + a_for_annotation varchar2 + ) is + l_annotation_name t_annotation_name; + l_line_no binary_integer; begin - l_suite_rollback := ut_utils.gc_rollback_auto; - for i in 1 .. a_object.annotations.count loop - - if a_object.annotations(i).subobject_name is null then - - if a_object.annotations(i).name in ('suite','displayname') then - l_suite_name := a_object.annotations(i).text; - if a_object.annotations(i).name = 'suite' then - l_is_suite := true; - end if; - elsif a_object.annotations(i).name = 'disabled' then - l_suite_disabled := true; - elsif a_object.annotations(i).name = 'suitepath' and a_object.annotations(i).text is not null then - l_suite_path := a_object.annotations(i).text || '.' || lower(a_object.object_name); - elsif a_object.annotations(i).name = 'rollback' then - if lower(a_object.annotations(i).text) = 'manual' then - l_suite_rollback := ut_utils.gc_rollback_manual; - else - l_suite_rollback := ut_utils.gc_rollback_auto; - end if; - end if; + if a_annotations.exists(a_for_annotation) then + if a_annotations(a_for_annotation).count > 1 then + --start from second occurrence of annotation + l_line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); + while l_line_no is not null loop + add_annotation_warning( a_suite, a_for_annotation, 'Duplicate annotation %%%.', l_line_no ); + l_line_no := a_annotations(a_for_annotation).next( l_line_no ); + end loop; + end if; + end if; + end; - elsif l_is_suite then - l_proc_name := a_object.annotations(i).subobject_name; - - if a_object.annotations(i).name = 'beforeeach' and l_default_setup_proc is null then - l_default_setup_proc := l_proc_name; - elsif a_object.annotations(i).name = 'aftereach' and l_default_teardown_proc is null then - l_default_teardown_proc := l_proc_name; - elsif a_object.annotations(i).name = 'beforeall' and l_suite_setup_proc is null then - l_suite_setup_proc := l_proc_name; - elsif a_object.annotations(i).name = 'afterall' and l_suite_teardown_proc is null then - l_suite_teardown_proc := l_proc_name; - elsif a_object.annotations(i).name = 'disabled' then - l_test_disabled := true; - elsif a_object.annotations(i).name = 'beforetest' then - l_beforetest_procedure := a_object.annotations(i).text; - elsif a_object.annotations(i).name = 'aftertest' then - l_aftertest_procedure := a_object.annotations(i).text; - elsif a_object.annotations(i).name = 'throws' then - l_expected_error_codes := build_exception_numbers_list(a_object.annotations(i).text); - elsif a_object.annotations(i).name in ('displayname','test') then - l_displayname := a_object.annotations(i).text; - if a_object.annotations(i).name = 'test' then - l_is_test := true; - end if; - elsif a_object.annotations(i).name = 'rollback' then - if lower(a_object.annotations(i).text) = 'manual' then - l_rollback_type := ut_utils.gc_rollback_manual; - elsif lower(a_object.annotations(i).text) = 'auto' then - l_rollback_type := ut_utils.gc_rollback_auto; - end if; + procedure warning_on_duplicate_annot( + a_suite in out nocopy ut_suite_item, + a_procedure_name t_object_name, + a_annotations tt_procedure_annotations, + a_for_annotation varchar2 + ) is + l_annotation_name t_annotation_name; + l_line_no binary_integer; + begin + if a_annotations.exists(a_for_annotation) then + if a_annotations(a_for_annotation).count > 1 then + --start from second occurrence of annotation + l_line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); + while l_line_no is not null loop + add_annotation_warning( a_suite, a_for_annotation, 'Duplicate annotation %%%.', l_line_no, a_procedure_name ); + l_line_no := a_annotations(a_for_annotation).next( l_line_no ); + end loop; end if; + end if; + end; + + procedure warning_bad_annot_combination( + a_suite in out nocopy ut_suite_item, + a_procedure_name t_object_name, + a_proc_annotations tt_procedure_annotations, + a_for_annotation varchar2, + a_invalid_annotations ut_varchar2_list + ) is + l_annotation_name t_annotation_name; + l_warning varchar2(32767); + l_line_no binary_integer; + begin + l_annotation_name := a_proc_annotations.first; + while l_annotation_name is not null loop + if l_annotation_name member of a_invalid_annotations then + l_line_no := a_proc_annotations(l_annotation_name).first; + while l_line_no is not null loop + add_annotation_warning( + a_suite, l_annotation_name, 'Annotation %%% cannot be used with "--%'|| a_for_annotation || '".', + l_line_no, a_procedure_name + ); + l_line_no := a_proc_annotations(l_annotation_name).next(l_line_no); + end loop; + end if; + l_annotation_name := a_proc_annotations.next(l_annotation_name); + end loop; + end; - if l_is_test and is_last_annotation_for_proc(a_object.annotations, i) then - l_suite_items.extend; - l_suite_items(l_suite_items.last) := - ut_test(a_object_owner => a_object.object_owner - ,a_object_name => a_object.object_name - ,a_name => l_proc_name - ,a_description => l_displayname - ,a_rollback_type => coalesce(l_rollback_type, l_suite_rollback) - ,a_disabled_flag => l_test_disabled - ,a_before_test_proc_name => l_beforetest_procedure - ,a_after_test_proc_name => l_aftertest_procedure - ,a_expected_error_codes => l_expected_error_codes); - - l_is_test := false; - l_test_disabled := false; - l_aftertest_procedure := null; - l_beforetest_procedure := null; - l_rollback_type := null; - l_expected_error_codes := null; + procedure add_test( + a_suite in out nocopy ut_suite, + a_procedure_name varchar2, + a_annotations tt_procedure_annotations + ) is + l_test ut_test; + l_annotation_texts tt_annotation_texts; + l_annotation_pos binary_integer; + begin + l_test := ut_test(a_suite.object_owner, a_suite.object_name, a_procedure_name); + + if a_annotations.exists(gc_displayname) then + l_annotation_texts := a_annotations(gc_displayname); + --take the last definition if more than one was provided + l_test.description := l_annotation_texts(l_annotation_texts.first); + --TODO if more than one - warning + else + l_test.description := a_annotations(gc_test)(a_annotations(gc_test).first); + end if; + l_test.path := a_suite.path ||'.'||a_procedure_name; + + if a_annotations.exists(gc_rollback) then + l_annotation_texts := a_annotations(gc_rollback); + l_test.rollback_type := get_rollback_type(l_annotation_texts(l_annotation_texts.first)); + if l_test.rollback_type is null then + add_annotation_warning( + a_suite, gc_rollback, 'Annotation %%% must be provided with one of values: "auto" or "manual".', + l_annotation_texts.first, a_procedure_name + ); + end if; + end if; + + if a_annotations.exists(gc_beforetest) then + add_all_to_list( l_test.before_test_list, l_test.object_owner, l_test.object_name, a_annotations(gc_beforetest), ut_utils.gc_before_test ); + end if; + if a_annotations.exists(gc_aftertest) then + add_all_to_list( l_test.after_test_list, l_test.object_owner, l_test.object_name, a_annotations(gc_aftertest), ut_utils.gc_after_test ); + end if; + if a_annotations.exists(gc_throws) then + add_to_throws_numbers_list(l_test.expected_error_codes, a_annotations(gc_throws)); + end if; + l_test.disabled_flag := ut_utils.boolean_to_int(a_annotations.exists(gc_disabled)); + + a_suite.add_item(l_test); + end; + + procedure update_before_after_list( + a_suite in out nocopy ut_logical_suite, + a_before_each_list ut_executables, + a_after_each_list ut_executables + ) is + l_test ut_test; + l_context ut_logical_suite; + begin + if a_suite.items is not null then + for i in 1 .. a_suite.items.count loop + if a_suite.items(i) is of (ut_test) then + l_test := treat( a_suite.items(i) as ut_test); + l_test.before_each_list := coalesce(a_before_each_list,ut_executables()) multiset union all l_test.before_each_list; + l_test.after_each_list := l_test.after_each_list multiset union all coalesce(a_after_each_list,ut_executables()); + a_suite.items(i) := l_test; + elsif a_suite.items(i) is of (ut_logical_suite) then + l_context := treat(a_suite.items(i) as ut_logical_suite); + update_before_after_list(l_context, a_before_each_list, a_after_each_list); + a_suite.items(i) := l_context; end if; + end loop; + end if; + end; + procedure add_annotated_procedure( + a_procedure_name t_object_name, + a_proc_annotations tt_procedure_annotations, + a_suite in out nocopy ut_suite, + a_before_each_list in out nocopy ut_executables, + a_after_each_list in out nocopy ut_executables + ) is + begin + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_test); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_displayname); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_rollback); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_beforeall); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_beforeeach); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_afterall); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, gc_aftereach); + + if a_proc_annotations.exists(gc_test) then + add_test( a_suite, a_procedure_name, a_proc_annotations); + + warning_bad_annot_combination( + a_suite, a_procedure_name, a_proc_annotations, gc_test, + ut_varchar2_list(gc_beforeeach, gc_aftereach, gc_beforeall, gc_afterall) + ); + + else + if a_proc_annotations.exists(gc_beforeeach) then + add_to_list( a_before_each_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_before_each ); + --TODO add warning if annotation has text - text ignored + end if; + if a_proc_annotations.exists(gc_aftereach) then + add_to_list( a_after_each_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_after_each ); + --TODO add warning if annotation has text - text ignored + end if; + if a_proc_annotations.exists(gc_beforeall) then + add_to_list( a_suite.before_all_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_before_all ); + --TODO add warning if annotation has text - text ignored end if; + if a_proc_annotations.exists(gc_afterall) then + add_to_list( a_suite.after_all_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_after_all ); + --TODO add warning if annotation has text - text ignored + end if; + end if; + end; + + procedure add_annotated_procedures( + a_annotations tt_package_annotations, + a_suite in out nocopy ut_suite, + a_before_each_list out ut_executables, + a_after_each_list out ut_executables + ) is + l_position t_annotation_position; + begin + a_before_each_list := ut_executables(); + a_after_each_list := ut_executables(); + l_position := a_annotations.first; + while l_position is not null loop + if a_annotations(l_position).procedure_name is not null then + add_annotated_procedure( + a_annotations(l_position).procedure_name, + a_annotations(l_position).procedure_annotations, + a_suite, + a_before_each_list, + a_after_each_list + ); + end if; + l_position := a_annotations.next( l_position); end loop; + end; - if l_is_suite then - l_suite := ut_suite ( - a_object_owner => a_object.object_owner, - a_object_name => a_object.object_name, - a_name => a_object.object_name, --this could be different for sub-suite (context) - a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) - a_description => l_suite_name, - a_rollback_type => l_suite_rollback, - a_disabled_flag => l_suite_disabled, - a_before_all_proc_name => l_suite_setup_proc, - a_after_all_proc_name => l_suite_teardown_proc - ); - for i in 1 .. l_suite_items.count loop - l_test := treat(l_suite_items(i) as ut_test); - l_test.set_beforeeach(l_default_setup_proc); - l_test.set_aftereach(l_default_teardown_proc); - l_test.path := l_suite.path || '.' || l_test.name; - l_suite.add_item(l_test); + procedure populate_suite_contents( + a_suite in out nocopy ut_suite, + a_annotations tt_package_annotations, + a_package_ann_index tt_annotations_index, + a_context_name t_object_name := null + ) is + l_before_each_list ut_executables; + l_after_each_list ut_executables; + l_rollback_type ut_utils.t_rollback_type; + l_annotation_text varchar2(32767); + l_object_name t_object_name; + begin + if a_context_name is not null then + l_object_name := a_suite.object_name||'.'||a_context_name; + else + l_object_name := a_suite.object_name; + end if; + if a_package_ann_index.exists(gc_suitepath) then + l_annotation_text := trim(a_annotations(a_package_ann_index(gc_suitepath).first).text); + if l_annotation_text is not null then + if regexp_like(l_annotation_text,'^((\w|[$#])+\.)*(\w|[$#])+$') then + a_suite.path := l_annotation_text||'.'||l_object_name; + else + add_annotation_warning( + a_suite, gc_suitepath||'('||l_annotation_text||')', + 'Invalid path value in annotation %%%.', a_package_ann_index(gc_suitepath).first + ); + end if; + else + add_annotation_warning( + a_suite, gc_suitepath, '%%% annotation requires a non-empty parameter value.', + a_package_ann_index(gc_suitepath).first + ); + end if; + warning_on_duplicate_annot(a_suite, a_package_ann_index, gc_suitepath); + end if; + a_suite.path := lower(coalesce(a_suite.path, l_object_name)); + + if a_package_ann_index.exists(gc_displayname) then + l_annotation_text := trim(a_annotations(a_package_ann_index(gc_displayname).first).text); + if l_annotation_text is not null then + a_suite.description := l_annotation_text; + else + add_annotation_warning( + a_suite, gc_displayname, '%%% annotation requires a non-empty parameter value.', + a_package_ann_index(gc_displayname).first + ); + end if; + warning_on_duplicate_annot(a_suite, a_package_ann_index, gc_displayname); + end if; + + if a_package_ann_index.exists(gc_rollback) then + l_rollback_type := get_rollback_type(a_annotations(a_package_ann_index(gc_rollback).first).text); + if l_rollback_type is null then + add_annotation_warning( + a_suite, gc_rollback, '%%% annotation requires one of values as parameter: "auto" or "manual".', + a_package_ann_index(gc_rollback).first + ); + end if; + warning_on_duplicate_annot(a_suite, a_package_ann_index, gc_rollback); + end if; + + a_suite.disabled_flag := ut_utils.boolean_to_int(a_package_ann_index.exists(gc_disabled)); + + --process procedure annotations for suite + add_annotated_procedures(a_annotations, a_suite, l_before_each_list, l_after_each_list); + + a_suite.set_rollback_type(l_rollback_type); + update_before_after_list(a_suite, l_before_each_list, l_after_each_list); + end; + + + procedure add_suite_contexts( + a_suite in out nocopy ut_suite, + a_annotations in out nocopy tt_package_annotations, + a_package_ann_index in out nocopy tt_annotations_index + ) is + l_context_pos t_annotation_position; + l_end_context_pos t_annotation_position; + l_package_ann_index tt_annotations_index; + l_annotations tt_package_annotations; + l_suite ut_suite; + l_context_no binary_integer := 1; + + function get_endcontext_position( + a_context_ann_pos t_annotation_position, + a_package_ann_index in out nocopy tt_annotations_index + ) return t_annotation_position is + l_result t_annotation_position; + begin + if a_package_ann_index.exists(gc_endcontext) then + l_result := a_package_ann_index(gc_endcontext).first; + while l_result <= a_context_ann_pos loop + --remove invalid endcontext + delete_from_annotation_index(a_package_ann_index, l_result, l_result); + --remove the bad endcontext from index + l_result := a_package_ann_index(gc_endcontext).next(l_result); + end loop; + end if; + return l_result; + end; + + function get_annotations_in_context( + a_annotations tt_package_annotations, + a_context_pos t_annotation_position, + a_end_context_pos t_annotation_position + ) return tt_package_annotations is + l_annotations tt_package_annotations; + l_position t_annotation_position; + begin + l_position := a_context_pos; + while l_position is not null and l_position <= a_end_context_pos loop + l_annotations(l_position) := a_annotations(l_position); + l_position := a_annotations.next(l_position); end loop; + return l_annotations; + end; + + begin + if not a_package_ann_index.exists(gc_context) then + return; end if; + l_context_pos := a_package_ann_index(gc_context).first; + while l_context_pos is not null loop + l_end_context_pos := get_endcontext_position(l_context_pos, a_package_ann_index); + if l_end_context_pos is null then + exit; + end if; + + --create a sub-set of annotations to process as sub-suite (context) + l_annotations := get_annotations_in_context(a_annotations, l_context_pos, l_end_context_pos); + l_package_ann_index := build_annotation_index(l_annotations); + + l_suite := ut_suite(a_suite.object_owner, a_suite.object_name, gc_context||'_'||l_context_no); + + l_suite.description := l_annotations(l_package_ann_index(gc_context).first).text; + l_suite.description := l_annotations(l_context_pos).text; + warning_on_duplicate_annot( l_suite, l_package_ann_index, gc_suite ); + + populate_suite_contents( l_suite, l_annotations, l_package_ann_index, gc_context||'_'||l_context_no ); + + a_suite.add_item(l_suite); + + -- remove annotations within context after processing them + a_annotations.delete(l_context_pos, l_end_context_pos); + delete_from_annotation_index(a_package_ann_index, l_context_pos, l_end_context_pos); + + if a_package_ann_index.exists(gc_context) then + l_context_pos := a_package_ann_index(gc_context).next(l_context_pos); + else + l_context_pos := null; + end if; + l_context_no := l_context_no + 1; + end loop; + end; + + procedure warning_on_incomplete_context( + a_suite in out nocopy ut_suite, + a_annotations tt_package_annotations, + a_package_ann_index tt_annotations_index + ) 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_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_warning( + a_suite, gc_endcontext, 'Invalid annotation %%%. Cannot find preceding "--%context".', + l_annotation_pos + ); + l_annotation_pos := a_package_ann_index(gc_endcontext).next(l_annotation_pos); + end loop; + end if; + end; + + function create_suite( + a_package_annotations t_package_annotations_info + ) return ut_logical_suite is + l_annotations tt_package_annotations := a_package_annotations.annotations; + l_package_ann_index tt_annotations_index; + l_suite ut_suite; + begin + l_package_ann_index := build_annotation_index(l_annotations); + if l_package_ann_index.exists(gc_suite) then + --create an incomplete suite + l_suite := ut_suite(a_package_annotations.owner, a_package_annotations.name); + + l_suite.description := l_annotations(l_package_ann_index(gc_suite).first).text; + warning_on_duplicate_annot(l_suite, l_package_ann_index, gc_suite); + + add_suite_contexts( l_suite, l_annotations, l_package_ann_index ); + --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, l_package_ann_index ); + + populate_suite_contents( l_suite, l_annotations, l_package_ann_index ); + + end if; return l_suite; + end; + function create_suite(a_object ut_annotated_object) return ut_logical_suite is + begin + return create_suite( convert_package_annotations(a_object) ); end create_suite; function build_suites_hierarchy(a_suites_by_path tt_schema_suites) return tt_schema_suites is diff --git a/source/core/ut_suite_builder.pks b/source/core/ut_suite_builder.pks index 4de69a584..43ea29c85 100644 --- a/source/core/ut_suite_builder.pks +++ b/source/core/ut_suite_builder.pks @@ -20,7 +20,10 @@ create or replace package ut_suite_builder authid current_user is * Responsible for converting annotations into unit test suites */ + --table of ut_suites indexed by object name ( suite_paths('object_name') = ut_logical_suite type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); + + --table of suite paths indexed by object name ( suite_paths('object_name') = 'suitepath.to.object' type t_object_suite_path is table of varchar2(4000) index by varchar2(4000 char); type t_schema_suites_info is record ( diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 7bb445272..4a8927496 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -326,6 +326,11 @@ create or replace package body ut_suite_manager is l_schema := l_schema_paths.next(l_schema); end loop; + --propagate rollback type to suite items after organizing suites into hierarchy + for i in 1 .. l_objects_to_run.count loop + l_objects_to_run(i).set_rollback_type( l_objects_to_run(i).get_rollback_type() ); + end loop; + return l_objects_to_run; end configure_execution_by_path; diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 9192877eb..438d32c60 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -513,7 +513,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, if a_list is not null then l_trimmed_list := ut_varchar2_list(); l_index := a_list.first; - + while (l_index is not null) loop l_trimmed_list.extend; l_trimmed_list(l_trimmed_list.count) := regexp_replace(a_list(l_index), '(^['||a_regexp_to_trim||']*)|(['||a_regexp_to_trim||']*$)'); @@ -531,7 +531,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, if a_list is not null then l_filtered_list := ut_varchar2_list(); l_index := a_list.first; - + while (l_index is not null) loop if regexp_like(a_list(l_index), a_regexp_filter) then l_filtered_list.extend; @@ -540,7 +540,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, l_index := a_list.next(l_index); end loop; end if; - + return l_filtered_list; end; @@ -549,7 +549,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, l_sql varchar2(32767) := q'!select q'[!'||a_string||q'!]' as "!'||a_string||'" from dual'; begin if a_string is not null then - select extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement() + select extract(dbms_xmlgen.getxmltype(l_sql),'/*/*/*').getRootElement() into l_result from dual; else @@ -558,5 +558,98 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, return l_result; end; + function replace_multiline_comments(a_source clob) return clob is + l_result clob; + l_ml_comment_start binary_integer := 1; + l_comment_start binary_integer := 1; + l_text_start binary_integer := 1; + l_escaped_text_start binary_integer := 1; + l_escaped_text_end_char varchar2(1 char); + l_end binary_integer := 1; + l_ml_comment clob; + l_newlines_count binary_integer; + l_offset binary_integer := 1; + l_length binary_integer := coalesce(dbms_lob.getlength(a_source), 0); + function is_before(a_x binary_integer, a_y binary_integer) return boolean is + begin + return a_x < a_y or a_y = 0; + end; + begin + l_ml_comment_start := instr(a_source,'/*'); + l_comment_start := instr(a_source,'--'); + l_text_start := instr(a_source,''''); + l_escaped_text_start := instr(a_source,q'[q']'); + while l_offset > 0 and l_ml_comment_start > 0 loop + + if l_ml_comment_start > 0 and (l_ml_comment_start < l_comment_start or l_comment_start = 0) + and (l_ml_comment_start < l_text_start or l_text_start = 0)and (l_ml_comment_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,'*/',l_ml_comment_start+2); + append_to_clob(l_result, dbms_lob.substr(a_source, l_ml_comment_start-l_offset, l_offset)); + if l_end > 0 then + l_ml_comment := substr(a_source, l_ml_comment_start, l_end-l_ml_comment_start); + l_newlines_count := length( l_ml_comment ) - length( translate( l_ml_comment, 'a'||chr(10), 'a') ); + if l_newlines_count > 0 then + append_to_clob(l_result, lpad( chr(10), l_newlines_count, chr(10) ) ); + end if; + l_end := l_end + 2; + end if; + else + + if l_comment_start > 0 and (l_comment_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_comment_start < l_text_start or l_text_start = 0) and (l_comment_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,chr(10),l_comment_start+2); + if l_end > 0 then + l_end := l_end + 1; + end if; + elsif l_text_start > 0 and (l_text_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_text_start < l_comment_start or l_comment_start = 0) and (l_text_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,q'[']',l_text_start+1); + + --skip double quotes while searching for end of quoted text + while l_end > 0 and l_end = instr(a_source,q'['']',l_text_start+1) loop + l_end := instr(a_source,q'[']',l_end+1); + end loop; + if l_end > 0 then + l_end := l_end + 1; + end if; + + elsif l_escaped_text_start > 0 and (l_escaped_text_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_escaped_text_start < l_comment_start or l_comment_start = 0) and (l_escaped_text_start < l_text_start or l_text_start = 0) + then + --translate char "[" from the start of quoted text "q'[someting]'" into "]" + l_escaped_text_end_char := translate( substr(a_source, l_escaped_text_start + 2, 1), '[{(<', ']})>'); + l_end := instr(a_source,l_escaped_text_end_char||'''',l_escaped_text_start + 3 ); + if l_end > 0 then + l_end := l_end + 2; + end if; + end if; + + if l_end = 0 then + append_to_clob(l_result, substr(a_source, l_offset, l_length-l_offset)); + else + append_to_clob(l_result, substr(a_source, l_offset, l_end-l_offset)); + end if; + end if; + l_offset := l_end; + if l_offset >= l_ml_comment_start then + l_ml_comment_start := instr(a_source,'/*',l_offset); + end if; + if l_offset >= l_comment_start then + l_comment_start := instr(a_source,'--',l_offset); + end if; + if l_offset >= l_text_start then + l_text_start := instr(a_source,'''',l_offset); + end if; + if l_offset >= l_escaped_text_start then + l_escaped_text_start := instr(a_source,q'[q']',l_offset); + end if; + end loop; + append_to_clob(l_result, substr(a_source, l_end)); + return l_result; + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 0bb66a4d7..84cbfcdef 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -24,23 +24,42 @@ create or replace package ut_utils authid definer is gc_version constant varchar2(50) := 'v3.1.0.1841-develop'; /* Constants: Event names */ - gc_run constant varchar2(12) := 'run'; - gc_suite constant varchar2(12) := 'suite'; - gc_before_all constant varchar2(12) := 'before_all'; - gc_before_each constant varchar2(12) := 'before_each'; - gc_before_test constant varchar2(12) := 'before_test'; - gc_test constant varchar2(12) := 'test'; - gc_test_execute constant varchar2(12) := 'test_execute'; - gc_after_test constant varchar2(10) := 'after_test'; - gc_after_each constant varchar2(12) := 'after_each'; - gc_after_all constant varchar2(12) := 'after_all'; - gc_finalize constant varchar2(12) := 'finalize'; + subtype t_event_name is varchar2(30); + gc_before_run constant t_event_name := 'before_run'; + gc_before_suite constant t_event_name := 'before_suite'; + gc_before_before_all constant t_event_name := 'before_before_all'; + gc_before_before_each constant t_event_name := 'before_before_each'; + gc_before_before_test constant t_event_name := 'before_before_test'; + gc_before_test_execute constant t_event_name := 'before_test_execute'; + gc_before_after_test constant t_event_name := 'before_after_test'; + gc_before_after_each constant t_event_name := 'before_after_each'; + gc_before_after_all constant t_event_name := 'before_after_all'; + gc_after_run constant t_event_name := 'after_run'; + gc_after_suite constant t_event_name := 'after_suite'; + gc_after_before_all constant t_event_name := 'after_before_all'; + gc_after_before_each constant t_event_name := 'after_before_each'; + gc_after_before_test constant t_event_name := 'after_before_test'; + gc_after_test_execute constant t_event_name := 'after_test_execute'; + gc_after_after_test constant t_event_name := 'after_after_test'; + gc_after_after_each constant t_event_name := 'after_after_each'; + gc_after_after_all constant t_event_name := 'after_after_all'; + gc_finalize constant t_event_name := 'finalize'; + + subtype t_executable_type is varchar2(30); + gc_before_all constant t_executable_type := 'before_all'; + gc_before_each constant t_executable_type := 'before_each'; + gc_before_test constant t_executable_type := 'before_test'; + gc_test_execute constant t_executable_type := 'test_execute'; + gc_after_test constant t_executable_type := 'after_test'; + gc_after_each constant t_executable_type := 'after_each'; + gc_after_all constant t_executable_type := 'after_all'; /* Constants: Test Results */ - gc_disabled constant number(1) := 0; -- test/suite was disabled - gc_success constant number(1) := 1; -- test passed - gc_failure constant number(1) := 2; -- one or more expectations failed - gc_error constant number(1) := 3; -- exception was raised + subtype t_test_result is binary_integer range 0 .. 3; + gc_disabled constant t_test_result := 0; -- test/suite was disabled + gc_success constant t_test_result := 1; -- test passed + gc_failure constant t_test_result := 2; -- one or more expectations failed + gc_error constant t_test_result := 3; -- exception was raised gc_disabled_char constant varchar2(8) := 'Disabled'; -- test/suite was disabled gc_success_char constant varchar2(7) := 'Success'; -- test passed @@ -50,9 +69,10 @@ create or replace package ut_utils authid definer is /* Constants: Rollback type for ut_test_object */ - gc_rollback_auto constant number(1) := 0; -- rollback after each test and suite - gc_rollback_manual constant number(1) := 1; -- leave transaction control manual - --gc_rollback_on_error constant number(1) := 2; -- rollback tests only on error + subtype t_rollback_type is binary_integer range 0 .. 1; + gc_rollback_auto constant t_rollback_type := 0; -- rollback after each test and suite + gc_rollback_manual constant t_rollback_type := 1; -- leave transaction control manual + gc_rollback_default constant t_rollback_type := gc_rollback_auto; ex_unsupported_rollback_type exception; gc_unsupported_rollback_type constant pls_integer := -20200; @@ -305,5 +325,10 @@ create or replace package ut_utils authid definer is -- Generates XMLGEN escaped string function xmlgen_escaped_string(a_string in varchar2) return varchar2; + /** + * Replaces multi-line comments in given source-code with empty lines + */ + function replace_multiline_comments(a_source clob) return clob; + end ut_utils; / diff --git a/source/expectations/data_values/ut_data_value_anydata.tpb b/source/expectations/data_values/ut_data_value_anydata.tpb index 599cd9bb8..be91c2d4a 100644 --- a/source/expectations/data_values/ut_data_value_anydata.tpb +++ b/source/expectations/data_values/ut_data_value_anydata.tpb @@ -37,8 +37,8 @@ create or replace type body ut_data_value_anydata as self.is_data_null := 1; end if; if not self.is_null() then - open l_query for select a_value val from dual; ut_expectation_processor.set_xml_nls_params(); + open l_query for select a_value val from dual; l_ctx := sys.dbms_xmlgen.newcontext( l_query ); dbms_xmlgen.setrowtag(l_ctx, ''); dbms_xmlgen.setrowsettag(l_ctx, ''); diff --git a/source/install.sql b/source/install.sql index 94ebe862f..4054086a6 100644 --- a/source/install.sql +++ b/source/install.sql @@ -50,15 +50,20 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'reporters/ut_ansiconsole_helper.pks' @@install_component.sql 'reporters/ut_ansiconsole_helper.pkb' +--event manager objects +@@install_component.sql 'core/events/ut_event_item.tps' +@@install_component.sql 'core/events/ut_event_listener.tps' +@@install_component.sql 'core/events/ut_event_manager.pks' +@@install_component.sql 'core/events/ut_event_manager.pkb' + --core types @@install_component.sql 'core/types/ut_expectation_result.tps' @@install_component.sql 'core/types/ut_expectation_results.tps' @@install_component.sql 'core/types/ut_results_counter.tps' -@@install_component.sql 'core/types/ut_suite_item_base.tps' -@@install_component.sql 'core/types/ut_event_listener_base.tps' @@install_component.sql 'core/types/ut_suite_item.tps' @@install_component.sql 'core/types/ut_suite_items.tps' @@install_component.sql 'core/types/ut_executable.tps' +@@install_component.sql 'core/types/ut_executables.tps' @@install_component.sql 'core/types/ut_executable_test.tps' @@install_component.sql 'core/types/ut_test.tps' @@install_component.sql 'core/types/ut_logical_suite.tps' @@ -69,7 +74,6 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'core/types/ut_run.tps' @@install_component.sql 'core/types/ut_reporter_base.tps' @@install_component.sql 'core/types/ut_reporters.tps' -@@install_component.sql 'core/types/ut_event_listener.tps' --output buffer base api @@install_component.sql 'core/output_buffers/ut_output_buffer_base.tps' @@ -148,7 +152,6 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'core/types/ut_logical_suite.tpb' @@install_component.sql 'core/types/ut_suite.tpb' @@install_component.sql 'core/types/ut_run.tpb' -@@install_component.sql 'core/types/ut_event_listener.tpb' @@install_component.sql 'core/types/ut_expectation_result.tpb' @@install_component.sql 'core/types/ut_reporter_base.tpb' @@install_component.sql 'core/types/ut_output_reporter_base.tpb' diff --git a/source/reporters/ut_documentation_reporter.tpb b/source/reporters/ut_documentation_reporter.tpb index 41cd24e11..90a06634e 100644 --- a/source/reporters/ut_documentation_reporter.tpb +++ b/source/reporters/ut_documentation_reporter.tpb @@ -65,14 +65,18 @@ create or replace type body ut_documentation_reporter is self.print_clob(a_test.get_serveroutputs); end; - overriding member procedure after_calling_before_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is + overriding member procedure after_calling_before_all(self in out nocopy ut_documentation_reporter, a_executable in ut_executable) is begin - self.print_clob(treat(a_suite as ut_suite).before_all.serveroutput); + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_clob(a_executable.serveroutput); + end if; end; - overriding member procedure after_calling_after_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is + overriding member procedure after_calling_after_all(self in out nocopy ut_documentation_reporter, a_executable in ut_executable) is begin - self.print_clob(treat(a_suite as ut_suite).after_all.serveroutput); + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_clob(a_executable.serveroutput); + end if; end; overriding member procedure after_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite) as diff --git a/source/reporters/ut_documentation_reporter.tps b/source/reporters/ut_documentation_reporter.tps index 152544452..6048eb970 100644 --- a/source/reporters/ut_documentation_reporter.tps +++ b/source/reporters/ut_documentation_reporter.tps @@ -23,8 +23,8 @@ create or replace type ut_documentation_reporter under ut_console_reporter_base( overriding member procedure print_text(self in out nocopy ut_documentation_reporter, a_text varchar2), overriding member procedure before_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite), overriding member procedure after_calling_test(self in out nocopy ut_documentation_reporter, a_test ut_test), - overriding member procedure after_calling_after_all (self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite), - overriding member procedure after_calling_before_all (self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite), + overriding member procedure after_calling_after_all (self in out nocopy ut_documentation_reporter, a_executable in ut_executable), + overriding member procedure after_calling_before_all (self in out nocopy ut_documentation_reporter, a_executable in ut_executable), overriding member procedure after_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite), overriding member procedure after_calling_run(self in out nocopy ut_documentation_reporter, a_run in ut_run), diff --git a/source/reporters/ut_junit_reporter.tpb b/source/reporters/ut_junit_reporter.tpb index 5d2fd54df..e1a6d7dca 100644 --- a/source/reporters/ut_junit_reporter.tpb +++ b/source/reporters/ut_junit_reporter.tpb @@ -77,15 +77,7 @@ create or replace type body ut_junit_reporter is else self.print_text(''); end if; - if a_test.before_test.get_error_stack_trace() is not null or a_test.after_test.get_error_stack_trace() is not null then - self.print_text(''); - self.print_text(c_cddata_tag_start); - self.print_text(trim(a_test.before_test.get_error_stack_trace()) || trim(chr(10) || chr(10) || a_test.after_test.get_error_stack_trace())); - self.print_text(c_cddata_tag_end); - self.print_text(''); - else - self.print_text(''); - end if; + self.print_text(''); self.print_text(''); end; @@ -94,6 +86,8 @@ create or replace type body ut_junit_reporter is a_suite.results_count.failure_count + a_suite.results_count.errored_count; l_suite ut_suite; l_tests ut_suite_items := ut_suite_items(); + l_data clob; + l_errors ut_varchar2_list; begin a_suite_id := a_suite_id + 1; self.print_text(' - - - + %'); end; diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb new file mode 100644 index 000000000..654143349 --- /dev/null +++ b/test/core/test_suite_builder.pkb @@ -0,0 +1,709 @@ +create or replace package body test_suite_builder is + + function invoke_builder_for_annotations( + a_annotations ut3.ut_annotations, + a_package_name varchar2 := 'TEST_SUITE_BUILDER_PACKAGE' + ) return clob is + l_suites ut3.ut_suite_builder.tt_schema_suites; + l_suite ut3.ut_logical_suite; + l_cursor sys_refcursor; + l_xml xmltype; + begin + open l_cursor for select value(x) from table( + ut3.ut_annotated_objects( + ut3.ut_annotated_object('UT3_TESTER', a_package_name, 'PACKAGE', a_annotations) + ) ) x; + l_suites := ut3.ut_suite_builder.build_suites(l_cursor).schema_suites; + l_suite := l_suites(l_suites.first); + + select deletexml( + xmltype(l_suite), + '//RESULTS_COUNT|//START_TIME|//END_TIME|//RESULT|//ASSOCIATED_EVENT_NAME' || + '|//TRANSACTION_INVALIDATORS|//ERROR_BACKTRACE|//ERROR_STACK|//SERVEROUTPUT' + ) + into l_xml + from dual; + + return l_xml.getClobVal(); + end; + + procedure no_suite_description is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_match( + 'UT3_TESTERsome_packagesome_package()?\s*some_package' + ); + end; + + procedure suite_description_from_suite is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Some description', null), + ut3.ut_annotation(2, 'suite','Another description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%some_packageSome description%' + ); + end; + + procedure suitepath_from_non_empty_path is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'suitepath','org.utplsql.some', null), + ut3.ut_annotation(3, 'suitepath','dummy.utplsql.some', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%org.utplsql.some%' + ); + end; + + procedure suite_descr_from_displayname is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Some description', null), + ut3.ut_annotation(2, 'suite','Another description', null), + ut3.ut_annotation(3, 'displayname','New description', null), + ut3.ut_annotation(4, 'displayname','Newest description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%some_packageNew description%' + ); + end; + + procedure rollback_type_valid is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'rollback','manual', null), + ut3.ut_annotation(3, 'rollback','bad', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%'||ut3.ut_utils.gc_rollback_manual||'%' + ); + end; + + procedure rollback_type_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'rollback','manual', null), + ut3.ut_annotation(3, 'rollback','bad', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback"%%UT3_TESTER.SOME_PACKAGE%3%%' + ); + end; + + procedure suite_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'suite','bad', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%suite"%UT3_TESTER.SOME_PACKAGE%line 8%%' + ); + end; + + procedure test_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'test','Dup', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%test"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure beforeall_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'beforeall', null, 'test_procedure'), + ut3.ut_annotation(9, 'beforeall', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%beforeall"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure beforeeach_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'beforeeach', null, 'test_procedure'), + ut3.ut_annotation(9, 'beforeeach', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%beforeeach"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure afterall_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'afterall', null, 'test_procedure'), + ut3.ut_annotation(9, 'afterall', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%afterall"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure aftereach_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'aftereach', null, 'test_procedure'), + ut3.ut_annotation(9, 'aftereach', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%aftereach"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure suitepath_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(3, 'suitepath','dummy.utplsql.some', null), + ut3.ut_annotation(4, 'suitepath','org.utplsql.some', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%suitepath"%line 4%%' + ); + end; + + procedure displayname_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(4, 'displayname','New description', null), + ut3.ut_annotation(5, 'displayname','Newest description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%displayname"%line 5%%' + ); + end; + + procedure suitepath_annot_empty 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, 'suitepath',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%suitepath" annotation requires a non-empty parameter value.%%' + ); + end; + + procedure suitepath_annot_invalid_path is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'suitepath','path with spaces', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%Invalid path value in annotation "--%suitepath(path with spaces)"%%' + ); + end; + + procedure displayname_annot_empty 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, 'displayname',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%displayname" annotation requires a non-empty parameter value.%%' + ); + end; + + procedure rollback_type_empty 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, 'rollback',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback" annotation requires one of values as parameter:%%' + ); + end; + + procedure rollback_type_invalid is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'rollback','bad', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback" annotation requires one of values as parameter: "auto" or "manual". Annotation ignored.%%' + ); + end; + + procedure multiple_before_after 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, 'first_before_all'), + ut3.ut_annotation(3, 'beforeall',null, 'another_before_all'), + ut3.ut_annotation(4, 'beforeeach',null, 'first_bfore_each'), + ut3.ut_annotation(5, 'beforeeach',null, 'another_before_each'), + ut3.ut_annotation(6, 'aftereach',null, 'first_after_each'), + ut3.ut_annotation(7, 'aftereach',null, 'another_after_each'), + ut3.ut_annotation(8, 'afterall',null, 'first_after_all'), + ut3.ut_annotation(9, 'afterall',null, 'another_after_all'), + ut3.ut_annotation(14, 'test','A test', 'some_test'), + ut3.ut_annotation(15, 'beforetest','before_test_proc', 'some_test'), + ut3.ut_annotation(16, 'beforetest','before_test_proc2', 'some_test'), + ut3.ut_annotation(18, 'aftertest','after_test_proc', 'some_test'), + ut3.ut_annotation(20, 'aftertest','after_test_proc2', 'some_test') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%some_package%some_test' || + '%' || + '%some_packagefirst_bfore_each' || + '%some_packageanother_before_each' || + '%' || + '%' || + '%some_packagebefore_test_proc' || + '%some_packagebefore_test_proc2' || + '%' || + '%' || + '%some_packageafter_test_proc' || + '%some_packageafter_test_proc2' || + '%' || + '%' || + '%some_packagefirst_after_each' || + '%some_packageanother_after_each' || + '%' || + '%' || + '%' || + '%some_packagefirst_before_all' || + '%some_packageanother_before_all' || + '%' || + '%' || + '%some_packagefirst_after_all' || + '%some_packageanother_after_all' || + '%%' + ); + end; + + procedure before_after_on_single_proc 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, 'do_stuff'), + ut3.ut_annotation(3, 'beforeeach',null, 'do_stuff'), + ut3.ut_annotation(4, 'aftereach',null, 'do_stuff'), + ut3.ut_annotation(5, 'afterall',null, 'do_stuff'), + ut3.ut_annotation(6, 'test','A test', 'some_test') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%some_package%some_test' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%%' + ); + end; + + procedure before_after_mixed_with_test 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, 'do_stuff'), + ut3.ut_annotation(3, 'beforeeach',null, 'do_stuff'), + ut3.ut_annotation(4, 'aftereach',null, 'do_stuff'), + ut3.ut_annotation(5, 'afterall',null, 'do_stuff'), + ut3.ut_annotation(6, 'test','A test', 'do_stuff') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeall"%line 2%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeeach"%line 3%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%aftereach"%line 4%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%afterall" cannot be used with "--\%test". Annotation ignored.' + ||'%at "UT3_TESTER.SOME_PACKAGE.DO_STUFF", line 5%%', '\'); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); + end; + + procedure suite_from_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(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(7, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(8, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%' || + '%' || + '' || + '%context_1A contextsome_package.context_1' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.context_1.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; + begin + --Arrange + 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(4, 'beforeall',null, 'context_beforeall'), + ut3.ut_annotation(5, 'beforeeach',null, 'context_beforeeach'), + ut3.ut_annotation(6, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(7, 'aftereach',null, 'context_aftereach'), + ut3.ut_annotation(8, 'afterall',null, 'context_afterall'), + ut3.ut_annotation(9, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '' || + '%' || + '%' || + '%context_1' || + '%' || + '%' || + '%test_in_a_context' || + '%%context_beforeeach%' || + '%%test_in_a_context%' || + '%%context_aftereach%' || + '%' || + '%' || + '%%context_beforeall%' || + '%%context_afterall%' || + '%' || + '%' || + '%suite_level_test' || + '%%suite_level_test%' || + '%' || + '%' || + '%' + ); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + end; + + procedure before_after_out_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(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(6, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(7, 'endcontext',null, null), + ut3.ut_annotation(8, 'aftereach',null, 'suite_level_aftereach'), + ut3.ut_annotation(9, 'afterall',null, 'suite_level_afterall') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '' || + '%' || + '%' || + '%context_1' || + '%' || + '%' || + '%test_in_a_context' || + '%%suite_level_beforeeach%' || + '%%test_in_a_context%' || + '%%suite_level_aftereach%' || + '%' || + '%' || + '%' || + '%' || + '%suite_level_test' || + '%%suite_level_beforeeach%' || + '%%suite_level_test%' || + '%%suite_level_aftereach%' || + '%' || + '%' || + '%%suite_level_beforeall%' || + '%%suite_level_afterall%' || + '%' + ); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + end; + + procedure context_without_endcontext 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, '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 "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' || + '%' || + '' || + '' + ); + end; + + procedure endcontext_without_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(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(7, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(8, 'endcontext',null, null), + ut3.ut_annotation(9, 'endcontext',null, null) + ); + --Act + 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 "UT3_TESTER.SOME_PACKAGE", line 9%' + ,'\' + ); + ut.expect(l_actual).to_be_like( + '' || + '%' || + '' || + '%context_1A contextsome_package.context_1' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.context_1.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagecontext_setup' || + '%' || + '' || + '' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '' || + '%some_packagesuite_level_beforeall' || + '%' || + '' || + '' + ); + end; + +end; +/ diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks new file mode 100644 index 000000000..d8bf40164 --- /dev/null +++ b/test/core/test_suite_builder.pks @@ -0,0 +1,87 @@ +create or replace package test_suite_builder is + --%suite(suite_builder) + --%suitepath(utplsql.core) + + --%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 path using first --%suitepath annotation) + procedure suitepath_from_non_empty_path; + + --%test(Overrides suite description using first --%displayname annotation) + procedure suite_descr_from_displayname; + + --%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 --%suite annotation used) + procedure suite_annot_duplicated; + + --%test(Gives warning if more than one --%test annotation used) + procedure test_annot_duplicated; + + --%test(Gives warning if more than one --%beforeall annotation used) + procedure beforeall_annot_duplicated; + + --%test(Gives warning if more than one --%beforeeach annotation used) + procedure beforeeach_annot_duplicated; + + --%test(Gives warning if more than one --%afterall annotation used) + procedure afterall_annot_duplicated; + + --%test(Gives warning if more than one --%aftereach annotation used) + procedure aftereach_annot_duplicated; + + --%test(Gives warning if more than one --%suitepath annotation used) + procedure suitepath_annot_duplicated; + + --%test(Gives warning if more than one --%displayname annotation used) + procedure displayname_annot_duplicated; + + --%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 --%displayname annotation has no value) + procedure displayname_annot_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(Supports multiple before/after definitions) + procedure multiple_before_after; + + --%test(Supports before/after all/each annotations on single procedure) + procedure before_after_on_single_proc; + + --%test(Gives warning on before/after all/each annotations mixed with test) + procedure before_after_mixed_with_test; + + --%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(Propagates beforeeach/aftereach to context) + procedure before_after_out_of_context; + + --%test(Does not create context and gives warning when endcontext is missing) + procedure context_without_endcontext; + + --%test(Gives warning if --%endcontext is missing a preceding --%context) + procedure endcontext_without_context; + +end; +/ diff --git a/test/core/test_suite_manager.pkb b/test/core/test_suite_manager.pkb index 28ddecde9..827812f98 100644 --- a/test/core/test_suite_manager.pkb +++ b/test/core/test_suite_manager.pkb @@ -3,6 +3,256 @@ create or replace package body test_suite_manager is ex_obj_doesnt_exist exception; pragma exception_init(ex_obj_doesnt_exist, -04043); + procedure compile_dummy_packages is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package test_package_1 is + + --%suite + --%displayname(test_package_1) + --%suitepath(tests) + --%rollback(manual) + + gv_glob_val number; + + --%beforeeach + procedure global_setup; + + --%aftereach + procedure global_teardown; + + --%test + --%displayname(Test1 from test package 1) + procedure test1; + + --%test(Test2 from test package 1) + --%beforetest(test2_setup) + --%aftertest(test2_teardown) + procedure test2; + + procedure test2_setup; + + procedure test2_teardown; + +end test_package_1;]'; + + execute immediate q'[create or replace package body test_package_1 is + gv_var_1 number; + gv_var_1_temp number; + + procedure global_setup is + begin + gv_var_1 := 1; + gv_glob_val := 1; + end; + + procedure global_teardown is + begin + gv_var_1 := 0; + gv_glob_val := 0; + end; + + procedure test1 is + begin + ut.expect(gv_var_1, 'Some expectation').to_equal(1); + end; + + procedure test2 is + begin + ut.expect(gv_var_1, 'Some expectation').to_equal(2); + end; + + procedure test2_setup is + begin + gv_var_1_temp := gv_var_1; + gv_var_1 := 2; + end; + + procedure test2_teardown is + begin + gv_var_1 := gv_var_1_temp; + gv_var_1_temp := null; + end; + +end test_package_1;]'; + + execute immediate q'[create or replace package test_package_2 is + --%suite + --%suitepath(tests.test_package_1) + + gv_glob_val varchar2(1); + + --%beforeeach + procedure global_setup; + + --%aftereach + procedure global_teardown; + + --%test + procedure test1; + + --%test + --%beforetest(test2_setup) + --%aftertest(test2_teardown) + procedure test2; + + procedure test2_setup; + + procedure test2_teardown; + + --%beforeall + procedure context_setup; + + --%test(Test in a context) + procedure context_test; + + --%afterall + procedure context_teardown; + +end test_package_2;]'; + + execute immediate q'[create or replace package body test_package_2 is + gv_var_1 varchar2(1); + gv_var_1_temp varchar2(1); + + procedure global_setup is + begin + gv_var_1 := 'a'; + gv_glob_val := 'z'; + end; + + procedure global_teardown is + begin + gv_var_1 := 'n'; + gv_glob_val := 'n'; + end; + + procedure test1 is + begin + ut.expect(gv_var_1).to_equal('a'); + end; + + procedure test2 is + begin + ut.expect(gv_var_1).to_equal('b'); + end; + + procedure test2_setup is + begin + gv_var_1_temp := gv_var_1; + gv_var_1 := 'b'; + end; + + procedure test2_teardown is + begin + gv_var_1 := gv_var_1_temp; + gv_var_1_temp := null; + end; + + procedure context_setup is + begin + gv_var_1_temp := gv_var_1 || 'a'; + end; + + procedure context_test is + begin + ut.expect(gv_var_1_temp, 'Some expectation').to_equal('na'); + end; + + procedure context_teardown is + begin + gv_var_1_temp := null; + end; + +end test_package_2;]'; + + execute immediate q'[create or replace package test_package_3 is + --%suite + --%suitepath(tests2) + --%rollback(auto) + + gv_glob_val number; + + --%beforeeach + procedure global_setup; + + --%aftereach + procedure global_teardown; + + --%test + --%rollback(auto) + procedure test1; + + --%test + --%beforetest(test2_setup) + --%aftertest(test2_teardown) + procedure test2; + + procedure test2_setup; + + procedure test2_teardown; + + --%test + --%disabled + procedure disabled_test; + +end test_package_3;]'; + + execute immediate q'[create or replace package body test_package_3 is + gv_var_1 number; + gv_var_1_temp number; + + procedure global_setup is + begin + gv_var_1 := 1; + gv_glob_val := 1; + end; + + procedure global_teardown is + begin + gv_var_1 := 0; + gv_glob_val := 0; + end; + + procedure test1 is + begin + ut.expect(gv_var_1).to_equal(1); + end; + + procedure test2 is + begin + ut.expect(gv_var_1).to_equal(2); + end; + + procedure test2_setup is + begin + gv_var_1_temp := gv_var_1; + gv_var_1 := 2; + end; + + procedure test2_teardown is + begin + gv_var_1 := gv_var_1_temp; + gv_var_1_temp := null; + end; + + procedure disabled_test is + begin + null; + end; + +end test_package_3;]'; + end; + + + procedure drop_dummy_packages is + pragma autonomous_transaction; + begin + execute immediate 'drop package test_package_1'; + execute immediate 'drop package test_package_2'; + execute immediate 'drop package test_package_3'; + end; + procedure test_schema_run is c_path constant varchar2(100) := USER; l_objects_to_run ut3.ut_suite_items := ut3.ut_suite_items(); @@ -35,10 +285,12 @@ create or replace package body test_suite_manager is when 'tests' then ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(3); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); when 'tests2' then ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); @@ -69,10 +321,12 @@ create or replace package body test_suite_manager is ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_top2_bt_name_cur_user is @@ -96,10 +350,12 @@ create or replace package body test_suite_manager is ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_by_path_to_subsuite is @@ -126,7 +382,7 @@ create or replace package body test_suite_manager is l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_by_path_to_subsuite_cu is @@ -153,7 +409,7 @@ create or replace package body test_suite_manager is l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_subsute_proc_by_path is @@ -177,16 +433,19 @@ create or replace package body test_suite_manager is l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test1_suite.name).to_equal('test_package_1'); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test2_suite.items.count).to_equal(1); l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; @@ -219,8 +478,8 @@ create or replace package body test_suite_manager is l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; procedure test_top_pack_by_name is @@ -247,24 +506,24 @@ create or replace package body test_suite_manager is ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).DISABLED_FLAG).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test_list.count).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test_list.count).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).disabled_flag).to_equal(0); ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).DISABLED_FLAG).to_equal(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).disabled_flag).to_equal(0); -- temporary behavior. -- decided that when executed by package, not path, only that package has to execute l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_top_pack_by_name_cu is @@ -291,24 +550,24 @@ create or replace package body test_suite_manager is ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).DISABLED_FLAG).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test_list.count).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test_list.count).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).disabled_flag).to_equal(0); ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).DISABLED_FLAG).to_equal(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each_list.count).to_be_greater_than(0); + ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).disabled_flag).to_equal(0); -- temporary behavior. -- decided that when executed by package, not path, only that package has to execute l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_top_pack_by_path is @@ -335,7 +594,7 @@ create or replace package body test_suite_manager is l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_top_pack_by_path_cu is @@ -362,7 +621,7 @@ create or replace package body test_suite_manager is l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_logical_suite); ut.expect(l_test2_suite.name).to_equal('test_package_2'); - ut.expect(l_test2_suite.items.count).to_equal(2); + ut.expect(l_test2_suite.items.count).to_equal(3); end; procedure test_top_pck_proc_by_path is @@ -391,8 +650,8 @@ create or replace package body test_suite_manager is ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; procedure test_top_pck_proc_by_path_cu is @@ -421,8 +680,8 @@ create or replace package body test_suite_manager is ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; procedure test_top_pkc_proc_by_name is @@ -449,8 +708,8 @@ create or replace package body test_suite_manager is l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; procedure test_top_pkc_proc_by_name_cu is @@ -477,8 +736,8 @@ create or replace package body test_suite_manager is l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); - ut.expect(l_test_proc.before_test is not null).to_be_true; - ut.expect(l_test_proc.after_test is not null).to_be_true; + ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); + ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); end; procedure test_top_pkc_nosub_by_name is @@ -978,236 +1237,6 @@ end;'; execute immediate 'drop package tst_empty_suite_path'; end; - procedure compile_dummy_packages is - pragma autonomous_transaction; - begin - execute immediate q'[create or replace package test_package_1 is - - --%suite - --%displayname(test_package_1) - --%suitepath(tests) - - gv_glob_val number; - - --%beforeeach - procedure global_setup; - - --%aftereach - procedure global_teardown; - - --%test - --%displayname(Test1 from test package 1) - procedure test1; - - --%test(Test2 from test package 1) - --%beforetest(test2_setup) - --%aftertest(test2_teardown) - procedure test2; - - procedure test2_setup; - - procedure test2_teardown; - -end test_package_1;]'; - - execute immediate q'[create or replace package body test_package_1 is - - gv_var_1 number; - - gv_var_1_temp number; - - procedure global_setup is - begin - gv_var_1 := 1; - gv_glob_val := 1; - end; - - procedure global_teardown is - begin - gv_var_1 := 0; - gv_glob_val := 0; - end; - - procedure test1 is - begin - ut.expect(gv_var_1, 'Some expectation').to_equal(1); - end; - - procedure test2 is - begin - ut.expect(gv_var_1, 'Some expectation').to_equal(2); - end; - - procedure test2_setup is - begin - gv_var_1_temp := gv_var_1; - gv_var_1 := 2; - end; - - procedure test2_teardown is - begin - gv_var_1 := gv_var_1_temp; - gv_var_1_temp := null; - end; - -end test_package_1;]'; - - execute immediate q'[create or replace package test_package_2 is - - --%suite - --%suitepath(tests.test_package_1) - - gv_glob_val varchar2(1); - - --%beforeeach - procedure global_setup; - - --%aftereach - procedure global_teardown; - - --%test - procedure test1; - - --%test - --%beforetest(test2_setup) - --%aftertest(test2_teardown) - procedure test2; - - procedure test2_setup; - - procedure test2_teardown; - -end test_package_2;]'; -execute immediate q'[create or replace package body test_package_2 is - - gv_var_1 varchar2(1); - - gv_var_1_temp varchar2(1); - - procedure global_setup is - begin - gv_var_1 := 'a'; - gv_glob_val := 'z'; - end; - - procedure global_teardown is - begin - gv_var_1 := 'n'; - gv_glob_val := 'n'; - end; - - procedure test1 is - begin - ut.expect(gv_var_1).to_equal('a'); - end; - - procedure test2 is - begin - ut.expect(gv_var_1).to_equal('b'); - end; - - procedure test2_setup is - begin - gv_var_1_temp := gv_var_1; - gv_var_1 := 'b'; - end; - - procedure test2_teardown is - begin - gv_var_1 := gv_var_1_temp; - gv_var_1_temp := null; - end; - -end test_package_2;]'; - - execute immediate q'[create or replace package test_package_3 is - - --%suite - --%suitepath(tests2) - --%rollback(auto) - - gv_glob_val number; - - --%beforeeach - procedure global_setup; - - --%aftereach - procedure global_teardown; - - --%test - --%rollback(auto) - procedure test1; - - --%test - --%beforetest(test2_setup) - --%aftertest(test2_teardown) - procedure test2; - - procedure test2_setup; - - procedure test2_teardown; - - --%test - --%disabled - procedure disabled_test; - -end test_package_3;]'; - execute immediate q'[create or replace package body test_package_3 is - - gv_var_1 number; - - gv_var_1_temp number; - - procedure global_setup is - begin - gv_var_1 := 1; - gv_glob_val := 1; - end; - - procedure global_teardown is - begin - gv_var_1 := 0; - gv_glob_val := 0; - end; - - procedure test1 is - begin - ut.expect(gv_var_1).to_equal(1); - end; - - procedure test2 is - begin - ut.expect(gv_var_1).to_equal(2); - end; - - procedure test2_setup is - begin - gv_var_1_temp := gv_var_1; - gv_var_1 := 2; - end; - - procedure test2_teardown is - begin - gv_var_1 := gv_var_1_temp; - gv_var_1_temp := null; - end; - - procedure disabled_test is - begin - null; - end; - -end test_package_3;]'; - end; - - procedure drop_dummy_packages is - pragma autonomous_transaction; - begin - execute immediate 'drop package test_package_1'; - execute immediate 'drop package test_package_2'; - execute immediate 'drop package test_package_3'; - end; - procedure test_pck_with_same_path is l_objects_to_run ut3.ut_suite_items; l_suite1 ut3.ut_logical_suite; diff --git a/test/core/test_ut_suite.pkb b/test/core/test_ut_suite.pkb index e953be675..c66e25af3 100644 --- a/test/core/test_ut_suite.pkb +++ b/test/core/test_ut_suite.pkb @@ -1,27 +1,23 @@ create or replace package body test_ut_suite is procedure cleanup_package_state is - begin - ut_example_tests.g_number := null; - end; + begin + ut_example_tests.g_number := null; + end; procedure disabled_suite is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_disabled_flag => true, - a_before_all_proc_name => 'set_g_number_0', - a_after_all_proc_name => 'add_1_to_g_number' - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.path := 'ut_example_tests'; + l_suite.disabled_flag := ut3.ut_utils.boolean_to_int(true); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_be_null; ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_disabled); @@ -33,20 +29,15 @@ create or replace package body test_ut_suite is end; procedure beforeall_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - /*a_rollback_type => ut3.ut_utils.gc_rollback_auto,*/ - a_before_all_proc_name => 'failing_procedure' - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.path := 'ut_example_tests'; + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_be_null; ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); @@ -58,20 +49,17 @@ create or replace package body test_ut_suite is end; procedure aftereall_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_after_all_proc_name => 'failing_procedure' - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.path := 'ut_example_tests'; + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_all)); + l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(1); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_success); @@ -83,83 +71,66 @@ create or replace package body test_ut_suite is end; procedure package_without_body is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; begin - l_suite := ut3.ut_suite( - a_description => 'Suite name', - a_name => 'UT_WITHOUT_BODY', - a_object_name => 'UT_WITHOUT_BODY', - a_path => 'ut_without_body'/*, - a_rollback_type => ut3.ut_utils.gc_rollback_auto*/ - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_WITHOUT_BODY'); + l_suite.path := 'UT_WITHOUT_BODY'; l_suite.add_item(ut3.ut_test(a_object_name => 'ut_without_body',a_name => 'test1'/*, a_rollback_type => ut3.ut_utils.gc_rollback_auto*/)); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); end; procedure package_with_invalid_body is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; begin - l_suite := ut3.ut_suite( - a_description => 'Suite name', - a_name => 'UT_WITH_INVALID_BODY', - a_object_name => 'UT_WITH_INVALID_BODY', - a_path => 'ut_with_invalid_body'/*, - a_rollback_type => ut3.ut_utils.gc_rollback_auto*/ - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_WITH_INVALID_BODY'); + l_suite.path := 'UT_WITH_INVALID_BODY'; l_suite.add_item(ut3.ut_test(a_object_name => 'ut_with_invalid_body',a_name => 'test1'/*, a_rollback_type => ut3.ut_utils.gc_rollback_auto*/)); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); end; procedure test_rollback_type(a_procedure_name varchar2, a_rollback_type integer, a_expectation ut3_latest_release.ut_matcher) is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); - begin - --Arrange - execute immediate 'delete from ut$test_table'; - l_suite := ut3.ut_suite( - a_description => 'Suite name', - a_name => 'UT_TRANSACTION_CONTROL', - a_object_name => 'UT_TRANSACTION_CONTROL', - a_path => 'ut_transaction_control', - a_rollback_type => a_rollback_type, - a_before_all_proc_name => 'setup' - ); - l_suite.add_item(ut3.ut_test(a_object_name => 'ut_transaction_control',a_name => a_procedure_name, a_rollback_type => a_rollback_type)); + l_suite ut3.ut_suite; + begin + --Arrange + execute immediate 'delete from ut$test_table'; + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_TRANSACTION_CONTROL'); + l_suite.path := 'ut_transaction_control'; + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_TRANSACTION_CONTROL', 'setup', ut3.ut_utils.gc_before_all)); + l_suite.add_item(ut3.ut_test(a_object_owner => USER, a_object_name => 'ut_transaction_control',a_name => a_procedure_name)); + l_suite.set_rollback_type(a_rollback_type); - --Act - l_suite.do_execute(l_listener); + --Act + l_suite.do_execute(); - --Assert - ut.expect(core.get_value(q'[ut_transaction_control.count_rows('t')]')).to_( a_expectation ); - ut.expect(core.get_value(q'[ut_transaction_control.count_rows('s')]')).to_( a_expectation ); - end; + --Assert + ut.expect(core.get_value(q'[ut_transaction_control.count_rows('t')]')).to_( a_expectation ); + ut.expect(core.get_value(q'[ut_transaction_control.count_rows('s')]')).to_( a_expectation ); + end; procedure rollback_auto is - begin - test_rollback_type('test', ut3.ut_utils.gc_rollback_auto, equal(0) ); - end; + begin + test_rollback_type('test', ut3.ut_utils.gc_rollback_auto, equal(0) ); + end; procedure rollback_auto_on_failure is - begin - test_rollback_type('test_failure', ut3.ut_utils.gc_rollback_auto, equal(0) ); - end; + begin + test_rollback_type('test_failure', ut3.ut_utils.gc_rollback_auto, equal(0) ); + end; procedure rollback_manual is - begin - test_rollback_type('test', ut3.ut_utils.gc_rollback_manual, be_greater_than(0) ); - end; + begin + test_rollback_type('test', ut3.ut_utils.gc_rollback_manual, be_greater_than(0) ); + end; procedure rollback_manual_on_failure is - begin - test_rollback_type('test_failure', ut3.ut_utils.gc_rollback_manual, be_greater_than(0) ); - end; + begin + test_rollback_type('test_failure', ut3.ut_utils.gc_rollback_manual, be_greater_than(0) ); + end; end; -/ +/ \ No newline at end of file diff --git a/test/core/test_ut_test.pkb b/test/core/test_ut_test.pkb index f2b1d6952..ecce27c3c 100644 --- a/test/core/test_ut_test.pkb +++ b/test/core/test_ut_test.pkb @@ -6,20 +6,19 @@ create or replace package body test_ut_test is end; procedure disabled_test is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; + l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_before_all_proc_name => 'set_g_number_0' - ); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.path := 'ut_example_tests'; + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + + l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); - l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number',a_disabled_flag => true)); + l_suite.items(l_suite.items.last).disabled_flag := ut3.ut_utils.boolean_to_int(true); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(1); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_success); @@ -31,20 +30,21 @@ create or replace package body test_ut_test is end; procedure aftertest_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; + l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_before_all_proc_name => 'set_g_number_0' - ); - l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number', a_before_test_proc_name=>'add_1_to_g_number', a_after_test_proc_name=>'failing_procedure')); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.path := 'ut_example_tests'; + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + + l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_test)); + l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(3); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); @@ -56,20 +56,19 @@ create or replace package body test_ut_test is end; procedure aftereach_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; + l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_before_all_proc_name => 'set_g_number_0' - ); - l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number', a_before_each_proc_name=>'add_1_to_g_number', a_after_each_proc_name=>'failing_procedure')); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_each)); + l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(3); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); @@ -81,20 +80,19 @@ create or replace package body test_ut_test is end; procedure beforetest_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; + l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_before_all_proc_name => 'set_g_number_0' - ); - l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number', a_before_test_proc_name=>'failing_procedure', a_after_test_proc_name=>'add_1_to_g_number')); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); + l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(2); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); @@ -106,20 +104,19 @@ create or replace package body test_ut_test is end; procedure beforeeach_errors is - l_suite ut3.ut_logical_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); + l_suite ut3.ut_suite; + l_test ut3.ut_test; begin --Arrange - l_suite := ut3.ut_suite( - a_name => 'UT_EXAMPLE_TESTS', - a_object_name => 'UT_EXAMPLE_TESTS', - a_path => 'ut_example_tests', - a_before_all_proc_name => 'set_g_number_0' - ); - l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number', a_before_each_proc_name=>'failing_procedure', a_after_each_proc_name=>'add_1_to_g_number')); + l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_after_each)); + l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(2); ut.expect(l_suite.result).to_equal(ut3.ut_utils.gc_error); diff --git a/test/core/test_ut_utils.pkb b/test/core/test_ut_utils.pkb index 7efe3b0ef..36f849aae 100644 --- a/test/core/test_ut_utils.pkb +++ b/test/core/test_ut_utils.pkb @@ -354,5 +354,66 @@ end;'; --Assert ut.expect(anydata.convertcollection(l_list_to_be_empty)).to_be_empty; end; + + procedure replace_multiline_comments + is + l_source clob; + l_actual clob; + l_expected clob; + begin + --Arrange + l_source := q'[ +create or replace package dummy as + + -- single line comment with disabled /* multi-line comment */ + gv_text0 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; + gv_text1 varchar2(200) := '/* multi-line comment in a string*/'; + gv_text2 varchar2(200) := '/* multi-line comment + in a multi-line + string*/'; + -- ignored start of multi-line comment with multi-byte text � /* + -- ignored end of multi-line comment with multi-byte text � */ + /* multi-line comment + with + multi-byte characters ��� + in it */ + gv_text3 varchar2(200) := 'some text'; /* multiline comment*/ --followed by single-line comment + /* multi-line comment in one line*/ + gv_text4 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; +end; +]'; + l_expected := q'[ +create or replace package dummy as + + -- single line comment with disabled /* multi-line comment */ + gv_text0 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; + gv_text1 varchar2(200) := '/* multi-line comment in a string*/'; + gv_text2 varchar2(200) := '/* multi-line comment + in a multi-line + string*/'; + -- ignored start of multi-line comment with multi-byte text � /* + -- ignored end of multi-line comment with multi-byte text � */ + ]'||q'[ + + + + gv_text3 varchar2(200) := 'some text'; --followed by single-line comment + ]'||q'[ + gv_text4 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; +end; +]'; + --Act + l_actual := ut3.ut_utils.replace_multiline_comments(l_source); + --Assert + ut.expect(l_actual).to_equal(l_expected); + end; end test_ut_utils; / diff --git a/test/core/test_ut_utils.pks b/test/core/test_ut_utils.pks index 597628b23..4d5ce56af 100644 --- a/test/core/test_ut_utils.pks +++ b/test/core/test_ut_utils.pks @@ -73,5 +73,9 @@ create or replace package test_ut_utils is --%test(Filter list elements with empty collection) procedure filter_list_empty_collection; + + --%test(Replace multi-line comments with empty lines) + procedure replace_multiline_comments; + end test_ut_utils; / diff --git a/test/install_tests.sql b/test/install_tests.sql index 5c97dc0b5..282e8b440 100644 --- a/test/install_tests.sql +++ b/test/install_tests.sql @@ -26,6 +26,7 @@ whenever oserror exit failure rollback @@core/test_output_buffer.pks @@core/test_file_mapper.pks @@core/test_suite_manager.pks +@@core/test_suite_builder.pks @@core/reporters.pks @@core/reporters/test_coverage.pks set define on @@ -68,6 +69,7 @@ set define off @@core/test_output_buffer.pkb @@core/test_file_mapper.pkb @@core/test_suite_manager.pkb +@@core/test_suite_builder.pkb @@core/reporters.pkb @@core/reporters/test_coverage.pkb set define on diff --git a/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql b/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql deleted file mode 100644 index f739d97c8..000000000 --- a/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql +++ /dev/null @@ -1,30 +0,0 @@ -set termout off -create or replace package empty_suite as - -- %suite - - procedure not_a_test; -end; -/ -create or replace package body empty_suite as - procedure not_a_test is begin null; end; -end; -/ -set termout on -declare - l_result integer; -begin - select * - into l_result - from table(ut.run('empty_suite',utplsql_test_reporter())); ---Assert - if l_result = ut_utils.tr_error then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line('expected failure of ''empty_suite'' got: '''||ut_utils.test_result_to_char(l_result)||'''' ); - end if; -end; -/ - -set termout off -drop package empty_suite; -set termout on diff --git a/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql b/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql deleted file mode 100644 index fd030a701..000000000 --- a/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql +++ /dev/null @@ -1,17 +0,0 @@ ---Arrange -declare - l_expected department$ := department$('hr'); - l_actual department$ := department$('hr'); - l_result integer; -begin ---Act - ut.expect( anydata.convertObject(l_actual) ).not_to_equal( anydata.convertObject(l_expected) ); - l_result := ut_expectation_processor.get_status(); ---Assert - if l_result = ut_utils.tr_failure then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line('expected: '''||ut_utils.tr_success||''', got: '''||l_result||'''' ); - end if; -end; -/ diff --git a/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql b/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql deleted file mode 100644 index 6c54e4330..000000000 --- a/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql +++ /dev/null @@ -1,24 +0,0 @@ ---Arrange -declare - l_result integer; - l_dummy integer; - l_output ut_output_buffer_base := ut_output_table_buffer(); - l_start_time timestamp := systimestamp; - l_wait_seconds integer := 1; -begin - --Act - l_output.send_line(lpad('a text',4000,',a text')); - - select count(*) into l_dummy from table( l_output.get_lines( a_initial_timeout => 0, a_timeout_sec => l_wait_seconds )); - l_result := round(extract(second from (systimestamp - l_start_time))); - - --Assert - ut.expect(l_result).to_equal(l_wait_seconds); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line(ut_expectation_processor.get_expectations_results()(1).get_result_clob); - end if; -end; -/