diff --git a/docs/images/tap_reporter_colored.png b/docs/images/tap_reporter_colored.png new file mode 100644 index 000000000..d4a5e9326 Binary files /dev/null and b/docs/images/tap_reporter_colored.png differ diff --git a/docs/images/tap_reporter_no_color.png b/docs/images/tap_reporter_no_color.png new file mode 100644 index 000000000..e7379a889 Binary files /dev/null and b/docs/images/tap_reporter_no_color.png differ diff --git a/docs/images/tap_reporter_suitepath.png b/docs/images/tap_reporter_suitepath.png new file mode 100644 index 000000000..488c7cfbf Binary files /dev/null and b/docs/images/tap_reporter_suitepath.png differ diff --git a/docs/userguide/reporters.md b/docs/userguide/reporters.md index 24d1065fe..1e4db4141 100644 --- a/docs/userguide/reporters.md +++ b/docs/userguide/reporters.md @@ -126,6 +126,20 @@ Details: utPLSQL comes with a set of build-in coverage reporters. [Code coverage](coverage.md) section describes in details how to use configure and use code coverage. +## TAP Reporter + +The `ut_tap_reporter` produces output compatible with the [Test Anything Protocol](https://testanything.org) (Version 14). TAP output can be consumed by a TAP consumer that can aggregate results from testsuites across different programming languages while maintaining good readability for humans. + +![tap_output_no_color](../images/tap_reporter_no_color.png) + +If you use a compatible terminal, you can also have a colored result. Only top level `not ok`-results will be colored: + +![tap_colored_output](../images/tap_reporter_colored.png) + +Suites (and suitepaths) and contexts are included as [commented subtests](https://testanything.org/tap-version-14-specification.html#:~:text=Commented%20Subtests) including a summary of the tests performed for that specific context/suite. This example package has the suitepath "org.utplsql.tests.helpers": + +![tap_include_suitepath](../images/tap_reporter_suitepath.png) + ## Debug reporter The `ut_debug_reporter` provides a highly verbose output containing thorough details about framework and test execution. diff --git a/source/create_grants.sql b/source/create_grants.sql index 0e9f46cc5..2cdea9970 100644 --- a/source/create_grants.sql +++ b/source/create_grants.sql @@ -104,6 +104,7 @@ grant execute on &&ut3_owner..ut_tfs_junit_reporter to &ut3_user; grant execute on &&ut3_owner..ut_documentation_reporter to &ut3_user; grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user; grant execute on &&ut3_owner..ut_realtime_reporter to &ut3_user; +grant execute on &&ut3_owner..ut_tap_reporter to &ut3_user; --reporters - coverage grant execute on &&ut3_owner..ut_coverage_html_reporter to &ut3_user; grant execute on &&ut3_owner..ut_coverage_sonar_reporter to &ut3_user; diff --git a/source/create_synonyms.sql b/source/create_synonyms.sql index fa1c1dc45..d839e11b7 100644 --- a/source/create_synonyms.sql +++ b/source/create_synonyms.sql @@ -119,6 +119,7 @@ create &action_type. synonym &ut3_user.ut_tfs_junit_reporter for &&ut3_owner..ut create &action_type. synonym &ut3_user.ut_documentation_reporter for &&ut3_owner..ut_documentation_reporter; create &action_type. synonym &ut3_user.ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter; create &action_type. synonym &ut3_user.ut_realtime_reporter for &&ut3_owner..ut_realtime_reporter; +create &action_type. synonym &ut3_user.ut_tap_reporter for &&ut3_owner..ut_tap_reporter; --reporters - coverage create &action_type. synonym &ut3_user.ut_coverage_html_reporter for &&ut3_owner..ut_coverage_html_reporter; create &action_type. synonym &ut3_user.ut_coverage_sonar_reporter for &&ut3_owner..ut_coverage_sonar_reporter; diff --git a/source/install.sql b/source/install.sql index 827213e6c..22ce75365 100644 --- a/source/install.sql +++ b/source/install.sql @@ -346,6 +346,8 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema @@install_component.sql 'reporters/ut_xunit_reporter.tpb' @@install_component.sql 'reporters/ut_sonar_test_reporter.tps' @@install_component.sql 'reporters/ut_sonar_test_reporter.tpb' +@@install_component.sql 'reporters/ut_tap_reporter.tps' +@@install_component.sql 'reporters/ut_tap_reporter.tpb' @@install_component.sql 'reporters/ut_coverage_html_reporter.tps' @@install_component.sql 'reporters/ut_coverage_report_html_helper.pks' diff --git a/source/reporters/ut_tap_reporter.tpb b/source/reporters/ut_tap_reporter.tpb new file mode 100644 index 000000000..ff095a369 --- /dev/null +++ b/source/reporters/ut_tap_reporter.tpb @@ -0,0 +1,124 @@ +create or replace type body ut_tap_reporter is + + + constructor function ut_tap_reporter(self in out nocopy ut_tap_reporter) return self as result is + begin + self.init($$plsql_unit); + self.lvl := 0; + return; + end ut_tap_reporter; + + member procedure print_comment(self in out nocopy ut_tap_reporter, a_comment clob) as + begin + self.print_clob(regexp_replace(a_comment, '^', '# ', 1, 0, 'm')); + end print_comment; + + member function escape_special_chars(self in out nocopy ut_tap_reporter, a_string_to_escape clob) return clob as + begin + return regexp_replace(a_string_to_escape, '([\\#])', '\\\1'); + end escape_special_chars; + + overriding member procedure before_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite) as + begin + self.print_text('# Subtest: ' || self.escape_special_chars(coalesce(a_suite.description, a_suite.name))); + lvl := lvl + 2; + self.print_text('1..' || a_suite.items.count); + end before_calling_suite; + + + overriding member procedure after_calling_test(self in out nocopy ut_tap_reporter, a_test ut_test) as + l_message varchar2(4000); + l_test_name varchar2(4000) := self.escape_special_chars(coalesce(a_test.description, a_test.name)); + + procedure print_failed_expectation(a_test ut_test) is + l_lines ut_varchar2_list; + l_failed boolean; + begin + if a_test.get_error_stack_traces().count = 0 then + -- If no error occurred, print failed expectation + l_lines := a_test.all_expectations(a_test.all_expectations.last).get_result_lines(); + l_failed := a_test.all_expectations(a_test.all_expectations.last).status >= ut_utils.gc_success; + if l_failed then + self.print_text('message: ''' || l_lines(1) || ''''); + self.print_text('severity: fail'); + end if; + else + -- Print multi-line YAML-String with implicit newline characters + self.print_text('message: |'); + self.lvl := self.lvl + 1; + self.print_text(ut_utils.table_to_clob(a_test.get_error_stack_traces())); + self.lvl := self.lvl - 1; + self.print_text('severity: error'); + end if; + end print_failed_expectation; + + begin + + if a_test.result = ut_utils.gc_disabled then + self.print_text('ok - ' || l_test_name || ' # SKIP'|| + case when a_test.disabled_reason is not null + then ': '|| self.escape_special_chars(a_test.disabled_reason) + else null + end ); + elsif a_test.result = ut_utils.gc_success then + self.print_text('ok - ' || l_test_name); + elsif a_test.result > ut_utils.gc_success then + if self.lvl = 0 then + self.print_text(ut_ansiconsole_helper.red('not ok') || ' - ' || l_test_name); + else + self.print_text('not ok - ' || l_test_name); + end if; + self.lvl := self.lvl + 1; + self.print_text('---'); + print_failed_expectation(a_test); + self.print_text('...'); + self.lvl := self.lvl - 1; + end if; + + self.print_comment(a_test.get_serveroutputs); + + end after_calling_test; + + overriding member procedure after_calling_before_all(self in out nocopy ut_tap_reporter, a_executable in ut_executable) is + begin + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_comment(a_executable.serveroutput); + end if; + end after_calling_before_all; + + overriding member procedure after_calling_after_all(self in out nocopy ut_tap_reporter, a_executable in ut_executable) is + begin + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_comment(a_executable.serveroutput); + end if; + end after_calling_after_all; + + overriding member procedure after_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite) as + l_suite_name varchar2(4000) := coalesce(a_suite.description, a_suite.name); + begin + lvl := lvl - 2; + if lvl = 0 then + if a_suite.result = ut_utils.gc_success or a_suite.result = ut_utils.gc_disabled then + self.print_text('ok - ' || self.escape_special_chars(l_suite_name)); + elsif a_suite.result > ut_utils.gc_success then + self.print_text(ut_ansiconsole_helper.red('not ok') || ' - ' || self.escape_special_chars(l_suite_name)); + end if; + + self.print_text(' '); + end if; + + end after_calling_suite; + + overriding member procedure before_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) as + begin + self.print_text('TAP version 14'); + self.print_text('1..' || a_run.items.count); + self.print_text(' '); + end before_calling_run; + + overriding member procedure after_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) as + begin + self.lvl := 0; + end; +end; +/ diff --git a/source/reporters/ut_tap_reporter.tps b/source/reporters/ut_tap_reporter.tps new file mode 100644 index 000000000..bed809059 --- /dev/null +++ b/source/reporters/ut_tap_reporter.tps @@ -0,0 +1,18 @@ +create or replace type ut_tap_reporter under ut_documentation_reporter( + + constructor function ut_tap_reporter(self in out nocopy ut_tap_reporter) return self as result, + member procedure print_comment(self in out nocopy ut_tap_reporter, a_comment clob), + member function escape_special_chars(self in out nocopy ut_tap_reporter, a_string_to_escape clob) return clob, + overriding member procedure before_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite), + + overriding member procedure after_calling_test(self in out nocopy ut_tap_reporter, a_test ut_test), + + overriding member procedure after_calling_before_all (self in out nocopy ut_tap_reporter, a_executable in ut_executable), + overriding member procedure after_calling_after_all (self in out nocopy ut_tap_reporter, a_executable in ut_executable), + overriding member procedure before_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run), + overriding member procedure after_calling_suite(self in out nocopy ut_tap_reporter, a_suite ut_logical_suite), + overriding member procedure after_calling_run(self in out nocopy ut_tap_reporter, a_run in ut_run) + +) +not final +/ diff --git a/test/install_ut3_user_tests.sql b/test/install_ut3_user_tests.sql index ad7f014bc..518e07841 100644 --- a/test/install_ut3_user_tests.sql +++ b/test/install_ut3_user_tests.sql @@ -43,6 +43,7 @@ set define off @@ut3_user/reporters/test_documentation_reporter.pks @@ut3_user/reporters/test_debug_reporter.pks @@ut3_user/reporters/test_realtime_reporter.pks +@@ut3_user/reporters/test_tap_reporter.pks @@ut3_user/reporters/test_coverage.pks @@ut3_user/reporters/test_coverage/test_coverage_standalone.pks set define on @@ -86,6 +87,7 @@ set define off @@ut3_user/reporters/test_documentation_reporter.pkb @@ut3_user/reporters/test_debug_reporter.pkb @@ut3_user/reporters/test_realtime_reporter.pkb +@@ut3_user/reporters/test_tap_reporter.pkb @@ut3_user/reporters/test_coverage/test_coverage_standalone.pkb set define on @@ut3_user/reporters/test_coverage/test_extended_coverage.pkb diff --git a/test/ut3_user/api/test_ut_runner.pkb b/test/ut3_user/api/test_ut_runner.pkb index b6b7c67e7..d665718e1 100644 --- a/test/ut3_user/api/test_ut_runner.pkb +++ b/test/ut3_user/api/test_ut_runner.pkb @@ -393,7 +393,8 @@ end;'; select 'UT3_DEVELOP.UT_SONAR_TEST_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_TEAMCITY_REPORTER', 'Y' from dual union all select 'UT3_DEVELOP.UT_TFS_JUNIT_REPORTER', 'Y' from dual union all - select 'UT3_DEVELOP.UT_XUNIT_REPORTER', 'Y' from dual + select 'UT3_DEVELOP.UT_XUNIT_REPORTER', 'Y' from dual union all + select 'UT3_DEVELOP.UT_TAP_REPORTER', 'Y' from dual order by 1; --Act open l_actual for select * from table(ut3_develop.ut_runner.GET_REPORTERS_LIST()) order by 1; diff --git a/test/ut3_user/reporters/test_tap_reporter.pkb b/test/ut3_user/reporters/test_tap_reporter.pkb new file mode 100644 index 000000000..786c53ee0 --- /dev/null +++ b/test/ut3_user/reporters/test_tap_reporter.pkb @@ -0,0 +1,254 @@ +create or replace package body test_tap_reporter as + + gc_boilerplate_suitepath_expression constant varchar2(300) := 'TAP version 14\s*1..1\s*# Subtest: org\s{5}1..1\s{5}# Subtest: utplsql\s{9}1..1\s{9}# Subtest: tests\s{13}1..1\s{13}# Subtest: helpers\s{17}1..1\s{17}# Subtest: A suite for testing different outcomes from reporters'; + + + procedure compile_tests as + pragma autonomous_transaction; + begin + + execute immediate q'[ + create or replace package test_tap_escaping as + --%suite(Some \ and # to be escaped) + + --%test(Even more \\ and multiple ###) + procedure more_escaping; + + --%test(Disabled test) + --%disabled(With \ and # in skip reason) + procedure not_skipping_escapes; + + --%test(Escaped Comments) + procedure escaped_comments; + + --%context(Some context) + + --%test(Another disabled test) + --%disabled + procedure another_disabled_test; + + --%endcontext + end test_tap_escaping; + ]'; + + execute immediate q'[ + create or replace package body test_tap_escaping as + + procedure more_escaping as + begin + ut.expect(1).to_equal(1); + end more_escaping; + + + procedure not_skipping_escapes as + begin + ut.expect(10).to_equal(1); + end not_skipping_escapes; + + procedure escaped_comments as + begin + dbms_output.put_line('This \ and # should not be escaped, and this not as well!!!'); + ut.expect(1).to_equal(1); + end escaped_comments; + + procedure another_disabled_test as + begin + ut.expect(10).to_equal(1); + end; + end test_tap_escaping; + ]'; + + end compile_tests; + + + procedure simple_succeeding_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || '\s{21}1..1\s{21}# \s{21}# Subtest: A description of some context\s{25}1..1\s{25}ok - passing_test\s{25}# \s{25}# \s{25}# \s{25}# \s{25}# \s{21}# \sok - org\s*'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_succeeding_test; + + procedure simple_failing_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}not ok - a test with failing assertion\s{23}---\s{23}message: '"Fails as values are different"'\s{23}severity: fail\s{23}...\s{21}# \s{21}# \s{21}# \s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.failing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_failing_test; + + + procedure simple_erroring_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}not ok - a test raising unhandled exception\s{23}---\s{23}message: |\s{25ORA-06502: .*\s{25}ORA-06512: at line [[:digit:]]+\s{23}severity: error\s{23}...\s{21}# \s{21}# \s{21}# \s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.erroring_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end simple_erroring_test; + + + procedure disabled_test as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}ok - a disabled test # SKIP: Disabled for testing purpose\s{21}# \sok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.disabled_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end disabled_test; + + + procedure disabled_test_no_description as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := gc_boilerplate_suitepath_expression || q'[\s{21}1..1\s{21}# \s{21}ok - a disabled test with no reason # SKIP\s{21}# \sok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.disabled_test_no_reason',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected); + end disabled_test_no_description; + + + procedure multiple_tests_different_outcome as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[TAP version 14\s*1..1\s*# Subtest: org.*# Subtest: A suite.*\s{21}1..5\s{21}# \s{21}# Subtest: A desc.*\s{25}1..1\s{25}ok - passing_test\s{25}# \s{25}# \s{21}not ok - a test w.*\s{23}---\s{23}message:.*\s{21}# .*\s{21}# not ok - a test rai.*\s{23}---\s{23}message: |.*ok - a disabled test # SKIP: Disabled for testing purpose.*ok - a dis.* # SKIP\s{21}# \snot ok - org\s*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected, 'n'); + end multiple_tests_different_outcome; + + + procedure escape_suite_name as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: Some \\ and \# to be escaped%ok - Some \\ and \# to be escaped%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.more_escaping',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end escape_suite_name; + + + procedure escape_multiple_characters_test_name as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Even more \\\\ and multiple \#\#\#%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.more_escaping',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end escape_multiple_characters_test_name; + + + procedure special_characters_in_disabled_reason as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Disabled test # SKIP: With \\ and \# in skip reason%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.not_skipping_escapes',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end special_characters_in_disabled_reason; + + + procedure special_characters_in_comment as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%ok - Escaped Comments%# This \ and # should not be escaped, and this not as well!!!%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.escaped_comments',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + end special_characters_in_comment; + + + procedure context_as_commented_subtests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: A suite for testing different outcomes from reporters%# Subtest: A description of some context%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + + end context_as_commented_subtests; + + + procedure suitepath_as_chopped_subtests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[.*# Subtest: org.*1\s{5}# Subtest: utplsql.*1\s{9}# Subtest: tests.*1\s{13}# Subtest: helpers.*1\s{17}# Subtest: A suite for testing different outcomes from reporters.*]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_reporters.passing_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_match(l_expected, 'n'); + + end suitepath_as_chopped_subtests; + + + procedure include_context_with_skipped_tests as + l_output_data ut3_develop.ut_varchar2_list; + l_expected varchar2(32767); + begin + l_expected := q'[%# Subtest: Some context%]'; + + select * + bulk collect into l_output_data + from table(ut3_develop.ut.run('test_tap_escaping.another_disabled_test',ut3_develop.ut_tap_reporter())); + + ut.expect(ut3_tester_helper.main_helper.table_to_clob(l_output_data)).to_be_like(l_expected); + + end include_context_with_skipped_tests; + + + procedure drop_help_tests as + pragma autonomous_transaction; + begin + execute immediate 'drop package test_tap_escaping'; + end drop_help_tests; +end test_tap_reporter; +/ \ No newline at end of file diff --git a/test/ut3_user/reporters/test_tap_reporter.pks b/test/ut3_user/reporters/test_tap_reporter.pks new file mode 100644 index 000000000..27f9651bc --- /dev/null +++ b/test/ut3_user/reporters/test_tap_reporter.pks @@ -0,0 +1,53 @@ +create or replace package test_tap_reporter as + + --%suite(ut_tap_reporter) + --%suitepath(utplsql.test_user.reporters) + + --%beforeall + procedure compile_tests; + + --%test(Simple succeeding test) + procedure simple_succeeding_test; + + --%test(Simple failing test) + procedure simple_failing_test; + + --%test(Simple erroring test) + procedure simple_erroring_test; + + --%test(Skipped test) + procedure disabled_test; + + --%test(Skipped test without description) + procedure disabled_test_no_description; + + --%test(Multiple tests with different outcome) + procedure multiple_tests_different_outcome; + + --%test(Escape special characters in suite name) + procedure escape_suite_name; + + --%test(Escape multiple special characters in test name) + procedure escape_multiple_characters_test_name; + + --%test(Disabled Test with special characters in disable reason) + procedure special_characters_in_disabled_reason; + + --%test(Don't escape special characters in comment) + procedure special_characters_in_comment; + + --%test(Include context as commented subtests) + procedure context_as_commented_subtests; + + --%test(Suitepath as chopped subtests) + procedure suitepath_as_chopped_subtests; + + --%test(Include context with only skipped tests in output) + procedure include_context_with_skipped_tests; + + + --%afterall + procedure drop_help_tests; + +end test_tap_reporter; +/ \ No newline at end of file