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#if __TBB_TEST_USE_WSUGGEST_OVERRIDE
18// __TBB_override may not be used in the tested header file
19#pragma GCC diagnostic ignored "-Wsuggest-override"
20#undef __TBB_TEST_USE_WSUGGEST_OVERRIDE
21#endif
22
23#include "harness_defs.h" // for suppress_unused_warning
24
25#if TBB_USE_EXCEPTIONS
26#include "harness_assert.h"
27#include "../../examples/common/utility/utility.h"
28#include <sstream>
29
30namespace implementation_unit_tests {
31 namespace argument_dest_test_suite{
32 void test_type_impl_parse_and_store_simple_parse(){
33 int a=0;
34 utility::internal::type_impl<int> a_("","",a);
35 a_.parse_and_store("9");
36 ASSERT(a==9,"");
37 }
38 void test_default_value_of_is_matched(){
39 //Testing for result of is_matched() for arguments not yet tried to be parsed.
40 //I.e. values were set up by argument::constructor.
41 using utility::internal::argument;
42 int i;
43 argument b("","",i);
44 ASSERT(!b.is_matched(),"");
45
46 argument c = b;
47 ASSERT(!c.is_matched(),"");
48
49 argument d = b;
50 d = c;
51 ASSERT(!d.is_matched(),"");
52 }
53 }
54 //TODO: test cases for argument type management
55 namespace compile_only{
56 //TODO: enhance these to actually do checks by a negative test, or (if possible)
57 //by a positive test that at compile time selects between two alternatives,
58 //depending on whether operators exist or not (yes, SFINAE :)) -
59 //as non_pod class does provide the operators, and test do not check that compiler
60 //will reject types which don't have those.
61 using utility::cli_argument_pack;
62 void arg_chain(){
63 cli_argument_pack p;
64 int size=0;
65 p.arg(size,"size","size");
66 }
67 namespace tc_helper{
68 struct non_pod{
69 std::string s;
70 friend std::ostream& operator<<(std::ostream& o, non_pod){ return o;}
71 friend std::istream& operator>>(std::istream& i, non_pod){ return i;}
72 };
73 }
74 void non_pod_dest_type(){
75 cli_argument_pack p;
76 tc_helper::non_pod np;
77 p.arg(np,"","");
78 }
79 }
80 namespace cli_argument_pack_suite{
81 void test_copy_assign(){
82 using utility::cli_argument_pack;
83 int i=9;
84 std::stringstream expected_output; using std::endl;
85 expected_output
86 << " Program usage is:" << endl
87 << " the_binary_name [i=value]"
88 << endl << endl
89 << " where:" << endl
90 << " i - i desc (9)" << endl
91 ;
92 cli_argument_pack copy(cli_argument_pack().arg(i,"i","i desc"));
93 ASSERT(copy.usage_string("the_binary_name") == expected_output.str(),"usage string is not as expected");
94 cli_argument_pack assignee; assignee = copy;
95 ASSERT(assignee.usage_string("the_binary_name") == expected_output.str(),"Copying of cli_argument_pack breaks generation of usage string?");
96 }
97 }
98}
99
100#include <utility>
101namespace high_level_api_tests {
102 using utility::cli_argument_pack;
103 using utility::internal::array_length;
104
105 static const char * wrong_exception = "wrong exception thrown";
106 static const char * wrong_exception_description = "caught exception has wrong description";
107 void test_parse_basic(){
108 char const* argv[]={"some.exe","1","a"};
109 cli_argument_pack p;
110 int i=0; char a=' ';
111 p.positional_arg(i,"int","").positional_arg(a,"char","");
112 p.parse(array_length(argv),argv);
113 ASSERT(i==1,"");
114 ASSERT(a=='a',"");
115 }
116 //helper function for test of named flag parsing
117 template<typename T, size_t N>
118 bool parse_silent_flag( T(& argv)[N]){
119 cli_argument_pack p;
120 bool silent=false;
121 p.arg(silent,"silent","is extra info needed");
122 p.parse(array_length(argv),argv);
123 return silent;
124 }
125 void test_named_flags_success(){
126 char const* argv[]={"some.exe","silent"};
127 ASSERT(true == parse_silent_flag(argv),"");
128 }
129
130 void test_named_flags_failure(){
131 try {
132 char const* argv[]={"some.exe","1"};
133 parse_silent_flag(argv);
134 ASSERT(false,"exception was expected due to invalid argument, but not caught");
135 }
136 catch(std::invalid_argument& e){
137 ASSERT(e.what()==std::string("unknown parameter starting at:'1'"),wrong_exception_description);
138 }
139 catch(...){ASSERT(false,wrong_exception);}
140 }
141
142 //helper function for test of named flag parsing
143 template<typename T, size_t N>
144 std::pair<bool,int> parse_silent_flag_and_int( T(& argv)[N]){
145 cli_argument_pack p;
146 bool silent=false;
147 int i=125;
148 p
149 .arg(silent,"silent","is extra info needed")
150 .positional_arg(i,"int","");
151 p.parse(array_length(argv),argv);
152 return std::make_pair(silent,i);
153 }
154
155 void test_named_flags_failure_and_other_arg(){
156 char const* argv[]={"some.exe","1"};
157 ASSERT(std::make_pair(false,1) == parse_silent_flag_and_int(argv),"");
158 }
159
160 void test_named_flags_and_other_arg(){
161 char const* argv[]={"some.exe","silent","7"};
162 ASSERT(std::make_pair(true,7) == parse_silent_flag_and_int(argv),"");
163 }
164
165 void test_named_flags_and_other_arg_different_order(){
166 char const* argv[]={"some.exe","7","silent"};
167 ASSERT(std::make_pair(true,7) == parse_silent_flag_and_int(argv),"");
168 }
169
170 void test_flags_only_others_default(){
171 char const* argv[]={"some.exe","silent"};
172 ASSERT(std::make_pair(true,125) == parse_silent_flag_and_int(argv),"");
173 }
174
175 namespace parameters_validation_test_suite{
176 namespace test_validation_function_called_helpers{
177 struct validator{
178 static bool called;
179 static bool accept(const int & ){
180 called = true;
181 return true;
182 }
183 };
184 bool validator::called =false;
185 }
186 void test_validation_function_called(){
187 using test_validation_function_called_helpers::validator;
188
189 char const* argv[]={"some.exe","7"};
190 cli_argument_pack p;
191 int size =0;
192 p.positional_arg(size,"size","",validator::accept);
193 p.parse(array_length(argv),argv);
194 ASSERT((validator::called),"validation function has not been called");
195 }
196 void test_validation_failed(){
197 struct validator{
198 static bool reject(const int &){
199 return false;
200 }
201 };
202 char const* argv[]={"some.exe","7"};
203 cli_argument_pack p;
204 int size =0;
205 p.positional_arg(size,"size","",validator::reject);
206 try {
207 p.parse(array_length(argv),argv);
208 ASSERT((false),"An exception was expected due to failed argument validation, "
209 "but no exception thrown");
210 }
211 catch(std::invalid_argument& e){
212 std::string error_msg("'7' is invalid value for argument 'size'");
213 ASSERT(e.what()==error_msg , wrong_exception_description);
214 }
215 catch(...){ASSERT((false),wrong_exception);}
216 }
217 }
218 namespace error_handling {
219 void test_wrong_input(){
220 char const* argv[]={"some.exe","silent"};
221 cli_argument_pack p;
222 int size =0;
223 p.positional_arg(size,"size","");
224 try{
225 p.parse(array_length(argv),argv);
226 ASSERT(false,"An exception was expected due to wrong input, but no exception thrown");
227 }
228 catch(std::invalid_argument & e){
229 std::string error_msg("'silent' is incorrect input for argument 'size' (error converting string 'silent')");
230 ASSERT(e.what()==error_msg, wrong_exception_description);
231 }
232 catch(...){ASSERT(false,wrong_exception);}
233 }
234 void test_duplicate_arg_names(){
235 cli_argument_pack p;
236 int a=0;
237 p.arg(a,"a","");
238 try{
239 int dup_a=0;
240 p.arg(dup_a,"a","");
241 ASSERT(false, "An exception was expected due adding duplicate parameter name, but not thrown");
242 }
243 catch(std::invalid_argument& e){
244 ASSERT(e.what()==std::string("argument with name: 'a' already registered"),wrong_exception_description);
245 }
246 catch(...){ASSERT(false,wrong_exception);}
247 }
248 void test_duplicate_positional_arg_names(){
249 cli_argument_pack p;
250 int a=0;
251 p.positional_arg(a,"a","");
252 try{
253 int dup_a=0;
254 p.positional_arg(dup_a,"a","");
255 ASSERT(false, "An exception was expected due adding duplicate parameter name, but not thrown");
256 }
257 catch(std::invalid_argument& e){
258 ASSERT(e.what()==std::string("argument with name: 'a' already registered"),wrong_exception_description);
259 }
260 catch(...){ASSERT(false,wrong_exception);}
261 }
262 }
263 namespace usage_string {
264 void test_one_arg(){
265 cli_argument_pack p;
266 int size =9;
267 p.arg(size,"size","size of problem domain");
268 std::string const binary_name = "binary.exe";
269 std::stringstream expected_output;
270 using std::endl;
271 expected_output << " Program usage is:" << endl
272 << " " << binary_name << " [size=value]"
273 << endl << endl
274 << " where:" << endl
275 << " size - size of problem domain (9)" << endl
276 ;
277 std::string usage= p.usage_string(binary_name);
278 ASSERT(usage==expected_output.str(),"");
279 }
280 void test_named_and_postional_args(){
281 cli_argument_pack p;
282 int size =9;
283 int length =8;
284 int stride = 7;
285 p
286 .arg(size,"size","")
287 .positional_arg(length,"length","")
288 .positional_arg(stride,"stride","");
289 std::string const binary_name = "binary.exe";
290 std::stringstream expected_output;
291 using std::endl;
292 expected_output << " Program usage is:" << endl
293 << " " << binary_name << " [size=value] [length=value] [stride=value] [length [stride]]"
294 << endl << endl
295 << " where:" << endl
296 << " size - (9)" << endl
297 << " length - (8)" << endl
298 << " stride - (7)" << endl
299 ;
300 std::string usage= p.usage_string(binary_name);
301 ASSERT(usage==expected_output.str(),"");
302 }
303 void test_bool_flag(){
304 bool flag=false;
305 cli_argument_pack p;
306 p.arg(flag,"flag","");
307 std::string const binary_name = "binary.exe";
308 std::stringstream expected_output;
309 using std::endl;
310 expected_output << " Program usage is:" << endl
311 << " " << binary_name << " [flag]"
312 << endl << endl
313 << " where:" << endl
314 << " flag - (0)" << endl
315 ;
316 std::string usage= p.usage_string(binary_name);
317 ASSERT(usage==expected_output.str(),"");
318
319 }
320
321 }
322 namespace name_positional_syntax {
323 void test_basic(){
324 cli_argument_pack p;
325 int size =0;
326 int time = 0;
327 p
328 .positional_arg(size,"size","")
329 .positional_arg(time,"time","");
330 char const* argv[]={"some.exe","1","2"};
331 p.parse(array_length(argv),argv);
332 ASSERT(size==1,"");
333 ASSERT(time==2,"");
334 }
335 void test_positional_args_explicitly_named(){
336 const char* no_or_wrong_exception_error_msg = "exception was expected but not thrown, or wrong exception caught";
337 //TODO: Similar functionality is used all over the test. Generalize this helper further, and use as wide within the test as possible?
338 struct failed_with_exception{
339 static bool _(cli_argument_pack & p, std::size_t argc, char const* argv[]){
340 try{
341 p.parse(argc,argv);
342 return false;
343 }
344 catch(std::exception &){
345 return true;
346 }
347 catch(...){
348 return false;
349 }
350 }
351 };
352 {
353 cli_argument_pack p;
354 int a,b,c,d;
355 p
356 .positional_arg(a,"a","")
357 .positional_arg(b,"b","")
358 .positional_arg(c,"c","")
359 .positional_arg(d,"d","");
360 char const* argv[]={"some.exe","a=7","0","1","2","4"};
361 ASSERT(failed_with_exception::_(p,array_length(argv),argv),no_or_wrong_exception_error_msg);
362 }
363 {
364 cli_argument_pack p;
365 int a,b,c,d;
366 p
367 .positional_arg(a,"a","")
368 .positional_arg(b,"b","")
369 .positional_arg(c,"c","")
370 .positional_arg(d,"d","");
371 char const* argv[]={"some.exe","a=7","0","1","2"};
372 ASSERT(failed_with_exception::_(p,array_length(argv),argv),no_or_wrong_exception_error_msg);
373 }
374 {
375 cli_argument_pack p;
376 int a=-1,b=-1,c = -1,d=-1;
377 p
378 .positional_arg(a,"a","")
379 .positional_arg(b,"b","")
380 .positional_arg(c,"c","")
381 .positional_arg(d,"d","");
382 char const* argv[]={"some.exe","0","1","d=7",};
383 ASSERT(!failed_with_exception::_(p,array_length(argv),argv),"unexpected exception");
384 ASSERT(a==0,""); ASSERT(b==1,""); ASSERT(c==-1,"");ASSERT(d==7,"");
385 }
386 }
387 }
388 namespace name_value_syntax {
389 void test_basic(){
390 cli_argument_pack p;
391 int size =0;
392 p.arg(size,"size","size of problem domain");
393 char const* argv[]={"some.exe","size=7"};
394 p.parse(array_length(argv),argv);
395 ASSERT(size==7,"");
396 }
397
398 void test_relaxed_order(){
399 cli_argument_pack p;
400 int size =0;
401 int time=0;
402 p
403 .arg(size,"size","")
404 .arg(time,"time","");
405 char const* argv[]={"some.exe","time=1","size=2"};
406 p.parse(array_length(argv),argv);
407 ASSERT(size==2,"");
408 ASSERT(time==1,"");
409 }
410
411 }
412 namespace number_of_argument_value{
413 void test_only_single_values_allowed(){
414 cli_argument_pack p;
415 int a=0;
416 p.arg(a,"a","");
417 const char* argv[] = {"","a=7","a=8"};
418 try {
419 p.parse(array_length(argv),argv);
420 ASSERT(false,"exception was expected due to duplicated values provided in input, but not thrown");
421 }
422 catch(std::invalid_argument& e){
423 //TODO: use patterns (regexp ?) to generate /validate exception descriptions
424 ASSERT(e.what() == std::string("several values specified for: 'a' argument"),wrong_exception_description);
425 }
426 catch(...){ASSERT(false,wrong_exception);}
427 }
428 }
429 namespace thread_range_tests{
430 using utility::thread_number_range;
431 using utility::internal::thread_range_step;
432 using utility::internal::step_function_multiply;
433 using utility::internal::step_function_plus;
434 using utility::internal::step_function_power2_ladder;
435
436 int auto_value(){
437 return 100;
438 }
439 bool operator ==(thread_range_step const& left, utility::internal::thread_range_step const& right){
440 return (left.step_function == right.step_function)
441 && (left.step_function_argument == right.step_function_argument)
442 ;
443 }
444
445 bool operator ==(thread_number_range const& left, thread_number_range const& right){
446 return (left.auto_number_of_threads==right.auto_number_of_threads)
447 && (left.first == right.first)
448 && (left.last == right.last)
449 && (left.step == right.step)
450 ;
451 }
452
453 void constructor_default_values(){
454 thread_number_range r(auto_value);
455 const int default_num_threads = auto_value();
456 ASSERT((r.first==1)&&(r.last==default_num_threads),"");
457 }
458 void validation(){
459 try{
460 thread_number_range range(auto_value,12,6);
461 Harness::suppress_unused_warning(range);
462 ASSERT(false,"exception was expected due to invalid range specified, but not thrown");
463 }
464 catch(std::invalid_argument& e){
465 ASSERT(e.what() == std::string("decreasing sequence not allowed"), wrong_exception_description);
466 }
467 catch(...){ASSERT(false,wrong_exception);}
468 }
469
470 thread_number_range thread_number_range_from_string(std::string const& string_to_parse){
471 thread_number_range r(auto_value,0,0);
472 std::stringstream str(string_to_parse); str>>r;
473 return r;
474 }
475 static const char* thread_range_parse_failed = "error parsing thread range string";
476 void post_process_single_value(){
477 ASSERT(thread_number_range_from_string("auto") ==
478 thread_number_range(auto_value,auto_value(),auto_value())
479 ,thread_range_parse_failed
480 );
481 }
482 void post_process_pair_value(){
483 ASSERT(thread_number_range_from_string("1:auto") ==
484 thread_number_range(auto_value,1,auto_value())
485 ,thread_range_parse_failed
486 );
487
488 ASSERT(thread_number_range_from_string("auto:auto") ==
489 thread_number_range(auto_value,auto_value(),auto_value())
490 ,thread_range_parse_failed
491 );
492 }
493
494 void post_process_troika_value_with_plus_step(){
495 ASSERT(thread_number_range_from_string("1:auto:+2") ==
496 thread_number_range(auto_value,1,auto_value(),thread_range_step(step_function_plus,2))
497 ,thread_range_parse_failed
498 );
499 }
500
501 void post_process_troika_value_with_multiply_step(){
502 ASSERT(thread_number_range_from_string("1:auto:*2.6") ==
503 thread_number_range(auto_value,1,auto_value(),thread_range_step(step_function_multiply,2.6))
504 ,thread_range_parse_failed
505 );
506 }
507
508 void post_process_troika_value_with_ladder_step(){
509 try{
510 thread_number_range range = thread_number_range_from_string("1:16:#3");
511 Harness::suppress_unused_warning(range);
512 ASSERT(false,"exception was expected due to invalid range specified, but not thrown");
513 }
514 catch(std::invalid_argument& e){
515 ASSERT(e.what() == std::string("the argument of # should be a power of 2"), wrong_exception_description);
516 }
517 catch(...){ASSERT(false,wrong_exception);}
518
519 ASSERT(thread_number_range_from_string("1:32:#4") ==
520 thread_number_range(auto_value,1,32,thread_range_step(step_function_power2_ladder,4))
521 ,thread_range_parse_failed
522 );
523 }
524
525 void test_print_content(){
526 std::stringstream str;
527 str<<thread_number_range(auto_value,1,8,thread_range_step(step_function_multiply,2));
528 ASSERT(str.str() == "1:8:*2","Unexpected string");
529 }
530 }
531}
532
533void run_implementation_unit_tests(){
534 using namespace implementation_unit_tests;
535 argument_dest_test_suite::test_type_impl_parse_and_store_simple_parse();
536 argument_dest_test_suite::test_default_value_of_is_matched();
537
538 cli_argument_pack_suite::test_copy_assign();
539}
540void run_high_level_api_tests(){
541 using namespace high_level_api_tests;
542
543 test_parse_basic();
544 test_named_flags_success();
545 test_named_flags_failure();
546 test_named_flags_failure_and_other_arg();
547 test_named_flags_and_other_arg();
548 test_flags_only_others_default();
549 test_named_flags_and_other_arg_different_order();
550
551 usage_string::test_one_arg();
552 usage_string::test_named_and_postional_args();
553 usage_string::test_bool_flag();
554
555 parameters_validation_test_suite::test_validation_function_called();
556 parameters_validation_test_suite::test_validation_failed();
557
558 name_value_syntax::test_basic();
559 name_value_syntax::test_relaxed_order();
560
561 number_of_argument_value::test_only_single_values_allowed();
562
563 name_positional_syntax::test_basic();
564 name_positional_syntax::test_positional_args_explicitly_named();
565
566 error_handling::test_wrong_input();
567 error_handling::test_duplicate_arg_names();
568 error_handling::test_duplicate_positional_arg_names();
569
570 thread_range_tests::constructor_default_values();
571 thread_range_tests::validation();
572 thread_range_tests::post_process_single_value();
573 thread_range_tests::post_process_pair_value();
574 thread_range_tests::post_process_troika_value_with_plus_step();
575 thread_range_tests::post_process_troika_value_with_multiply_step();
576 thread_range_tests::post_process_troika_value_with_ladder_step();
577 thread_range_tests::test_print_content();
578}
579#endif // TBB_USE_EXCEPTIONS
580
581#include "harness.h"
582int TestMain(){
583#if TBB_USE_EXCEPTIONS
584 Harness::suppress_unused_warning(utility::thread_number_range_desc);
585 try{
586 run_implementation_unit_tests();
587 run_high_level_api_tests();
588 }catch(std::exception& e){
589 //something went wrong , dump any possible details
590 std::stringstream str; str<< "run time error: " << e.what()<<std::endl;
591 ASSERT(false,str.str().c_str());
592 }
593 return Harness::Done;
594#else
595 REPORT("Known issue: the test cannot work with exceptions disabled\n");
596 return Harness::Done;
597#endif
598}
599