Skip to content

Commit 46e0a8e

Browse files
authored
Merge pull request #1250 from utPLSQL/feature/1042_add_and_to_tags
Feature/1042 add and to tags
2 parents 69092f2 + 46ffe73 commit 46e0a8e

25 files changed

+1250
-187
lines changed

docs/userguide/annotations.md

Lines changed: 10 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,93 +1616,25 @@ or
16161616

16171617
Tags are defined as a comma separated list within the `--%tags` annotation.
16181618

1619-
When executing a test run with tag filter applied, the framework will find all tests associated with the given tags and execute them.
1620-
The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run.
1621-
1622-
When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent. Parent suite tests are not executed, but a suitepath hierarchy is kept.
1623-
1624-
1625-
Sample test suite package with tags.
1626-
```sql linenums="1"
1627-
create or replace package ut_sample_test is
1628-
1629-
--%suite(Sample Test Suite)
1630-
--%tags(api)
1631-
1632-
--%test(Compare Ref Cursors)
1633-
--%tags(complex,fast)
1634-
procedure ut_refcursors1;
1635-
1636-
--%test(Run equality test)
1637-
--%tags(simple,fast)
1638-
procedure ut_test;
1639-
1640-
end ut_sample_test;
1641-
/
1642-
1643-
create or replace package body ut_sample_test is
1644-
1645-
procedure ut_refcursors1 is
1646-
v_actual sys_refcursor;
1647-
v_expected sys_refcursor;
1648-
begin
1649-
open v_expected for select 1 as test from dual;
1650-
open v_actual for select 2 as test from dual;
1651-
1652-
ut.expect(v_actual).to_equal(v_expected);
1653-
end;
1654-
1655-
procedure ut_test is
1656-
begin
1657-
ut.expect(1).to_equal(0);
1658-
end;
1659-
1660-
end ut_sample_test;
1661-
/
1662-
```
1663-
1664-
Execution of the test is done by using the parameter `a_tags`
1665-
1666-
```sql linenums="1"
1667-
select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api'));
1668-
```
1669-
The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api`
1670-
1671-
```sql linenums="1"
1672-
select * from table(ut.run(a_tags => 'complex'));
1673-
```
1674-
The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex`
1675-
1676-
```sql linenums="1"
1677-
select * from table(ut.run(a_tags => 'fast'));
1678-
```
1679-
The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast`
1619+
When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent, unless they are excluded explicitly at runtime with a negated tag expression.
1620+
See [running unit tests](running-unit-tests.md) for more information on using tags to filter test suites that are to be executed.
16801621

16811622
#### Tag naming convention
16821623

16831624
Tags must follow the below naming convention:
16841625

16851626
- tag is case sensitive
1686-
- tag can contain special characters like `$#/\?-!` etc.
1687-
- tag cannot be an empty string
1627+
- tag must not contain any of the following reserved characters:
1628+
- comma (,)
1629+
- left or right parenthesis ((, ))
1630+
- ampersand (&)
1631+
- vertical bar (|)
1632+
- exclamation point (!)
1633+
- tag cannot be null or blank
16881634
- tag cannot start with a dash, e.g. `-some-stuff` is **not** a valid tag
16891635
- tag cannot contain spaces, e.g. `test of batch`. To create a multi-word tag use underscores or dashes, e.g. `test_of_batch`, `test-of-batch`
16901636
- leading and trailing spaces are ignored in tag name, e.g. `--%tags( tag1 , tag2 )` becomes `tag1` and `tag2` tag names
1691-
1692-
1693-
#### Excluding tests/suites by tags
1694-
1695-
It is possible to exclude parts of test suites with tags.
1696-
In order to do so, prefix the tag name to exclude with a `-` (dash) sign when invoking the test run.
1697-
1698-
Examples (based on above sample test suite)
1699-
1700-
```sql linenums="1"
1701-
select * from table(ut.run(a_tags => 'api,fast,-complex'));
1702-
```
1703-
The above call will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex`.
1704-
Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed.
1705-
1637+
- tag cannot be one of two reserved words: `none` and `any`. `none` and `any` as a tag will be treated as no tag
17061638

17071639

17081640
### Suitepath

docs/userguide/running-unit-tests.md

Lines changed: 203 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -319,22 +319,218 @@ select * from table(ut.run('hr.test_apply_bonus', a_random_test_order_seed => 30
319319

320320
In addition to the path, you can filter the tests to be run by specifying tags. Tags are defined in the test / context / suite with the `--%tags`-annotation ([Read more](annotations.md#tags)).
321321
Multiple tags are separated by comma.
322-
The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run.
322+
323+
324+
### Tag Expressions
325+
326+
Tag expressions are boolean expressions created by combining tags with the `!`, `&`, `|` operators. Tag expressions can be grouped using `(` and `)` braces. Grouping tag expressions affects operator precedence.
327+
328+
Two reserved keywords, `any` and `none`, can be used when creating a tag expression to run tests.
329+
- `any` keyword represents tests and suites with any tags
330+
- `none` keyword represents tests and suites without tags
331+
332+
These keywords may be combined with other expressions just like normal tags.
333+
334+
!!! note
335+
When specifying `none`, be aware that it will exclude any tests/suites/contexts contained within a tagged suite.
336+
337+
| Operator | Meaning |
338+
| -------- | --------|
339+
| ! | not |
340+
| & | and |
341+
| \| | or |
342+
343+
If you are tagging your tests across multiple dimensions, tag expressions help you to select which tests to execute. When tagging by test type (e.g., micro, integration, end-to-end) and feature (e.g., product, catalog, shipping), the following tag expressions can be useful.
344+
345+
346+
| Tag Expression | Selection |
347+
| -------- | --------|
348+
| product | all tests for product |
349+
| catalog \| shipping | all tests for catalog plus all tests for shipping |
350+
| catalog & shipping | all tests that are tagged with both `catalog` and `shipping` tags |
351+
| product & !end-to-end | all tests tagged `product`, except the tests tagged `end-to-end` |
352+
| (micro \| integration) & (product \| shipping) | all micro or integration tests for product or shipping |
353+
354+
355+
Taking the last expression above `(micro | integration) & (product | shipping)`
356+
357+
| --%tags |included in run |
358+
| -------- | --------|
359+
| micro | no |
360+
| integration | no |
361+
| micro | no |
362+
| product | no |
363+
| shipping | no |
364+
| micro | no |
365+
| micro, integration | no |
366+
| product, shipping | no |
367+
| micro, product | yes |
368+
| micro, shipping | yes |
369+
| integration, product | yes |
370+
| integration, shipping | yes |
371+
| integration, micro, shipping | yes |
372+
| integration, micro, product | yes |
373+
| integration, shipping ,product | yes |
374+
| micro, shipping ,product | yes |
375+
| integration, micro, shipping ,product | yes |
376+
377+
378+
### Sample execution of test with tags.
379+
380+
Execution of the test with tag expressions is done using the parameter `a_tags`.
381+
Given a test package `ut_sample_test` defined below
323382

324383
```sql linenums="1"
325-
begin
326-
ut.run('hr.test_apply_bonus', a_tags => 'test1,test2');
327-
end;
384+
create or replace package ut_sample_test is
385+
386+
--%suite(Sample Test Suite)
387+
--%tags(api)
388+
389+
--%test(Compare Ref Cursors)
390+
--%tags(complex,fast)
391+
procedure ut_refcursors1;
392+
393+
--%test(Run equality test)
394+
--%tags(simple,fast)
395+
procedure ut_test;
396+
397+
end ut_sample_test;
398+
/
399+
400+
create or replace package body ut_sample_test is
401+
402+
procedure ut_refcursors1 is
403+
v_actual sys_refcursor;
404+
v_expected sys_refcursor;
405+
begin
406+
open v_expected for select 1 as test from dual;
407+
open v_actual for select 2 as test from dual;
408+
409+
ut.expect(v_actual).to_equal(v_expected);
410+
end;
411+
412+
procedure ut_test is
413+
begin
414+
ut.expect(1).to_equal(0);
415+
end;
416+
417+
end ut_sample_test;
418+
/
328419
```
420+
329421
```sql linenums="1"
330-
select * from table(ut.run('hr.test_apply_bonus', a_tags => 'suite1'))
422+
select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api'));
331423
```
424+
The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api`
332425

333-
You can also exclude specific tags by adding a `-` (dash) in front of the tag
426+
```sql linenums="1"
427+
select * from table(ut.run(a_tags => 'fast&complex'));
428+
```
429+
The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex` and `fast`
334430

335431
```sql linenums="1"
336-
select * from table(ut.run('hr.test_apply_bonus', a_tags => '-suite1'))
432+
select * from table(ut.run(a_tags => 'fast'));
337433
```
434+
The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast`
435+
436+
### Excluding tests/suites by tags
437+
438+
It is possible to exclude parts of test suites with tags.
439+
In order to do so, prefix the tag name to exclude with a `!` (exclamation) sign when invoking the test run which is equivalent of `-` (dash) in legacy notation.
440+
Examples (based on above sample test suite)
441+
442+
```sql linenums="1"
443+
select * from table(ut.run(a_tags => '(api|fast)&!complex'));
444+
```
445+
446+
or
447+
448+
```sql linenums="1"
449+
select * from table(ut.run(a_tags => '(api|fast)&!complex&!test1'));
450+
```
451+
452+
which is equivalent of exclusion on whole expression
453+
454+
```sql linenums="1"
455+
select * from table(ut.run(a_tags => '(api|fast)&!(complex|test1)'));
456+
```
457+
458+
The above calls will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex` and except those suites/contexts/tests that are marked as `test1`.
459+
Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed.
460+
461+
462+
### Sample execution with `any` and `none`
463+
464+
Given a sample test package:
465+
466+
```sql linenums="1"
467+
create or replace package ut_sample_test is
468+
469+
--%suite(Sample Test Suite)
470+
471+
--%test(Compare Ref Cursors)
472+
--%tags(complex,fast)
473+
procedure ut_refcursors1;
474+
475+
--%test(Run equality test)
476+
--%tags(simple,fast)
477+
procedure ut_test;
478+
479+
--%test(Run equality test no tag)
480+
procedure ut_test_no_tag;
481+
482+
end ut_sample_test;
483+
/
484+
485+
create or replace package body ut_sample_test is
486+
487+
procedure ut_refcursors1 is
488+
v_actual sys_refcursor;
489+
v_expected sys_refcursor;
490+
begin
491+
open v_expected for select 1 as test from dual;
492+
open v_actual for select 2 as test from dual;
493+
494+
ut.expect(v_actual).to_equal(v_expected);
495+
end;
496+
497+
procedure ut_test is
498+
begin
499+
ut.expect(1).to_equal(0);
500+
end;
501+
502+
procedure ut_test_no_tag is
503+
begin
504+
ut.expect(1).to_equal(0);
505+
end;
506+
507+
end ut_sample_test;
508+
/
509+
```
510+
511+
```sql linenums="1"
512+
select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none'));
513+
```
514+
515+
The above call will execute tests `ut_test_no_tag`
516+
517+
```sql linenums="1"
518+
select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'any'));
519+
```
520+
521+
The above call will execute tests `ut_test` and `ut_refcursors1`
522+
523+
```sql linenums="1"
524+
select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none|simple'));
525+
```
526+
527+
The above call will execute tests `ut_test_no_tag` and `ut_test`
528+
529+
```sql linenums="1"
530+
select * from table(ut.run(a_tags => 'none|!simple'));
531+
```
532+
533+
The above call will execute tests `ut_test_no_tag` and `ut_refcursors1`
338534

339535
## Keeping uncommitted data after test-run
340536

source/api/ut_runner.pkb

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ create or replace package body ut_runner is
7878
l_run ut_run;
7979
l_coverage_schema_names ut_varchar2_rows;
8080
l_paths ut_varchar2_list;
81-
l_random_test_order_seed positive;
82-
l_tags ut_varchar2_rows := ut_varchar2_rows();
81+
l_random_test_order_seed positive;
8382
begin
8483
ut_event_manager.initialize();
8584
if a_reporters is not empty then
@@ -118,12 +117,6 @@ create or replace package body ut_runner is
118117
l_coverage_schema_names := ut_suite_manager.get_schema_names(l_paths);
119118
end if;
120119

121-
122-
if a_tags is not null then
123-
l_tags := l_tags multiset union distinct ut_utils.convert_collection(
124-
ut_utils.trim_list_elements(ut_utils.filter_list(ut_utils.string_to_table(a_tags,','),ut_utils.gc_word_no_space))
125-
);
126-
end if;
127120
l_run := ut_run(
128121
a_run_paths => l_paths,
129122
a_coverage_options => ut_coverage_options(
@@ -140,10 +133,10 @@ create or replace package body ut_runner is
140133
a_test_file_mappings => set(a_test_file_mappings),
141134
a_client_character_set => a_client_character_set,
142135
a_random_test_order_seed => l_random_test_order_seed,
143-
a_run_tags => l_tags
136+
a_run_tags => a_tags
144137
);
145138

146-
ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, l_tags);
139+
ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, a_tags);
147140
if a_force_manual_rollback then
148141
l_run.set_rollback_type( a_rollback_type => ut_utils.gc_rollback_manual, a_force => true );
149142
end if;

source/core/types/ut_run.tpb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ create or replace type body ut_run as
2424
a_test_file_mappings ut_file_mappings := null,
2525
a_client_character_set varchar2 := null,
2626
a_random_test_order_seed positive := null,
27-
a_run_tags ut_varchar2_rows := null
27+
a_run_tags varchar2 := null
2828
) return self as result is
2929
begin
3030
self.run_paths := a_run_paths;

source/core/types/ut_run.tps

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ create or replace type ut_run under ut_suite_item (
2121
project_name varchar2(4000),
2222
items ut_suite_items,
2323
run_paths ut_varchar2_list,
24-
run_tags ut_varchar2_rows,
24+
run_tags varchar2(4000),
2525
coverage_options ut_coverage_options,
2626
test_file_mappings ut_file_mappings,
2727
client_character_set varchar2(100),
@@ -34,7 +34,7 @@ create or replace type ut_run under ut_suite_item (
3434
a_test_file_mappings ut_file_mappings := null,
3535
a_client_character_set varchar2 := null,
3636
a_random_test_order_seed positive := null,
37-
a_run_tags ut_varchar2_rows := null
37+
a_run_tags varchar2 := null
3838
) return self as result,
3939
overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2),
4040
overriding member function do_execute(self in out nocopy ut_run) return boolean,

0 commit comments

Comments
 (0)