| 1 | /* |
| 2 | Copyright (c) 2005-2019 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef tbb_harness_test_cases_framework_H |
| 18 | #define tbb_harness_test_cases_framework_H |
| 19 | |
| 20 | #if defined(_MSC_VER) |
| 21 | #define _SCL_SECURE_NO_WARNINGS |
| 22 | #endif |
| 23 | |
| 24 | #undef DO_ITT_NOTIFY |
| 25 | |
| 26 | #include "harness.h" |
| 27 | #include "harness_assert.h" |
| 28 | #include "tbb/tbb_stddef.h" |
| 29 | |
| 30 | #include <cstdlib> |
| 31 | |
| 32 | #include <vector> |
| 33 | #include <algorithm> |
| 34 | #include <string> |
| 35 | #include <sstream> |
| 36 | #include <iostream> |
| 37 | |
| 38 | namespace test_framework{ |
| 39 | template<typename test_class> |
| 40 | void run_test(){ |
| 41 | test_class()(); |
| 42 | } |
| 43 | |
| 44 | #if TBB_USE_EXCEPTIONS |
| 45 | struct assertion_failure:std::exception{ |
| 46 | const char* my_filename; |
| 47 | int my_line; |
| 48 | const char* my_expression; |
| 49 | const char * ; |
| 50 | assertion_failure(const char* filename, int line, const char* expression, const char * ): |
| 51 | my_filename(filename), |
| 52 | my_line(line), |
| 53 | my_expression(expression), |
| 54 | my_comment(comment) |
| 55 | {} |
| 56 | virtual const char* what() const throw() __TBB_override { |
| 57 | return "test assertion failed" ; |
| 58 | } |
| 59 | }; |
| 60 | void throw_assertion_failure(){throw assertion_failure("" ,0,"" ,"" );} |
| 61 | void throw_assertion_failure(const char* filename, int line, const char* expression, const char * ){ |
| 62 | throw assertion_failure(filename, line, expression, comment); |
| 63 | } |
| 64 | #endif // TBB_USE_EXCEPTIONS |
| 65 | |
| 66 | class test_suite{ |
| 67 | typedef void(*run_test_function_pointer_type)(); |
| 68 | typedef std::pair<std::string, run_test_function_pointer_type> tc_record_pair; |
| 69 | std::vector<tc_record_pair > test_cases; |
| 70 | public: |
| 71 | template<class test_class> |
| 72 | void register_test_case(std::string const& name, test_class * ){ |
| 73 | test_cases.push_back(tc_record_pair(name,& run_test<test_class>)); |
| 74 | } |
| 75 | std::string operator()(bool silent=false){ |
| 76 | std::stringstream str; |
| 77 | size_t failed=0; |
| 78 | for (size_t i=0;i<test_cases.size();++i){ |
| 79 | #if TBB_USE_EXCEPTIONS |
| 80 | try{ |
| 81 | (test_cases[i].second)(); |
| 82 | }catch(std::exception& e){ |
| 83 | failed++; |
| 84 | str<<"test case \"" <<test_cases[i].first<<"\" failed with exception. what():\"" <<e.what()<<"\"" <<std::endl; |
| 85 | } |
| 86 | #else |
| 87 | (test_cases[i].second)(); |
| 88 | #endif |
| 89 | } |
| 90 | if (!silent) { |
| 91 | str<<test_cases.size()<<" test cases are run; " <<failed<<" failed" <<std::endl; |
| 92 | } |
| 93 | return str.str(); |
| 94 | } |
| 95 | }; |
| 96 | test_suite& get_suite_ref(){static test_suite ts; return ts;} |
| 97 | void run_all_and_print_results(test_suite& ts,std::ostream& o , bool silent=false){ |
| 98 | o<<ts(silent); |
| 99 | } |
| 100 | } |
| 101 | using test_framework::get_suite_ref; |
| 102 | #define TEST_CASE_WITH_FIXTURE(TC_NAME,FIXTURE_NAME) \ |
| 103 | struct TC_NAME; \ |
| 104 | struct TC_NAME:FIXTURE_NAME { \ |
| 105 | /* explicitly implemented default constructor \ |
| 106 | is need here to please gcc 4.3.2*/ \ |
| 107 | TC_NAME(){} \ |
| 108 | void operator()(); \ |
| 109 | }; \ |
| 110 | bool TC_NAME##_registerd = (get_suite_ref().register_test_case(#TC_NAME,static_cast<TC_NAME*>(0)),true);\ |
| 111 | void TC_NAME::operator()() |
| 112 | |
| 113 | namespace test_framework_unit_tests{ |
| 114 | namespace test_helper{ |
| 115 | template <size_t id> struct tag{}; |
| 116 | template<typename tag> |
| 117 | struct test_case{ |
| 118 | static bool is_run; |
| 119 | void operator()(){ |
| 120 | is_run=true; |
| 121 | } |
| 122 | }; |
| 123 | template<typename tag> bool test_case<tag>::is_run = false; |
| 124 | |
| 125 | } |
| 126 | using namespace test_framework; |
| 127 | namespace test_test_suite_ref{ |
| 128 | void run_all_runs_all_registered_test_cases(){ |
| 129 | test_suite s; |
| 130 | using test_helper::tag; |
| 131 | test_helper::test_case<tag<__LINE__> > tc1; |
| 132 | test_helper::test_case<tag<__LINE__> > tc2; |
| 133 | s.register_test_case("tc1" ,&tc1); |
| 134 | s.register_test_case("tc2" ,&tc2); |
| 135 | s(); |
| 136 | ASSERT(tc1.is_run && tc2.is_run,"test_suite::operator() should run all the tests" ); |
| 137 | } |
| 138 | |
| 139 | struct silent_switch_fixture{ |
| 140 | test_helper::test_case<test_helper::tag<__LINE__> > empty_test_case; |
| 141 | }; |
| 142 | struct run_all_and_print_results_should_respect_silent_mode: silent_switch_fixture{ |
| 143 | void operator()(){ |
| 144 | using test_helper::tag; |
| 145 | test_helper::test_case<tag<__LINE__> > do_nothing_tc; |
| 146 | test_suite ts; |
| 147 | ts.register_test_case("tc_name" ,&do_nothing_tc); |
| 148 | bool silent =true; |
| 149 | ASSERT(ts(silent).empty(),"in silent mode no message except error should be output" ); |
| 150 | } |
| 151 | }; |
| 152 | struct run_all_and_print_results_should_respect_verbose_mode: silent_switch_fixture{ |
| 153 | void operator()(){ |
| 154 | using test_helper::tag; |
| 155 | test_helper::test_case<tag<__LINE__> > do_nothing_tc; |
| 156 | test_suite ts; |
| 157 | ts.register_test_case("tc_name" ,&do_nothing_tc); |
| 158 | bool silent =true; |
| 159 | ASSERT(!ts(!silent).empty(),"in verbose mode all messages should be outputted" ); |
| 160 | } |
| 161 | }; |
| 162 | } |
| 163 | namespace test_test_case_macro{ |
| 164 | test_suite& get_suite_ref(){static test_suite ts; return ts;} |
| 165 | typedef test_helper::test_case<test_helper::tag<__LINE__> > unique_test_type; |
| 166 | TEST_CASE_WITH_FIXTURE(test_auto_registration,unique_test_type){ |
| 167 | unique_test_type::operator()(); |
| 168 | } |
| 169 | void run_test_test_case_macro(){ |
| 170 | get_suite_ref()(); |
| 171 | ASSERT(unique_test_type::is_run,"test case macro should register the test case in suite" ); |
| 172 | } |
| 173 | void test_test_case_macro_does_not_create_test_case_object(){ |
| 174 | ASSERT(false,"to implement" ); |
| 175 | } |
| 176 | } |
| 177 | namespace internal_assertions_failure_test_cases{ |
| 178 | |
| 179 | test_suite& get_suite_ref(){static test_suite ts; return ts;} |
| 180 | |
| 181 | //TODO: investigate compilation errors regarding tbb::set_assertion_handler |
| 182 | // struct empty_fixture{}; |
| 183 | // TEST_CASE_WITH_FIXTURE(test_internal_assertion_does_not_stop_test_suite,empty_fixture){ |
| 184 | // struct handler{ |
| 185 | // static void _( const char* /*filename*/, int /*line*/, const char* /*expression*/, const char * /*comment*/ ){ |
| 186 | // } |
| 187 | // }; |
| 188 | // |
| 189 | // tbb::assertion_handler_type previous = tbb::set_assertion_handler(handler::_); |
| 190 | // __TBB_ASSERT(false,"this assert should not stop the test suite run"); |
| 191 | // tbb::set_assertion_handler(previous ); |
| 192 | //// ASSERT(assertion_handler::is_called,"__TBB_ASSERT should call installed assertion handler"); |
| 193 | // } |
| 194 | // TEST_CASE_WITH_FIXTURE(test_internal_assertion_does_mark_the_test_as_failed,empty_fixture){ |
| 195 | // test_suite ts; |
| 196 | // struct _{ |
| 197 | //// static |
| 198 | // static void assertion_handler_type( const char* /*filename*/, int /*line*/, const char* /*expression*/, const char * /*comment*/ ){ |
| 199 | // } |
| 200 | // }; |
| 201 | // tbb::assertion_handler_type previous = tbb::set_assertion_handler(_::assertion_handler_type); |
| 202 | // __TBB_ASSERT(false,"this assert should not stop the test suite run"); |
| 203 | // tbb::set_assertion_handler(previous ); |
| 204 | // std::string result = ts(); |
| 205 | // std::size_t test_case_name_begin_pos = result.find("test case \""); |
| 206 | // std::size_t failed_begin_pos = result.find("failed"); |
| 207 | // ASSERT(test_case_name_begin_pos!=std::string::npos && failed_begin_pos!=std::string::npos && test_case_name_begin_pos<failed_begin_pos,"internal assertion should result in test failure"); |
| 208 | // } |
| 209 | |
| 210 | } |
| 211 | void run_all_test(){ |
| 212 | test_test_suite_ref::run_all_runs_all_registered_test_cases(); |
| 213 | test_test_suite_ref::run_all_and_print_results_should_respect_silent_mode()(); |
| 214 | test_test_suite_ref::run_all_and_print_results_should_respect_verbose_mode()(); |
| 215 | test_test_case_macro::run_test_test_case_macro(); |
| 216 | //TODO: uncomment and implement |
| 217 | // test_test_case_macro::test_test_case_macro_does_not_create_test_case_object(); |
| 218 | run_all_and_print_results(internal_assertions_failure_test_cases::get_suite_ref(),std::cout,!Verbose); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | int TestMain (){ |
| 223 | #if TBB_USE_EXCEPTIONS |
| 224 | SetHarnessErrorProcessing(test_framework::throw_assertion_failure); |
| 225 | //TODO: deal with assertions during stack unwinding |
| 226 | //tbb::set_assertion_handler( test_framework::throw_assertion_failure ); |
| 227 | #endif |
| 228 | { |
| 229 | test_framework_unit_tests::run_all_test(); |
| 230 | } |
| 231 | bool silent = !Verbose; |
| 232 | run_all_and_print_results(test_framework::get_suite_ref(),std::cout,silent); |
| 233 | return Harness::Done; |
| 234 | } |
| 235 | |
| 236 | #endif //tbb_harness_test_cases_framework_H |
| 237 | |