Skip to content

Commit 7740ec2

Browse files
authored
Merge pull request #1013 from utPLSQL/feature/nested_context
Feature/nested context
2 parents c69af46 + be39586 commit 7740ec2

File tree

8 files changed

+789
-197
lines changed

8 files changed

+789
-197
lines changed

docs/userguide/annotations.md

Lines changed: 227 additions & 22 deletions
Large diffs are not rendered by default.

source/core/ut_suite_builder.pkb

Lines changed: 102 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ create or replace package body ut_suite_builder is
3636
gc_throws constant t_annotation_name := 'throws';
3737
gc_rollback constant t_annotation_name := 'rollback';
3838
gc_context constant t_annotation_name := 'context';
39+
gc_name constant t_annotation_name := 'name';
3940
gc_endcontext constant t_annotation_name := 'endcontext';
4041

4142
type tt_annotations is table of t_annotation_name;
@@ -57,6 +58,7 @@ create or replace package body ut_suite_builder is
5758
gc_throws,
5859
gc_rollback,
5960
gc_context,
61+
gc_name,
6062
gc_endcontext
6163
);
6264

@@ -637,7 +639,7 @@ create or replace package body ut_suite_builder is
637639
a_suite.path := lower(coalesce(a_suite.path, a_suite.object_name));
638640
end;
639641

640-
procedure add_suite_tests(
642+
procedure add_tests_to_items(
641643
a_suite in out nocopy ut_suite,
642644
a_annotations t_annotations_info,
643645
a_suite_items in out nocopy ut_suite_items
@@ -747,59 +749,120 @@ create or replace package body ut_suite_builder is
747749
return l_result;
748750
end;
749751

750-
procedure get_suite_contexts_items(
751-
a_suite in out nocopy ut_suite,
752+
procedure get_context_items(
753+
a_parent in out nocopy ut_suite,
752754
a_annotations in out nocopy t_annotations_info,
753-
a_suite_items out nocopy ut_suite_items
755+
a_suite_items out nocopy ut_suite_items,
756+
a_parent_context_pos in integer := 0
754757
) is
755-
l_context_pos t_annotation_position;
756-
l_end_context_pos t_annotation_position;
757-
l_context_name t_object_name;
758-
l_ctx_annotations t_annotations_info;
759-
l_context ut_suite_context;
760-
l_context_no binary_integer := 1;
761-
l_context_items ut_suite_items;
758+
l_context_pos t_annotation_position;
759+
l_next_context_pos t_annotation_position;
760+
l_end_context_pos t_annotation_position;
761+
l_ctx_annotations t_annotations_info;
762+
l_context ut_suite_context;
763+
l_context_no binary_integer := 1;
764+
l_context_items ut_suite_items;
762765
type tt_context_names is table of boolean index by t_object_name;
763-
l_context_names tt_context_names;
766+
l_used_context_names tt_context_names;
767+
l_context_name t_object_name;
768+
l_default_context_name t_object_name;
769+
function get_context_name(
770+
a_parent in out nocopy ut_suite,
771+
a_context_names in tt_annotation_texts,
772+
a_start_position binary_integer,
773+
a_end_position binary_integer
774+
) return varchar2 is
775+
l_result t_annotation_name;
776+
l_found boolean;
777+
l_annotation_pos binary_integer;
778+
begin
779+
l_annotation_pos := a_context_names.first;
780+
while l_annotation_pos is not null loop
781+
if l_annotation_pos > a_start_position and l_annotation_pos < a_end_position then
782+
if l_found then
783+
add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos);
784+
else
785+
l_result := a_context_names(l_annotation_pos);
786+
end if;
787+
l_found := true;
788+
end if;
789+
l_annotation_pos := a_context_names.next(l_annotation_pos);
790+
end loop;
791+
return l_result;
792+
end;
764793
begin
765794
a_suite_items := ut_suite_items();
766795
if not a_annotations.by_name.exists(gc_context) then
767796
return;
768797
end if;
769798

770-
l_context_pos := a_annotations.by_name( gc_context).first;
799+
l_context_pos := a_annotations.by_name( gc_context).next(a_parent_context_pos);
771800

772801
while l_context_pos is not null loop
802+
l_default_context_name := 'nested_context_#'||l_context_no;
773803
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
774804

775-
exit when l_end_context_pos is null;
805+
l_next_context_pos := a_annotations.by_name(gc_context).next(l_context_pos);
806+
if a_annotations.by_name.exists(gc_name) then
807+
l_context_name :=
808+
get_context_name(
809+
a_parent,
810+
a_annotations.by_name( gc_name ),
811+
l_context_pos,
812+
least(
813+
coalesce( l_end_context_pos, a_annotations.by_line.last ),
814+
coalesce( l_next_context_pos, a_annotations.by_line.last )
815+
)
816+
);
817+
end if;
818+
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) or l_context_name is null then
819+
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) then
820+
a_parent.put_warning(
821+
'Invalid value "'||l_context_name||'" for context name.' ||
822+
' Context name ignored and fallback to auto-name "'||l_default_context_name||'" ' ||
823+
get_object_reference( a_parent, null, l_context_pos )
824+
);
825+
end if;
826+
l_context_name := l_default_context_name;
827+
end if;
828+
if l_used_context_names.exists(l_context_name) then
829+
add_annotation_ignored_warning(
830+
a_parent, gc_name,
831+
'Context name "'||l_context_name||'" already used in this scope. Name must be unique.' ||
832+
' Using fallback name '||l_default_context_name||'.', l_context_pos );
833+
l_context_name := l_default_context_name;
834+
end if;
835+
l_used_context_names(l_context_name) := true;
776836

777-
l_context_items := ut_suite_items();
778-
--create a sub-set of annotations to process as sub-suite (context)
779-
l_ctx_annotations := get_annotations_in_context( a_annotations, l_context_pos, l_end_context_pos);
837+
l_context := ut_suite_context(a_parent.object_owner, a_parent.object_name, l_context_name, l_context_pos );
838+
l_context.path := a_parent.path||'.'||l_context_name;
839+
l_context.description := coalesce( a_annotations.by_line( l_context_pos ).text, l_context_name );
840+
l_context.parse_time := a_annotations.parse_time;
780841

781-
l_context_name := coalesce(
782-
l_ctx_annotations.by_line( l_context_pos ).text
783-
, gc_context||'_'||l_context_no
784-
);
785-
if l_context_names.exists(l_context_name) then
786-
add_annotation_ignored_warning( a_suite, 'context', 'Context name must be unique in a suite. Context and all of it''s content ignored.', l_context_pos );
842+
--if nested context found
843+
if l_next_context_pos < l_end_context_pos or l_end_context_pos is null then
844+
get_context_items( l_context, a_annotations, l_context_items, l_context_pos );
845+
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
787846
else
788-
l_context_names(l_context_name) := true;
847+
l_context_items := ut_suite_items();
848+
end if;
789849

790-
l_context := ut_suite_context(a_suite.object_owner, a_suite.object_name, l_context_name, l_context_pos );
850+
if l_end_context_pos is null then
851+
a_parent.put_warning(
852+
'Missing "--%endcontext" annotation for a "--%context" annotation. The end of package is considered end of context.'|| get_object_reference( a_parent, null, l_context_pos )
853+
);
854+
l_end_context_pos := a_annotations.by_line.last;
855+
end if;
791856

792-
l_context.path := a_suite.path||'.'||l_context_name;
793-
l_context.description := l_ctx_annotations.by_line( l_context_pos ).text;
794-
l_context.parse_time := a_annotations.parse_time;
857+
--create a sub-set of annotations to process as sub-suite (context)
858+
l_ctx_annotations := get_annotations_in_context( a_annotations, l_context_pos, l_end_context_pos);
795859

796-
warning_on_duplicate_annot( l_context, l_ctx_annotations.by_name, gc_context );
860+
warning_on_duplicate_annot( l_context, l_ctx_annotations.by_name, gc_context );
797861

798-
add_suite_tests( l_context, l_ctx_annotations, l_context_items );
799-
add_items_to_list(a_suite_items, l_context_items);
800-
a_suite_items.extend;
801-
a_suite_items(a_suite_items.last) := l_context;
802-
end if;
862+
add_tests_to_items( l_context, l_ctx_annotations, l_context_items );
863+
add_items_to_list(a_suite_items, l_context_items);
864+
a_suite_items.extend;
865+
a_suite_items(a_suite_items.last) := l_context;
803866
-- remove annotations within context after processing them
804867
delete_annotations_range(a_annotations, l_context_pos, l_end_context_pos);
805868

@@ -810,27 +873,17 @@ create or replace package body ut_suite_builder is
810873
end loop;
811874
end;
812875

813-
procedure warning_on_incomplete_context(
876+
procedure warning_on_extra_endcontext(
814877
a_suite in out nocopy ut_suite,
815878
a_package_ann_index tt_annotations_by_name
816879
) is
817880
l_annotation_pos t_annotation_position;
818881
begin
819-
if a_package_ann_index.exists(gc_context) then
820-
l_annotation_pos := a_package_ann_index(gc_context).first;
821-
while l_annotation_pos is not null loop
822-
add_annotation_ignored_warning(
823-
a_suite, gc_context, 'Invalid annotation %%%. Cannot find following "--%endcontext".',
824-
l_annotation_pos
825-
);
826-
l_annotation_pos := a_package_ann_index(gc_context).next(l_annotation_pos);
827-
end loop;
828-
end if;
829882
if a_package_ann_index.exists(gc_endcontext) then
830883
l_annotation_pos := a_package_ann_index(gc_endcontext).first;
831884
while l_annotation_pos is not null loop
832885
add_annotation_ignored_warning(
833-
a_suite, gc_endcontext, 'Invalid annotation %%%. Cannot find preceding "--%context".',
886+
a_suite, gc_endcontext, 'Extra %%% annotation found. Cannot find corresponding "--%context".',
834887
l_annotation_pos
835888
);
836889
l_annotation_pos := a_package_ann_index(gc_endcontext).next(l_annotation_pos);
@@ -901,12 +954,12 @@ create or replace package body ut_suite_builder is
901954
warning_on_duplicate_annot( l_suite, l_annotations.by_name, gc_suite );
902955

903956
build_suitepath( l_suite, l_annotations );
904-
get_suite_contexts_items( l_suite, l_annotations, a_suite_items );
957+
get_context_items( l_suite, l_annotations, a_suite_items );
905958
--create suite tests and add
906-
add_suite_tests( l_suite, l_annotations, a_suite_items );
959+
add_tests_to_items( l_suite, l_annotations, a_suite_items );
907960

908961
--by this time all contexts were consumed and l_annotations should not have any context/endcontext annotation in it.
909-
warning_on_incomplete_context( l_suite, l_annotations.by_name );
962+
warning_on_extra_endcontext( l_suite, l_annotations.by_name );
910963

911964
a_suite_items.extend;
912965
a_suite_items( a_suite_items.last) := l_suite;

0 commit comments

Comments
 (0)