Skip to content

Commit 587f966

Browse files
authored
Merge pull request #1035 from utPLSQL/bugfix/nested_context_mixup
Bugfix/nested context mixup
2 parents 090b74c + 3327fc3 commit 587f966

File tree

3 files changed

+159
-46
lines changed

3 files changed

+159
-46
lines changed

source/core/ut_suite_builder.pkb

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ create or replace package body ut_suite_builder is
1515
See the License for the specific language governing permissions and
1616
limitations under the License.
1717
*/
18-
18+
1919
subtype t_annotation_text is varchar2(4000);
2020
subtype t_annotation_name is varchar2(4000);
2121
subtype t_object_name is varchar2(500);
@@ -315,13 +315,13 @@ create or replace package body ut_suite_builder is
315315
l_annotation_pos := a_throws_ann_text.next(l_annotation_pos);
316316
end loop;
317317
end;
318-
318+
319319
procedure add_tags_to_suite_item(
320320
a_suite in out nocopy ut_suite,
321321
a_tags_ann_text tt_annotation_texts,
322322
a_list in out nocopy ut_varchar2_rows,
323323
a_procedure_name t_object_name := null
324-
) is
324+
) is
325325
l_annotation_pos binary_integer;
326326
l_tags_list ut_varchar2_list := ut_varchar2_list();
327327
l_tag_items ut_varchar2_list;
@@ -354,7 +354,7 @@ create or replace package body ut_suite_builder is
354354
--remove empty strings from table list e.g. tag1,,tag2 and convert to rows
355355
a_list := ut_utils.convert_collection( ut_utils.filter_list(set(l_tags_list),ut_utils.gc_word_no_space) );
356356
end;
357-
357+
358358
procedure set_seq_no(
359359
a_list in out nocopy ut_executables
360360
) is
@@ -533,16 +533,16 @@ create or replace package body ut_suite_builder is
533533
);
534534
set_seq_no(l_test.after_test_list);
535535
end if;
536-
536+
537537
if l_proc_annotations.exists( gc_tags) then
538538
add_tags_to_suite_item(a_suite, l_proc_annotations( gc_tags), l_test.tags, a_procedure_name);
539539
end if;
540-
540+
541541
if l_proc_annotations.exists( gc_throws) then
542542
add_to_throws_numbers_list(a_suite, l_test.expected_error_codes, a_procedure_name, l_proc_annotations( gc_throws));
543543
end if;
544544
l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled));
545-
545+
546546
a_suite_items.extend;
547547
a_suite_items( a_suite_items.last ) := l_test;
548548

@@ -687,7 +687,7 @@ create or replace package body ut_suite_builder is
687687
if a_annotations.by_name.exists(gc_aftereach) then
688688
l_after_each_list := add_executables( a_suite.object_owner, a_suite.object_name, a_annotations.by_name(gc_aftereach), gc_aftereach );
689689
end if;
690-
690+
691691
if a_annotations.by_name.exists(gc_tags) then
692692
add_tags_to_suite_item(a_suite, a_annotations.by_name(gc_tags),a_suite.tags);
693693
end if;
@@ -704,21 +704,59 @@ create or replace package body ut_suite_builder is
704704
set_seq_no(a_suite.after_all_list);
705705
end;
706706

707+
function get_next_annotation_of_type(
708+
a_start_position t_annotation_position,
709+
a_annotation_type varchar2,
710+
a_package_annotations in tt_annotations_by_name
711+
) return t_annotation_position is
712+
l_result t_annotation_position;
713+
begin
714+
if a_package_annotations.exists(a_annotation_type) then
715+
l_result := a_package_annotations(a_annotation_type).first;
716+
while l_result <= a_start_position loop
717+
l_result := a_package_annotations(a_annotation_type).next(l_result);
718+
end loop;
719+
end if;
720+
return l_result;
721+
end;
722+
707723
function get_endcontext_position(
708724
a_context_ann_pos t_annotation_position,
709-
a_package_annotations in out nocopy tt_annotations_by_name
725+
a_package_annotations in tt_annotations_by_line
710726
) return t_annotation_position is
711727
l_result t_annotation_position;
728+
l_open_count integer := 1;
729+
l_idx t_annotation_position := a_package_annotations.next(a_context_ann_pos);
712730
begin
713-
if a_package_annotations.exists(gc_endcontext) then
714-
l_result := a_package_annotations(gc_endcontext).first;
715-
while l_result <= a_context_ann_pos loop
716-
l_result := a_package_annotations(gc_endcontext).next(l_result);
717-
end loop;
731+
while l_open_count > 0 and l_idx is not null loop
732+
if ( a_package_annotations(l_idx).name = gc_context ) then
733+
l_open_count := l_open_count+1;
734+
elsif ( a_package_annotations(l_idx).name = gc_endcontext ) then
735+
l_open_count := l_open_count-1;
736+
l_result := l_idx;
737+
end if;
738+
l_idx := a_package_annotations.next(l_idx);
739+
end loop;
740+
if ( l_open_count > 0 ) then
741+
l_result := null;
718742
end if;
719743
return l_result;
720744
end;
721745

746+
function has_nested_context(
747+
a_context_ann_pos t_annotation_position,
748+
a_package_annotations in tt_annotations_by_name
749+
) return boolean is
750+
l_next_endcontext_pos t_annotation_position := 0;
751+
l_next_context_pos t_annotation_position := 0;
752+
begin
753+
if ( a_package_annotations.exists(gc_endcontext) and a_package_annotations.exists(gc_context)) then
754+
l_next_endcontext_pos := get_next_annotation_of_type(a_context_ann_pos, gc_endcontext, a_package_annotations);
755+
l_next_context_pos := a_package_annotations(gc_context).next(a_context_ann_pos);
756+
end if;
757+
return ( l_next_context_pos < l_next_endcontext_pos );
758+
end;
759+
722760
function get_annotations_in_context(
723761
a_annotations t_annotations_info,
724762
a_context_pos t_annotation_position,
@@ -753,7 +791,8 @@ create or replace package body ut_suite_builder is
753791
a_parent in out nocopy ut_suite,
754792
a_annotations in out nocopy t_annotations_info,
755793
a_suite_items out nocopy ut_suite_items,
756-
a_parent_context_pos in integer := 0
794+
a_parent_context_pos in integer := 0,
795+
a_parent_end_context_pos in integer default null
757796
) is
758797
l_context_pos t_annotation_position;
759798
l_next_context_pos t_annotation_position;
@@ -768,26 +807,36 @@ create or replace package body ut_suite_builder is
768807
l_default_context_name t_object_name;
769808
function get_context_name(
770809
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
810+
a_start_position binary_integer
774811
) return varchar2 is
775812
l_result t_annotation_name;
776813
l_found boolean;
814+
l_end_position binary_integer;
777815
l_annotation_pos binary_integer;
816+
l_context_names tt_annotation_texts;
778817
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);
818+
if a_annotations.by_name.exists(gc_name) then
819+
l_context_names := a_annotations.by_name( gc_name );
820+
-- Maximum end-position to look for %name annotation is either the next %context or the next %endcontext annotation
821+
l_end_position :=
822+
least(
823+
coalesce( get_next_annotation_of_type(a_start_position, gc_endcontext, a_annotations.by_name), a_annotations.by_line.last ),
824+
coalesce( get_next_annotation_of_type(a_start_position, gc_context, a_annotations.by_name), a_annotations.by_line.last )
825+
);
826+
l_annotation_pos := l_context_names.first;
827+
828+
while l_annotation_pos is not null loop
829+
if l_annotation_pos > a_start_position and l_annotation_pos < l_end_position then
830+
if l_found then
831+
add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos);
832+
else
833+
l_result := l_context_names(l_annotation_pos);
834+
end if;
835+
l_found := true;
786836
end if;
787-
l_found := true;
788-
end if;
789-
l_annotation_pos := a_context_names.next(l_annotation_pos);
790-
end loop;
837+
l_annotation_pos := l_context_names.next(l_annotation_pos);
838+
end loop;
839+
end if;
791840
return l_result;
792841
end;
793842
begin
@@ -801,21 +850,9 @@ create or replace package body ut_suite_builder is
801850
while l_context_pos is not null loop
802851
l_default_context_name := 'nested_context_#'||l_context_no;
803852
l_context_name := null;
804-
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
805-
853+
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_line );
806854
l_next_context_pos := a_annotations.by_name(gc_context).next(l_context_pos);
807-
if a_annotations.by_name.exists(gc_name) then
808-
l_context_name :=
809-
get_context_name(
810-
a_parent,
811-
a_annotations.by_name( gc_name ),
812-
l_context_pos,
813-
least(
814-
coalesce( l_end_context_pos, a_annotations.by_line.last ),
815-
coalesce( l_next_context_pos, a_annotations.by_line.last )
816-
)
817-
);
818-
end if;
855+
l_context_name := get_context_name(a_parent, l_context_pos);
819856
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) or l_context_name is null then
820857
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) then
821858
a_parent.put_warning(
@@ -841,9 +878,8 @@ create or replace package body ut_suite_builder is
841878
l_context.parse_time := a_annotations.parse_time;
842879

843880
--if nested context found
844-
if l_next_context_pos < l_end_context_pos or l_end_context_pos is null then
845-
get_context_items( l_context, a_annotations, l_context_items, l_context_pos );
846-
l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
881+
if has_nested_context(l_context_pos, a_annotations.by_name) then
882+
get_context_items( l_context, a_annotations, l_context_items, l_context_pos, l_end_context_pos );
847883
else
848884
l_context_items := ut_suite_items();
849885
end if;
@@ -870,6 +906,10 @@ create or replace package body ut_suite_builder is
870906
exit when not a_annotations.by_name.exists( gc_context);
871907

872908
l_context_pos := a_annotations.by_name( gc_context).next( l_context_pos);
909+
-- don't go on when the next context is outside the parent's context boundaries
910+
if (a_parent_end_context_pos <= l_context_pos ) then
911+
l_context_pos := null;
912+
end if;
873913
l_context_no := l_context_no + 1;
874914
end loop;
875915
end;

test/ut3_tester/core/test_suite_builder.pkb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,74 @@ create or replace package body test_suite_builder is
767767
);
768768
end;
769769

770+
procedure nested_contexts_2 is
771+
l_actual clob;
772+
l_annotations ut3.ut_annotations;
773+
begin
774+
--Arrange
775+
l_annotations := ut3.ut_annotations(
776+
ut3.ut_annotation( 1, 'suite','Cool', null),
777+
ut3.ut_annotation( 2, 'suitepath','path', null),
778+
ut3.ut_annotation( 3, 'context','Level 1', null),
779+
ut3.ut_annotation( 4, 'name','context_1', null),
780+
ut3.ut_annotation( 5, 'context','Level 1.1', null),
781+
ut3.ut_annotation( 6, 'name','context_1_1', null),
782+
ut3.ut_annotation( 7, 'test', 'Test 1.1.1', 'test_1_1_1'),
783+
ut3.ut_annotation( 8, 'test', 'Test 1.1.2', 'test_1_1_2'),
784+
ut3.ut_annotation( 9, 'endcontext', null, null),
785+
ut3.ut_annotation(10, 'endcontext', null, null),
786+
ut3.ut_annotation(11, 'context','Level 2', null),
787+
ut3.ut_annotation(12, 'name','context_2', null),
788+
ut3.ut_annotation(13, 'test', 'Test 2.1', 'test_2_1'),
789+
ut3.ut_annotation(14, 'endcontext',null, null)
790+
);
791+
--Act
792+
l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
793+
--Assert
794+
ut.expect(l_actual).to_be_like(
795+
'<ROWSET><ROW>'||
796+
'<UT_LOGICAL_SUITE>' ||
797+
'%<ITEMS>%' ||
798+
'<UT_SUITE_ITEM>' ||
799+
'%<NAME>context_1</NAME><DESCRIPTION>Level 1</DESCRIPTION><PATH>path.some_package.context_1</PATH>' ||
800+
'%<ITEMS>' ||
801+
'<UT_SUITE_ITEM>' ||
802+
'%<NAME>context_1_1</NAME><DESCRIPTION>Level 1.1</DESCRIPTION><PATH>path.some_package.context_1.context_1_1</PATH>' ||
803+
'%<ITEMS>' ||
804+
'<UT_SUITE_ITEM>' ||
805+
'%<NAME>test_1_1_1</NAME><DESCRIPTION>Test 1.1.1</DESCRIPTION><PATH>path.some_package.context_1.context_1_1.test_1_1_1</PATH>' ||
806+
'%</UT_SUITE_ITEM>' ||
807+
'<UT_SUITE_ITEM>' ||
808+
'%<NAME>test_1_1_2</NAME><DESCRIPTION>Test 1.1.2</DESCRIPTION><PATH>path.some_package.context_1.context_1_1.test_1_1_2</PATH>' ||
809+
'%</UT_SUITE_ITEM>' ||
810+
'</ITEMS>' ||
811+
'%<BEFORE_ALL_LIST/>' ||
812+
'%</UT_SUITE_ITEM>' ||
813+
'</ITEMS>' ||
814+
'%</UT_SUITE_ITEM>' ||
815+
'%</ITEMS>' ||
816+
'%</UT_LOGICAL_SUITE>'||
817+
'</ROW></ROWSET>'
818+
);
819+
-- Test both contexts separately due to ordering
820+
ut.expect(l_actual).to_be_like(
821+
'<ROWSET><ROW>'||
822+
'<UT_LOGICAL_SUITE>' ||
823+
'%<ITEMS>%' ||
824+
'<UT_SUITE_ITEM>' ||
825+
'%<NAME>context_2</NAME><DESCRIPTION>Level 2</DESCRIPTION><PATH>path.some_package.context_2</PATH>' ||
826+
'%<ITEMS>' ||
827+
'<UT_SUITE_ITEM>' ||
828+
'%<NAME>test_2_1</NAME><DESCRIPTION>Test 2.1</DESCRIPTION><PATH>path.some_package.context_2.test_2_1</PATH>' ||
829+
'%</UT_SUITE_ITEM>' ||
830+
'%</ITEMS>' ||
831+
'%</UT_SUITE_ITEM>' ||
832+
'%</ITEMS>' ||
833+
'%</UT_LOGICAL_SUITE>'||
834+
'</ROW></ROWSET>'
835+
);
836+
end;
837+
770838

771839
procedure before_after_in_context is
772840
l_actual clob;

test/ut3_tester/core/test_suite_builder.pks

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ create or replace package test_suite_builder is
33
--%suitepath(utplsql.ut3_tester.core)
44

55
--%context(--%suite annotation)
6+
--%name(suite)
67

78
--%test(Sets suite name from package name and leaves description empty)
89
procedure no_suite_description;
@@ -105,13 +106,17 @@ create or replace package test_suite_builder is
105106
--%endcontext
106107

107108
--%context(--%context annotation)
109+
--%name(context)
108110

109111
--%test(Creates nested suite for content between context/endcontext annotations)
110112
procedure suite_from_context;
111113

112114
--%test(Creates nested contexts inside a context)
113115
procedure nested_contexts;
114116

117+
--%test(Creates multiple nested contexts inside a context)
118+
procedure nested_contexts_2;
119+
115120
--%test(Associates before/after all/each to tests in context only)
116121
procedure before_after_in_context;
117122

0 commit comments

Comments
 (0)