Skip to content

Commit

Permalink
Removed dependency on dbms_utility.name_resolve.
Browse files Browse the repository at this point in the history
Fixed issue with utPLSQL failing to run when schema contains package named the same as schema owning it.
It is now possible to have utPLSQL test package like this:
```
create or replace package some_schema.some_schema as
  --%suite

  --%test
  procedure sample_test;
end;
/

create or replace package body some_schema.some_schema as
   procedure sample_test is begin ut.expect(1).to_equal(1); end;
end;
/
```

The tests from the package can be invoked by calling:
`exec ut.run('some_schema.some_schema');`

If only schema is provided, all test packages in schema are executed.
`exec ut.run('some_schema');`

Resolves #569
Resolves #885
  • Loading branch information
jgebal committed Jun 15, 2019
1 parent 75d2053 commit 85942e9
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 79 deletions.
15 changes: 9 additions & 6 deletions source/core/types/ut_executable.tpb
Expand Up @@ -29,9 +29,13 @@ create or replace type body ut_executable is
return;
end;

member function form_name return varchar2 is
member function form_name(a_skip_current_user_schema boolean := false) return varchar2 is
l_owner_name varchar2(250) := owner_name;
begin
return ut_metadata.form_name(owner_name, object_name, procedure_name);
if a_skip_current_user_schema and sys_context('userenv', 'current_schema') = owner_name then
l_owner_name := null;
end if;
return ut_metadata.form_name(l_owner_name, object_name, procedure_name);
end;

member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) is
Expand Down Expand Up @@ -71,10 +75,9 @@ create or replace type body ut_executable is
begin

if not ut_metadata.package_valid(self.owner_name, self.object_name) then
self.error_stack := l_message_part || 'package does not exist or is invalid: ' ||upper(self.owner_name||'.'||self.object_name);
self.error_stack := l_message_part || 'package '||upper(self.owner_name||'.'||self.object_name)||' does not exist or is invalid.';
elsif not ut_metadata.procedure_exists(self.owner_name, self.object_name, self.procedure_name) then
self.error_stack := l_message_part || 'procedure does not exist '
|| upper(self.owner_name || '.' || self.object_name || '.' ||self.procedure_name);
self.error_stack := l_message_part || 'procedure '||upper(self.owner_name || '.' || self.object_name || '.' ||self.procedure_name)||' does not exist.';
else
l_result := false;
end if;
Expand Down Expand Up @@ -115,7 +118,7 @@ create or replace type body ut_executable is
' l_error_backtrace varchar2(32767);' || chr(10) ||
'begin' || chr(10) ||
' begin' || chr(10) ||
' ' || self.form_name() || ';' || chr(10) ||
' ' || self.form_name( a_skip_current_user_schema => true ) || ';' || chr(10) ||
' exception' || chr(10) ||
' when others then ' || chr(10) ||
' l_error_stack := dbms_utility.format_error_stack;' || chr(10) ||
Expand Down
2 changes: 1 addition & 1 deletion source/core/types/ut_executable.tps
Expand Up @@ -30,7 +30,7 @@ create or replace type ut_executable under ut_event_item(
*/
seq_no integer,
constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_executable_type varchar2) return self as result,
member function form_name return varchar2,
member function form_name(a_skip_current_user_schema boolean := false) return varchar2,
member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item),
/**
* executes the defines executable
Expand Down
74 changes: 17 additions & 57 deletions source/core/ut_metadata.pkb
Expand Up @@ -21,31 +21,6 @@ create or replace package body ut_metadata as
g_cached_object varchar2(500);
------------------------------
--public definitions

procedure do_resolve(a_owner in out nocopy varchar2, a_object in out nocopy varchar2, a_procedure_name in out nocopy varchar2) is
l_name varchar2(200);
l_context integer := 1; --plsql
begin
l_name := form_name(a_owner, a_object, a_procedure_name);
do_resolve(l_name,l_context,a_owner,a_object, a_procedure_name);
end do_resolve;

procedure do_resolve(a_fully_qualified_name in varchar2,a_context in integer,a_owner out nocopy varchar2, a_object out nocopy varchar2,
a_procedure_name out nocopy varchar2) is
l_dblink varchar2(200);
l_part1_type number;
l_object_number number;
begin
dbms_utility.name_resolve(name => a_fully_qualified_name
,context => a_context
,schema => a_owner
,part1 => a_object
,part2 => a_procedure_name
,dblink => l_dblink
,part1_type => l_part1_type
,object_number => l_object_number);
end;

function form_name(a_owner_name varchar2, a_object varchar2, a_subprogram varchar2 default null) return varchar2 is
l_name varchar2(200);
begin
Expand All @@ -61,25 +36,16 @@ create or replace package body ut_metadata as

function package_valid(a_owner_name varchar2, a_package_name in varchar2) return boolean as
l_cnt number;
l_schema varchar2(200);
l_package_name varchar2(200);
l_procedure_name varchar2(200);
l_view_name varchar2(200) := get_objects_view_name;
begin

l_schema := a_owner_name;
l_package_name := a_package_name;

do_resolve(l_schema, l_package_name, l_procedure_name);

execute immediate q'[select count(decode(status, 'VALID', 1, null)) / count(*)
execute immediate q'[select count(*)
from ]'||l_view_name||q'[
where owner = :l_schema
and object_name = :l_package_name
and object_type in ('PACKAGE')]'
into l_cnt using l_schema, l_package_name;

-- expect both package and body to be valid
where owner = :a_owner_name
and object_name = :a_package_name
and object_type = 'PACKAGE'
and status = 'VALID']'
into l_cnt using upper(a_owner_name), upper(a_package_name);
return l_cnt = 1;
exception
when others then
Expand All @@ -89,22 +55,12 @@ create or replace package body ut_metadata as
function procedure_exists(a_owner_name varchar2, a_package_name in varchar2, a_procedure_name in varchar2)
return boolean as
l_cnt number;
l_schema varchar2(200);
l_package_name varchar2(200);
l_procedure_name varchar2(200);
l_view_name varchar2(200) := get_dba_view('dba_procedures');
begin

l_schema := a_owner_name;
l_package_name := a_package_name;
l_procedure_name := a_procedure_name;

do_resolve(l_schema, l_package_name, l_procedure_name);

execute immediate
'select count(*) from '||l_view_name
||' where owner = :l_schema and object_name = :l_package_name and procedure_name = :l_procedure_name'
into l_cnt using l_schema, l_package_name, l_procedure_name;
||' where owner = :l_schema and object_name = :l_package_name and procedure_name = :l_procedure_name and rownum = 1'
into l_cnt using a_owner_name, a_package_name, a_procedure_name;

--expect one method only for the package with that name.
return l_cnt = 1;
Expand Down Expand Up @@ -327,12 +283,16 @@ create or replace package body ut_metadata as
end;

function get_object_name(a_full_object_name in varchar2) return varchar2 is
l_schema varchar2(250);
l_object varchar2(250);
l_procedure_name varchar2(250);
l_result varchar2(250);
begin
ut_metadata.do_resolve(a_full_object_name,7,l_schema,l_object, l_procedure_name);
return l_object;
l_result := regexp_substr(
a_full_object_name,
'^([A-Za-z0-9$#_]+|".*?")\.([A-Za-z0-9$#_]+|".*?")', subexpression => 2
);
if not l_result like '"%"' then
l_result := upper(l_result);
end if;
return l_result;
end;

function get_anydata_compound_type(a_data_value anydata) return varchar2 is
Expand Down
12 changes: 0 additions & 12 deletions source/core/ut_metadata.pks
Expand Up @@ -64,18 +64,6 @@ create or replace package ut_metadata authid current_user as
function procedure_exists(a_owner_name varchar2, a_package_name in varchar2, a_procedure_name in varchar2)
return boolean;

/**
* Resolves [owner.]object[.procedure] using dbms_utility.name_resolve and returns resolved parts
*
*/
procedure do_resolve(a_owner in out nocopy varchar2, a_object in out nocopy varchar2, a_procedure_name in out nocopy varchar2);

/**
* Resolves single string [owner.]object[.procedure] using dbms_utility.name_resolve and returns parts [owner] [object] [procedure]
*/
procedure do_resolve(a_fully_qualified_name in varchar2,a_context in integer,a_owner out nocopy varchar2,
a_object out nocopy varchar2, a_procedure_name out nocopy varchar2);

/**
* Return the text of the source line for a given object (body). It excludes package spec and type spec
*/
Expand Down
Expand Up @@ -282,8 +282,7 @@ create or replace package body test_before_after_annotations is
);
ut.expect(g_tests_results).to_match(
'1\) beforetest_missing_procedure\s+' ||
'Call params for beforetest are not valid: procedure does not exist ' ||
'UT3_TESTER.DUMMY_BEFORE_AFTER_TEST.NON_EXISTENT_PROCEDURE'
'Call params for beforetest are not valid: procedure UT3_TESTER\.DUMMY_BEFORE_AFTER_TEST\.NON_EXISTENT_PROCEDURE does not exist\.'
,'m'
);
end;
Expand Down
38 changes: 37 additions & 1 deletion test/ut3_user/api/test_ut_run.pkb
@@ -1,5 +1,7 @@
create or replace package body test_ut_run is

g_owner varchar2(250) := sys_context('userenv', 'current_schema');

procedure clear_expectations is
begin
ut3_tester_helper.main_helper.clear_expectations();
Expand Down Expand Up @@ -576,7 +578,7 @@ Failures:%
select * bulk collect into l_results from table(ut3.ut.run('failing_invalid_spec'));

l_actual := ut3_tester_helper.main_helper.table_to_clob(l_results);
ut.expect(l_actual).to_be_like('%Call params for % are not valid: package does not exist or is invalid: %FAILING_INVALID_SPEC%');
ut.expect(l_actual).to_be_like('%Call params for % are not valid: package %FAILING_INVALID_SPEC% does not exist or is invalid.%');

end;

Expand Down Expand Up @@ -739,6 +741,40 @@ Failures:%
end loop;
end;

procedure run_schema_name_test is
l_results ut3.ut_varchar2_list;
l_expected clob;
begin
select * bulk collect into l_results
from table ( ut3.ut.run( g_owner||'.'||g_owner ) );
l_expected := '%1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s)%';
ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( l_expected );
end;

procedure create_schema_name_package is
pragma autonomous_transaction;
begin
execute immediate '
create or replace package '||g_owner||'.'||g_owner||' as
--%suite

--%test
procedure sample_test;
end;';

execute immediate '
create or replace package body '||g_owner||'.'||g_owner||' as
procedure sample_test is begin ut.expect(1).to_equal(1); end;
end;';

end;

procedure drop_schema_name_package is
pragma autonomous_transaction;
begin
execute immediate 'drop package '||g_owner||'.'||g_owner;
end;

procedure run_with_random_order is
l_random_results ut3.ut_varchar2_list;
l_results ut3.ut_varchar2_list;
Expand Down
11 changes: 11 additions & 0 deletions test/ut3_user/api/test_ut_run.pks
Expand Up @@ -85,6 +85,7 @@ create or replace package test_ut_run is


--%context(ut_run_function)

--%displayname(ut.run() function options)
--%beforeall(create_ut3$user#_tests)
--%afterall(drop_ut3$user#_tests)
Expand Down Expand Up @@ -145,9 +146,18 @@ create or replace package test_ut_run is
procedure run_and_report_warnings;
procedure create_bad_annot;
procedure drop_bad_annot;

--%test(Can run test package that is named the same as schema name)
--%beforetest(create_schema_name_package)
--%aftertest(drop_schema_name_package)
procedure run_schema_name_test;
procedure create_schema_name_package;
procedure drop_schema_name_package;

--%endcontext

--%context(random_order)

--%displayname(Random test execution order)
--%beforeall(create_ut3$user#_tests)
--%afterall(drop_ut3$user#_tests)
Expand All @@ -164,6 +174,7 @@ create or replace package test_ut_run is
--%endcontext

--%context(run with tags)

--%displayname(Call ut.run with #tags)
--%beforeall(create_ut3$user#_tests)
--%afterall(drop_ut3$user#_tests)
Expand Down

0 comments on commit 85942e9

Please sign in to comment.