diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb
index c7e968467..5f468f756 100644
--- a/source/core/ut_suite_builder.pkb
+++ b/source/core/ut_suite_builder.pkb
@@ -15,7 +15,7 @@ create or replace package body ut_suite_builder is
See the License for the specific language governing permissions and
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);
@@ -315,13 +315,13 @@ create or replace package body ut_suite_builder is
l_annotation_pos := a_throws_ann_text.next(l_annotation_pos);
end loop;
end;
-
+
procedure add_tags_to_suite_item(
a_suite in out nocopy ut_suite,
a_tags_ann_text tt_annotation_texts,
a_list in out nocopy ut_varchar2_rows,
a_procedure_name t_object_name := null
- ) is
+ ) is
l_annotation_pos binary_integer;
l_tags_list ut_varchar2_list := ut_varchar2_list();
l_tag_items ut_varchar2_list;
@@ -354,7 +354,7 @@ create or replace package body ut_suite_builder is
--remove empty strings from table list e.g. tag1,,tag2 and convert to rows
a_list := ut_utils.convert_collection( ut_utils.filter_list(set(l_tags_list),ut_utils.gc_word_no_space) );
end;
-
+
procedure set_seq_no(
a_list in out nocopy ut_executables
) is
@@ -533,16 +533,16 @@ create or replace package body ut_suite_builder is
);
set_seq_no(l_test.after_test_list);
end if;
-
+
if l_proc_annotations.exists( gc_tags) then
add_tags_to_suite_item(a_suite, l_proc_annotations( gc_tags), l_test.tags, a_procedure_name);
end if;
-
+
if l_proc_annotations.exists( gc_throws) then
add_to_throws_numbers_list(a_suite, l_test.expected_error_codes, a_procedure_name, l_proc_annotations( gc_throws));
end if;
l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled));
-
+
a_suite_items.extend;
a_suite_items( a_suite_items.last ) := l_test;
@@ -687,7 +687,7 @@ create or replace package body ut_suite_builder is
if a_annotations.by_name.exists(gc_aftereach) then
l_after_each_list := add_executables( a_suite.object_owner, a_suite.object_name, a_annotations.by_name(gc_aftereach), gc_aftereach );
end if;
-
+
if a_annotations.by_name.exists(gc_tags) then
add_tags_to_suite_item(a_suite, a_annotations.by_name(gc_tags),a_suite.tags);
end if;
@@ -704,21 +704,59 @@ create or replace package body ut_suite_builder is
set_seq_no(a_suite.after_all_list);
end;
+ function get_next_annotation_of_type(
+ a_start_position t_annotation_position,
+ a_annotation_type varchar2,
+ a_package_annotations in tt_annotations_by_name
+ ) return t_annotation_position is
+ l_result t_annotation_position;
+ begin
+ if a_package_annotations.exists(a_annotation_type) then
+ l_result := a_package_annotations(a_annotation_type).first;
+ while l_result <= a_start_position loop
+ l_result := a_package_annotations(a_annotation_type).next(l_result);
+ end loop;
+ end if;
+ return l_result;
+ end;
+
function get_endcontext_position(
a_context_ann_pos t_annotation_position,
- a_package_annotations in out nocopy tt_annotations_by_name
+ a_package_annotations in tt_annotations_by_line
) return t_annotation_position is
l_result t_annotation_position;
+ l_open_count integer := 1;
+ l_idx t_annotation_position := a_package_annotations.next(a_context_ann_pos);
begin
- if a_package_annotations.exists(gc_endcontext) then
- l_result := a_package_annotations(gc_endcontext).first;
- while l_result <= a_context_ann_pos loop
- l_result := a_package_annotations(gc_endcontext).next(l_result);
- end loop;
+ while l_open_count > 0 and l_idx is not null loop
+ if ( a_package_annotations(l_idx).name = gc_context ) then
+ l_open_count := l_open_count+1;
+ elsif ( a_package_annotations(l_idx).name = gc_endcontext ) then
+ l_open_count := l_open_count-1;
+ l_result := l_idx;
+ end if;
+ l_idx := a_package_annotations.next(l_idx);
+ end loop;
+ if ( l_open_count > 0 ) then
+ l_result := null;
end if;
return l_result;
end;
+ function has_nested_context(
+ a_context_ann_pos t_annotation_position,
+ a_package_annotations in tt_annotations_by_name
+ ) return boolean is
+ l_next_endcontext_pos t_annotation_position := 0;
+ l_next_context_pos t_annotation_position := 0;
+ begin
+ if ( a_package_annotations.exists(gc_endcontext) and a_package_annotations.exists(gc_context)) then
+ l_next_endcontext_pos := get_next_annotation_of_type(a_context_ann_pos, gc_endcontext, a_package_annotations);
+ l_next_context_pos := a_package_annotations(gc_context).next(a_context_ann_pos);
+ end if;
+ return ( l_next_context_pos < l_next_endcontext_pos );
+ end;
+
function get_annotations_in_context(
a_annotations t_annotations_info,
a_context_pos t_annotation_position,
@@ -753,7 +791,8 @@ create or replace package body ut_suite_builder is
a_parent in out nocopy ut_suite,
a_annotations in out nocopy t_annotations_info,
a_suite_items out nocopy ut_suite_items,
- a_parent_context_pos in integer := 0
+ a_parent_context_pos in integer := 0,
+ a_parent_end_context_pos in integer default null
) is
l_context_pos t_annotation_position;
l_next_context_pos t_annotation_position;
@@ -768,26 +807,36 @@ create or replace package body ut_suite_builder is
l_default_context_name t_object_name;
function get_context_name(
a_parent in out nocopy ut_suite,
- a_context_names in tt_annotation_texts,
- a_start_position binary_integer,
- a_end_position binary_integer
+ a_start_position binary_integer
) return varchar2 is
l_result t_annotation_name;
l_found boolean;
+ l_end_position binary_integer;
l_annotation_pos binary_integer;
+ l_context_names tt_annotation_texts;
begin
- l_annotation_pos := a_context_names.first;
- while l_annotation_pos is not null loop
- if l_annotation_pos > a_start_position and l_annotation_pos < a_end_position then
- if l_found then
- add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos);
- else
- l_result := a_context_names(l_annotation_pos);
+ if a_annotations.by_name.exists(gc_name) then
+ l_context_names := a_annotations.by_name( gc_name );
+ -- Maximum end-position to look for %name annotation is either the next %context or the next %endcontext annotation
+ l_end_position :=
+ least(
+ coalesce( get_next_annotation_of_type(a_start_position, gc_endcontext, a_annotations.by_name), a_annotations.by_line.last ),
+ coalesce( get_next_annotation_of_type(a_start_position, gc_context, a_annotations.by_name), a_annotations.by_line.last )
+ );
+ l_annotation_pos := l_context_names.first;
+
+ while l_annotation_pos is not null loop
+ if l_annotation_pos > a_start_position and l_annotation_pos < l_end_position then
+ if l_found then
+ add_annotation_ignored_warning(a_parent, gc_name,'Duplicate annotation %%%.', l_annotation_pos);
+ else
+ l_result := l_context_names(l_annotation_pos);
+ end if;
+ l_found := true;
end if;
- l_found := true;
- end if;
- l_annotation_pos := a_context_names.next(l_annotation_pos);
- end loop;
+ l_annotation_pos := l_context_names.next(l_annotation_pos);
+ end loop;
+ end if;
return l_result;
end;
begin
@@ -801,21 +850,9 @@ create or replace package body ut_suite_builder is
while l_context_pos is not null loop
l_default_context_name := 'nested_context_#'||l_context_no;
l_context_name := null;
- l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
-
+ l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_line );
l_next_context_pos := a_annotations.by_name(gc_context).next(l_context_pos);
- if a_annotations.by_name.exists(gc_name) then
- l_context_name :=
- get_context_name(
- a_parent,
- a_annotations.by_name( gc_name ),
- l_context_pos,
- least(
- coalesce( l_end_context_pos, a_annotations.by_line.last ),
- coalesce( l_next_context_pos, a_annotations.by_line.last )
- )
- );
- end if;
+ l_context_name := get_context_name(a_parent, l_context_pos);
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) or l_context_name is null then
if not regexp_like( l_context_name, '^(\w|[$#])+$' ) then
a_parent.put_warning(
@@ -841,9 +878,8 @@ create or replace package body ut_suite_builder is
l_context.parse_time := a_annotations.parse_time;
--if nested context found
- if l_next_context_pos < l_end_context_pos or l_end_context_pos is null then
- get_context_items( l_context, a_annotations, l_context_items, l_context_pos );
- l_end_context_pos := get_endcontext_position(l_context_pos, a_annotations.by_name );
+ if has_nested_context(l_context_pos, a_annotations.by_name) then
+ get_context_items( l_context, a_annotations, l_context_items, l_context_pos, l_end_context_pos );
else
l_context_items := ut_suite_items();
end if;
@@ -870,6 +906,10 @@ create or replace package body ut_suite_builder is
exit when not a_annotations.by_name.exists( gc_context);
l_context_pos := a_annotations.by_name( gc_context).next( l_context_pos);
+ -- don't go on when the next context is outside the parent's context boundaries
+ if (a_parent_end_context_pos <= l_context_pos ) then
+ l_context_pos := null;
+ end if;
l_context_no := l_context_no + 1;
end loop;
end;
diff --git a/test/ut3_tester/core/test_suite_builder.pkb b/test/ut3_tester/core/test_suite_builder.pkb
index a204a04e6..86d86032a 100644
--- a/test/ut3_tester/core/test_suite_builder.pkb
+++ b/test/ut3_tester/core/test_suite_builder.pkb
@@ -767,6 +767,74 @@ create or replace package body test_suite_builder is
);
end;
+ procedure nested_contexts_2 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', null),
+ ut3.ut_annotation( 3, 'context','Level 1', null),
+ ut3.ut_annotation( 4, 'name','context_1', null),
+ ut3.ut_annotation( 5, 'context','Level 1.1', null),
+ ut3.ut_annotation( 6, 'name','context_1_1', null),
+ ut3.ut_annotation( 7, 'test', 'Test 1.1.1', 'test_1_1_1'),
+ ut3.ut_annotation( 8, 'test', 'Test 1.1.2', 'test_1_1_2'),
+ ut3.ut_annotation( 9, 'endcontext', null, null),
+ ut3.ut_annotation(10, 'endcontext', null, null),
+ ut3.ut_annotation(11, 'context','Level 2', null),
+ ut3.ut_annotation(12, 'name','context_2', null),
+ ut3.ut_annotation(13, 'test', 'Test 2.1', 'test_2_1'),
+ ut3.ut_annotation(14, 'endcontext',null, null)
+ );
+ --Act
+ l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE');
+ --Assert
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%%' ||
+ '' ||
+ '%context_1Level 1path.some_package.context_1' ||
+ '%' ||
+ '' ||
+ '%context_1_1Level 1.1path.some_package.context_1.context_1_1' ||
+ '%' ||
+ '' ||
+ '%test_1_1_1Test 1.1.1path.some_package.context_1.context_1_1.test_1_1_1' ||
+ '%' ||
+ '' ||
+ '%test_1_1_2Test 1.1.2path.some_package.context_1.context_1_1.test_1_1_2' ||
+ '%' ||
+ '' ||
+ '%' ||
+ '%' ||
+ '' ||
+ '%' ||
+ '%' ||
+ '%'||
+ '
'
+ );
+ -- Test both contexts separately due to ordering
+ ut.expect(l_actual).to_be_like(
+ ''||
+ '' ||
+ '%%' ||
+ '' ||
+ '%context_2Level 2path.some_package.context_2' ||
+ '%' ||
+ '' ||
+ '%test_2_1Test 2.1path.some_package.context_2.test_2_1' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ '%' ||
+ '%'||
+ '
'
+ );
+ end;
+
procedure before_after_in_context is
l_actual clob;
diff --git a/test/ut3_tester/core/test_suite_builder.pks b/test/ut3_tester/core/test_suite_builder.pks
index 7a85cb946..7c7fc77f6 100644
--- a/test/ut3_tester/core/test_suite_builder.pks
+++ b/test/ut3_tester/core/test_suite_builder.pks
@@ -3,6 +3,7 @@ create or replace package test_suite_builder is
--%suitepath(utplsql.ut3_tester.core)
--%context(--%suite annotation)
+ --%name(suite)
--%test(Sets suite name from package name and leaves description empty)
procedure no_suite_description;
@@ -105,6 +106,7 @@ create or replace package test_suite_builder is
--%endcontext
--%context(--%context annotation)
+ --%name(context)
--%test(Creates nested suite for content between context/endcontext annotations)
procedure suite_from_context;
@@ -112,6 +114,9 @@ create or replace package test_suite_builder is
--%test(Creates nested contexts inside a context)
procedure nested_contexts;
+ --%test(Creates multiple nested contexts inside a context)
+ procedure nested_contexts_2;
+
--%test(Associates before/after all/each to tests in context only)
procedure before_after_in_context;