From 2a88bdcbffe0dccf7d8b35dcbdbf922d703a991e Mon Sep 17 00:00:00 2001 From: Jacek Date: Tue, 17 Oct 2017 01:59:23 +0100 Subject: [PATCH] Added buffering of dbms_output before the run. `dbms_output` buffer content is now cached before the run starts. The outcomes from the output are spooled back to the buffer at the end of the run. That results in the outputs getting shown in the right order when executing `ut.run` in a PLSQL block that has some buffered data already. Resolves #482 --- source/api/ut_runner.pkb | 3 ++ source/core/ut_dbms_output_cache.sql | 23 +++++++++++++ source/core/ut_utils.pkb | 51 ++++++++++++++++++++++++++++ source/core/ut_utils.pks | 15 ++++++++ source/install.sql | 3 ++ source/uninstall.sql | 2 ++ test/ut_runner/test_ut_runner.pkb | 28 +++++++++++++++ test/ut_runner/test_ut_runner.pks | 3 ++ 8 files changed, 128 insertions(+) create mode 100644 source/core/ut_dbms_output_cache.sql diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index e3141b527..fcf44ba7b 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -66,6 +66,7 @@ create or replace package body ut_runner is begin begin ut_output_buffer.cleanup_buffer(); + ut_utils.save_dbms_output_to_cache(); ut_console_reporter_base.set_color_enabled(a_color_console); if a_reporters is null or a_reporters.count = 0 then @@ -87,11 +88,13 @@ create or replace package body ut_runner is ut_utils.cleanup_temp_tables; ut_output_buffer.close(l_listener.reporters); ut_metadata.reset_source_definition_cache; + ut_utils.read_cache_to_dbms_output(); exception when others then ut_utils.cleanup_temp_tables; ut_output_buffer.close(l_listener.reporters); ut_metadata.reset_source_definition_cache; + ut_utils.read_cache_to_dbms_output(); dbms_output.put_line(dbms_utility.format_error_backtrace); dbms_output.put_line(dbms_utility.format_error_stack); raise; diff --git a/source/core/ut_dbms_output_cache.sql b/source/core/ut_dbms_output_cache.sql new file mode 100644 index 000000000..4ac1a9191 --- /dev/null +++ b/source/core/ut_dbms_output_cache.sql @@ -0,0 +1,23 @@ +create global temporary table ut_dbms_output_cache( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 utPLSQL Project + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + /* + * This table is not a global temporary table as it needs to allow cross-session data exchange + * It is used however as a temporary table with multiple writers. + * This is why it has very high initrans and has nologging + */ + seq_no number(20,0) not null, + text varchar2(4000), + constraint ut_dbms_output_cache_pk primary key(seq_no) +) on commit preserve rows; diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index ceb962f23..83edb74a4 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -397,5 +397,56 @@ create or replace package body ut_utils is end if; return l_result; end; + + procedure save_dbms_output_to_cache is + l_status number; + l_line varchar2(32767); + l_line_no integer := 1; + l_lines ut_varchar2_rows := ut_varchar2_rows(); + c_lines_limit constant integer := 100; + pragma autonomous_transaction; + + procedure flush_lines is + begin + insert into ut_dbms_output_cache (seq_no,text) + select rownum, column_value + from table(l_lines); + l_lines.delete; + end; + begin + loop + dbms_output.get_line(line => l_line, status => l_status); + exit when l_status = 1; + l_lines := l_lines multiset union all ut_utils.convert_collection(ut_utils.clob_to_table(l_line||chr(7),4000)); + if l_lines.count > c_lines_limit then + flush_lines(); + end if; + end loop; + flush_lines(); + commit; + end; + + procedure read_cache_to_dbms_output is + l_lines_data sys_refcursor; + l_lines ut_varchar2_rows; + c_lines_limit constant integer := 100; + pragma autonomous_transaction; + begin + open l_lines_data for select text from ut_dbms_output_cache order by seq_no; + loop + fetch l_lines_data bulk collect into l_lines limit c_lines_limit; + for i in 1 .. l_lines.count loop + if substr(l_lines(i),-1) = chr(7) then + dbms_output.put_line(rtrim(l_lines(i),chr(7))); + else + dbms_output.put(l_lines(i)); + end if; + end loop; + exit when l_lines_data%notfound; + end loop; + delete from ut_dbms_output_cache; + commit; + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 4d2c2c070..fe7b73a69 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -257,5 +257,20 @@ create or replace package ut_utils authid definer is */ function to_version(a_version_no varchar2) return t_version; + + /** + * Saves data from dbms_output buffer into a global temporary table (cache) + * used to store dbms_output buffer captured before the run + * + */ + procedure save_dbms_output_to_cache; + + /** + * Reads data from global temporary table (cache) abd puts it back into dbms_output + * used to recover dbms_output buffer data after a run is complete + * + */ + procedure read_cache_to_dbms_output; + end ut_utils; / diff --git a/source/install.sql b/source/install.sql index 71bc70eeb..bd19576d3 100644 --- a/source/install.sql +++ b/source/install.sql @@ -32,6 +32,9 @@ alter session set current_schema = &&ut3_owner; alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6003,6009,6010,7206)'; --set define off +--dbms_output buffer cache table +@@install_component.sql 'core/ut_dbms_output_cache.sql' + --common utilities @@install_component.sql 'core/types/ut_varchar2_list.tps' @@install_component.sql 'core/types/ut_varchar2_rows.tps' diff --git a/source/uninstall.sql b/source/uninstall.sql index 48aba7d04..a786fb330 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -78,6 +78,8 @@ drop package ut_suite_manager; drop package ut; +drop table ut_dbms_output_cache; + drop type ut_expectation_yminterval force; drop type ut_expectation_varchar2 force; diff --git a/test/ut_runner/test_ut_runner.pkb b/test/ut_runner/test_ut_runner.pkb index 5c8e4fb26..b6f958413 100644 --- a/test/ut_runner/test_ut_runner.pkb +++ b/test/ut_runner/test_ut_runner.pkb @@ -97,5 +97,33 @@ end;'; drop_test_package(); end; + procedure run_keep_dbms_output_buffer is + l_expected dbmsoutput_linesarray; + l_actual dbmsoutput_linesarray; + l_lines number := 100; + begin + --Arrange + create_test_spec(); + create_test_body(0); + l_expected := dbmsoutput_linesarray( + 'A text placed into DBMS_OUTPUT', + 'Another line', + lpad('A very long line',10000,'a') + ); + dbms_output.enable; + dbms_output.put_line(l_expected(1)); + dbms_output.put_line(l_expected(2)); + dbms_output.put_line(l_expected(3)); + --Act + ut3.ut.run('test_cache'); + + --Assert + dbms_output.get_lines(lines => l_actual, numlines => l_lines); + for i in 1 .. l_expected.count loop + ut.expect(l_actual(i)).to_equal(l_expected(i)); + end loop; + drop_test_package(); + end; + end; / diff --git a/test/ut_runner/test_ut_runner.pks b/test/ut_runner/test_ut_runner.pks index 993cd1770..06a212a8a 100644 --- a/test/ut_runner/test_ut_runner.pks +++ b/test/ut_runner/test_ut_runner.pks @@ -18,5 +18,8 @@ create or replace package test_ut_runner is --%test(run resets cache of package body after every run) procedure run_reset_package_body_cache; + --%test(does not consume dbms_output from before the run) + procedure run_keep_dbms_output_buffer; + end; /