@@ -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