Skip to content
Merged
126 changes: 88 additions & 38 deletions source/core/ut_suite_builder.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -704,21 +704,69 @@ create or replace package body ut_suite_builder is
set_seq_no(a_suite.after_all_list);
end;

function get_endcontext_position(
a_context_ann_pos t_annotation_position,
function get_next_annotation_of_type(
a_start_position t_annotation_position,
a_annotation_type varchar2,
a_package_annotations in out nocopy tt_annotations_by_name
) return t_annotation_position is
l_result t_annotation_position;
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);
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
) return t_annotation_position is
l_next_endcontext_pos t_annotation_position;
l_next_context_pos t_annotation_position;
l_open_count integer := 0;
begin
if a_package_annotations.exists(gc_endcontext) and a_package_annotations.exists(gc_context) then
Comment thread
pesse marked this conversation as resolved.
Outdated
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);
Comment thread
pesse marked this conversation as resolved.
Outdated

loop
-- Get all the %context annotations between start and first %endcontext
while l_next_context_pos is not null and l_next_context_pos < l_next_endcontext_pos loop
l_open_count := l_open_count+1;
l_next_context_pos := a_package_annotations(gc_context).next(l_next_context_pos);
end loop;
-- Skip as many %endcontexts as we had additional contexts open
while l_open_count > 0 loop
l_open_count := l_open_count-1;
l_next_endcontext_pos := a_package_annotations(gc_endcontext).next(l_next_endcontext_pos);
end loop;
-- Repeat until the next %context is later than next %endcontext
exit when l_next_context_pos is null or l_next_context_pos > l_next_endcontext_pos;
Comment thread
pesse marked this conversation as resolved.
Outdated
end loop;
end if;
return l_next_endcontext_pos;
end;

function has_nested_context(
a_context_ann_pos t_annotation_position,
a_package_annotations in out nocopy tt_annotations_by_name
Comment thread
pesse marked this conversation as resolved.
Outdated
) return boolean is
l_next_endcontext_pos t_annotation_position;
l_next_context_pos t_annotation_position;
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);
if ( l_next_context_pos < l_next_endcontext_pos ) then
return true;
Comment thread
pesse marked this conversation as resolved.
Outdated
end if;
end if;
return false;
end;

function get_annotations_in_context(
a_annotations t_annotations_info,
a_context_pos t_annotation_position,
Expand Down Expand Up @@ -753,7 +801,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;
Expand All @@ -768,26 +817,35 @@ 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);
end if;
l_found := true;
end if;
l_annotation_pos := a_context_names.next(l_annotation_pos);
end loop;
if a_annotations.by_name.exists(gc_name) then
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really great.
Nice improvement to code readability and functionality.

l_context_names := a_annotations.by_name( gc_name );
l_end_position :=
least(
coalesce( get_endcontext_position(a_start_position, a_annotations.by_name), a_annotations.by_line.last ),
coalesce( a_annotations.by_name(gc_context).next(a_start_position), a_annotations.by_line.last )
Comment thread
pesse marked this conversation as resolved.
Outdated
);
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_annotation_pos := l_context_names.next(l_annotation_pos);
end loop;
end if;
return l_result;
end;
begin
Expand All @@ -802,20 +860,9 @@ create or replace package body ut_suite_builder is
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_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(
Expand All @@ -841,9 +888,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;
Expand All @@ -870,6 +916,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 is not null and a_parent_end_context_pos <= l_context_pos ) then
Comment thread
pesse marked this conversation as resolved.
Outdated
l_context_pos := null;
end if;
l_context_no := l_context_no + 1;
end loop;
end;
Expand Down
68 changes: 68 additions & 0 deletions test/ut3_tester/core/test_suite_builder.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add some tests in parent context of nested context before/after nested context.
I wonder if this will still work as expected.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that already done with nested_contexts test?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right

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(
'<ROWSET><ROW>'||
'<UT_LOGICAL_SUITE>' ||
'%<ITEMS>%' ||
'<UT_SUITE_ITEM>' ||
'%<NAME>context_1</NAME><DESCRIPTION>Level 1</DESCRIPTION><PATH>path.some_package.context_1</PATH>' ||
'%<ITEMS>' ||
'<UT_SUITE_ITEM>' ||
'%<NAME>context_1_1</NAME><DESCRIPTION>Level 1.1</DESCRIPTION><PATH>path.some_package.context_1.context_1_1</PATH>' ||
'%<ITEMS>' ||
'<UT_SUITE_ITEM>' ||
'%<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>' ||
'%</UT_SUITE_ITEM>' ||
'<UT_SUITE_ITEM>' ||
'%<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>' ||
'%</UT_SUITE_ITEM>' ||
'</ITEMS>' ||
'%<BEFORE_ALL_LIST/>' ||
'%</UT_SUITE_ITEM>' ||
'</ITEMS>' ||
'%</UT_SUITE_ITEM>' ||
'%</ITEMS>' ||
'%</UT_LOGICAL_SUITE>'||
'</ROW></ROWSET>'
);
-- Test both contexts separately due to ordering
ut.expect(l_actual).to_be_like(
'<ROWSET><ROW>'||
'<UT_LOGICAL_SUITE>' ||
'%<ITEMS>%' ||
'<UT_SUITE_ITEM>' ||
'%<NAME>context_2</NAME><DESCRIPTION>Level 2</DESCRIPTION><PATH>path.some_package.context_2</PATH>' ||
'%<ITEMS>' ||
'<UT_SUITE_ITEM>' ||
'%<NAME>test_2_1</NAME><DESCRIPTION>Test 2.1</DESCRIPTION><PATH>path.some_package.context_2.test_2_1</PATH>' ||
'%</UT_SUITE_ITEM>' ||
'%</ITEMS>' ||
'%</UT_SUITE_ITEM>' ||
'%</ITEMS>' ||
'%</UT_LOGICAL_SUITE>'||
'</ROW></ROWSET>'
);
end;


procedure before_after_in_context is
l_actual clob;
Expand Down
5 changes: 5 additions & 0 deletions test/ut3_tester/core/test_suite_builder.pks
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -105,13 +106,17 @@ create or replace package test_suite_builder is
--%endcontext

--%context(--%context annotation)
--%name(context)
Comment thread
pesse marked this conversation as resolved.
Outdated

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

--%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;

Expand Down