1 /* $Id: test_ncbidiag_f_mt.cpp 633612 2021-06-22 17:38:24Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Vyacheslav Kononenko
27 *
28 * File Description:
29 * Test for "NCBIDIAG" filter mechanism in multithreaded environment
30 *
31 */
32
33 #include <ncbi_pch.hpp>
34 #include <corelib/test_mt.hpp>
35 #include <corelib/ncbidiag.hpp>
36 #include <algorithm>
37
38 #include <common/test_assert.h> /* This header must go last */
39
40
41 USING_NCBI_SCOPE;
42
43
44 #define NUM_TESTS 9
45
46
47
48 /////////////////////////////////////////////////////////////////////////////
49 // Test application
50
51 class CTestDiagApp : public CThreadedApp
52 {
53 public:
54 void SetCase(int case_num);
55
56 virtual bool Thread_Init(int idx);
57 virtual bool Thread_Run (int idx);
58
59 protected:
60 virtual bool TestApp_Init(void);
61 virtual bool TestApp_Exit(void);
62 virtual bool TestApp_Args(CArgDescriptions& args);
63
64 private:
65 typedef pair<int,int> TCase;
66 typedef map<TCase, string> TMessages;
67 typedef set< pair<TCase,int> > TThreads;
68
69 void x_SetExpects(const string& expects, const string& ex_expects);
70 string x_MakeMessage(const string& msg,
71 int idx,
72 const TCase& ncase);
73 void x_PrintMessages(int case_number,
74 int idx,
75 const char* module,
76 const char* nclass,
77 const char* function);
78
79 string m_Case;
80 TMessages m_Messages;
81 TThreads m_CaseExpected;
82 };
83
84
85 static CNcbiOstrstream s_Sout;
86
SetCase(int case_num)87 void CTestDiagApp::SetCase(int case_num)
88 {
89 string filter;
90
91 switch(case_num){
92 case 1 :
93 filter = string();
94 x_SetExpects("111111111", "111111111");
95 break;
96 case 2 :
97 filter = "module";
98 x_SetExpects("111100000", "000000000");
99 break;
100 case 3 :
101 filter = "::class";
102 x_SetExpects("110011000", "000000000");
103 break;
104 case 4:
105 filter = "function()";
106 x_SetExpects("101010100", "000000000");
107 break;
108 case 5:
109 filter = "?";
110 x_SetExpects("000011110", "111111111");
111 break;
112 case 6:
113 filter = "!foo";
114 x_SetExpects("111111110", "111111111");
115 break;
116 case 7:
117 filter = "/corelib";
118 x_SetExpects("111111111", "111111111");
119 break;
120 case 8:
121 filter = "/corelib/";
122 x_SetExpects("000000000", "000000000");
123 break;
124 case 9:
125 filter = "module::class::function()";
126 x_SetExpects("100000000", "000000000");
127 break;
128 case 10:
129 filter = "module::class";
130 x_SetExpects("110000000", "000000000");
131 break;
132 case 11:
133 filter = "::class::function()";
134 x_SetExpects("100010000", "000000000");
135 break;
136 case 12:
137 filter = "module foo";
138 x_SetExpects("111100001", "000000000");
139 break;
140 case 13:
141 filter = "::class ::boo";
142 x_SetExpects("110011001", "000000000");
143 break;
144 case 14:
145 // TWO spaces ...
146 filter = "::class ::boo";
147 x_SetExpects("110011001", "000000000");
148 break;
149 case 15:
150 filter = "function() doo()";
151 x_SetExpects("101010101", "000000000");
152 break;
153 case 16:
154 filter = "module ::boo";
155 x_SetExpects("111100001", "000000000");
156 break;
157 case 17:
158 filter = "module doo()";
159 x_SetExpects("111100001", "000000000");
160 break;
161 case 18:
162 filter = "::class doo()";
163 x_SetExpects("110011001", "000000000");
164 break;
165 case 19:
166 filter = "!module";
167 x_SetExpects("000011111", "111111111");
168 break;
169 case 20:
170 filter = "!::class";
171 x_SetExpects("001100111", "111111111");
172 break;
173 case 21:
174 filter = "!function()";
175 x_SetExpects("010101011", "111111111");
176 break;
177 case 22:
178 filter = "!module foo";
179 x_SetExpects("000000001", "000000000");
180 break;
181 case 23:
182 filter = "!module ::class";
183 x_SetExpects("000011000", "000000000");
184 break;
185 case 24:
186 filter = "!module function()";
187 x_SetExpects("000010100", "000000000");
188 break;
189 case 25:
190 filter = "!::class module";
191 x_SetExpects("001100000", "000000000");
192 break;
193 case 26:
194 filter = "!function() module";
195 x_SetExpects("010100000", "000000000");
196 break;
197 case 27:
198 filter = "module !foo";
199 x_SetExpects("111100000", "000000000");
200 break;
201 case 28:
202 filter = "module !::class";
203 x_SetExpects("001100000", "000000000");
204 break;
205 case 29:
206 filter = "module !function()";
207 x_SetExpects("010100000", "000000000");
208 break;
209 case 30:
210 filter = "!module !foo";
211 x_SetExpects("000011110", "111111111");
212 break;
213 case 31:
214 filter = "!module !::class";
215 x_SetExpects("000000111", "111111111");
216 break;
217 case 32:
218 filter = "!module !function()";
219 x_SetExpects("000001011", "111111111");
220 break;
221 case 33:
222 filter = "!foo !module";
223 x_SetExpects("000011110", "111111111");
224 break;
225 case 34:
226 filter = "!::class !module";
227 x_SetExpects("000000111", "111111111");
228 break;
229 case 35:
230 filter = "!function() !module";
231 x_SetExpects("000001011", "111111111");
232 break;
233 case 36:
234 filter = "!/corelib";
235 x_SetExpects("000000000", "000000000");
236 break;
237 case 37:
238 filter = "!/corelib/";
239 x_SetExpects("111111111", "111111111");
240 break;
241 case 38:
242 filter = "!function() !module !foo";
243 x_SetExpects("000001010", "111111111");
244 break;
245 case 39:
246 filter = "!/corelib/test";
247 x_SetExpects("000000000", "000000000");
248 break;
249 case 40:
250 filter = "!/corelib/test/";
251 x_SetExpects("000000000", "000000000");
252 break;
253 case 41:
254 filter = "/corelib !/dbapi";
255 x_SetExpects("111111111", "111111111");
256 break;
257 case 42:
258 filter = "!/dbapi /corelib";
259 x_SetExpects("111111111", "111111111");
260 break;
261 case 43:
262 filter = "!/dbapi !/corelib";
263 x_SetExpects("000000000", "000000000");
264 break;
265 case 44:
266 filter = "/dbapi !/corelib";
267 x_SetExpects("000000000", "000000000");
268 break;
269 case 45:
270 filter = "!/corelib /dbapi";
271 x_SetExpects("000000000", "000000000");
272 break;
273 case 46:
274 filter = "!/dbapi !/corelib/";
275 x_SetExpects("111111111", "111111111");
276 break;
277 case 47:
278 filter = "!/corelib/ !/dbapi";
279 x_SetExpects("111111111", "111111111");
280 break;
281 }
282
283 if ( !filter.empty() ) {
284 SetDiagFilter(eDiagFilter_All, filter.c_str());
285 } else {
286 filter = "no filter";
287 }
288 m_Case = "filter \"" + filter + "\"";
289
290 }
291
292
x_SetExpects(const string & expects,const string & ex_expects)293 void CTestDiagApp::x_SetExpects(const string& expects, const string& ex_expects)
294 {
295 for (int i = 0; i < NUM_TESTS; ++i) {
296 if (expects[i] == '1') {
297 for (unsigned int thr = 0; thr < s_NumThreads; ++thr)
298 m_CaseExpected.insert(
299 TThreads::value_type(TCase(i, 0), thr) );
300 }
301 if (ex_expects[i] == '1') {
302 for (int num = 1; num < 3; ++num)
303 for (unsigned int thr = 0; thr < s_NumThreads; ++thr)
304 m_CaseExpected.insert(
305 TThreads::value_type(TCase(i, num), thr) );
306 }
307 }
308 }
309
310
Thread_Init(int)311 bool CTestDiagApp::Thread_Init(int /* idx */)
312 {
313 return true;
314 }
315
316
x_MakeMessage(const string & msg,int idx,const TCase & ncase)317 string CTestDiagApp::x_MakeMessage(const string& msg,
318 int idx,
319 const TCase& ncase)
320 {
321 return string("!=") + NStr::IntToString(ncase.first) + " "
322 + NStr::IntToString(ncase.second) + " "
323 + NStr::IntToString(idx) + " "
324 + msg + "=!";
325 }
326
327
x_PrintMessages(int test_number,int idx,const char * module,const char * nclass,const char * function)328 void CTestDiagApp::x_PrintMessages(int test_number,
329 int idx,
330 const char* module,
331 const char* nclass,
332 const char* function)
333 {
334 string location = string(module) + "::" + nclass + "::" +
335 function + "()";
336 string postmsg = location + " in ERR_POST";
337 string exceptmsg = location + " in the one level exception";
338 string secondmsg = location + " in the two levels exception";
339
340 if (idx == 0) {
341 m_Messages[ TCase( test_number, 0 ) ] = postmsg;
342 m_Messages[ TCase( test_number, 1 ) ] = exceptmsg;
343 m_Messages[ TCase( test_number, 2 ) ] = secondmsg;
344 }
345
346 // ERR_POST
347 ERR_POST(x_MakeMessage(postmsg, idx, TCase(test_number,0))
348 << MDiagModule(module)
349 << MDiagClass(nclass)
350 << MDiagFunction(function));
351
352 // one level exception
353 try {
354 NCBI_EXCEPTION_VAR(expt, CException, eUnknown, "exception");
355 expt.SetModule(module);
356 expt.SetClass(nclass);
357 expt.SetFunction(function);
358 NCBI_EXCEPTION_THROW(expt);
359 }
360 catch (const CException& ex) {
361 NCBI_REPORT_EXCEPTION(x_MakeMessage(exceptmsg,
362 idx,
363 TCase(test_number, 1)), ex);
364 }
365
366
367 #if defined(NCBI_COMPILER_WORKSHOP)
368 # if NCBI_COMPILER_VERSION == 530 || NCBI_COMPILER_VERSION == 550
369 // Workshop 5.3 and 5.5 have MT-unsafe throw. To avoid test failures
370 // use mutex.
371 DEFINE_STATIC_FAST_MUTEX(s_ThrowMutex);
372 CFastMutexGuard guard(s_ThrowMutex);
373 # endif
374 #endif
375
376
377 // two level exceptions
378 try {
379 try {
380 NCBI_EXCEPTION_VAR(expt, CException, eUnknown, "exception1");
381 expt.SetModule(module);
382 expt.SetClass(nclass);
383 expt.SetFunction(function);
384 NCBI_EXCEPTION_THROW(expt);
385 }
386 catch (const CException& ex) {
387 NCBI_EXCEPTION_VAR_EX(e2, &ex, CException, eUnknown, "exception2");
388 e2.SetModule(module);
389 e2.SetClass(nclass);
390 e2.SetFunction(function);
391 NCBI_EXCEPTION_THROW(e2);
392 }
393 }
394 catch (const CException& ex) {
395 NCBI_REPORT_EXCEPTION(x_MakeMessage(secondmsg,
396 idx,
397 TCase(test_number, 2)), ex);
398 }
399 }
400
401
Thread_Run(int idx)402 bool CTestDiagApp::Thread_Run(int idx)
403 {
404 int testnum = 0;
405
406 x_PrintMessages(testnum++, idx, "module", "class", "function");
407 x_PrintMessages(testnum++, idx, "module", "class", "" );
408 x_PrintMessages(testnum++, idx, "module", "", "function");
409 x_PrintMessages(testnum++, idx, "module", "", "" );
410 x_PrintMessages(testnum++, idx, "", "class", "function");
411 x_PrintMessages(testnum++, idx, "", "class", "" );
412 x_PrintMessages(testnum++, idx, "", "", "function");
413 x_PrintMessages(testnum++, idx, "", "", "" );
414 x_PrintMessages(testnum++, idx, "foo", "boo", "doo" );
415
416 return true;
417 }
418
419
TestApp_Args(CArgDescriptions & args)420 bool CTestDiagApp::TestApp_Args(CArgDescriptions& args)
421 {
422 args.AddPositional("case_number",
423 "case number",
424 CArgDescriptions::eInteger);
425
426 args.SetConstraint("case_number",
427 new CArgAllow_Integers(1, 47));
428
429 return true;
430 }
431
432
TestApp_Init(void)433 bool CTestDiagApp::TestApp_Init(void)
434 {
435 // Get arguments
436 const CArgs& args = GetArgs();
437 SetCase( args["case_number"].AsInteger() );
438
439 NcbiCout << NcbiEndl
440 << "Testing NCBIDIAG FILTERING case ("
441 << m_Case << ") with "
442 << NStr::IntToString(s_NumThreads)
443 << " threads..."
444 << NcbiEndl;
445
446 SetDiagPostFlag(eDPF_Location);
447 SetDiagPostFlag(eDPF_MergeLines);
448 // Output to the string stream -- to verify the result
449 SetDiagStream(&s_Sout);
450
451 return true;
452 }
453
454
TestApp_Exit(void)455 bool CTestDiagApp::TestApp_Exit(void)
456 {
457 // Cleanup
458 SetDiagStream(0);
459
460 // Verify the result
461 typedef list<string> TStrings;
462 TStrings messages;
463
464 // Get the list of messages
465 string s = CNcbiOstrstreamToString(s_Sout);
466 NStr::Split(s, "\r\n", messages,
467 NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
468
469 bool result = true;
470 ITERATE(TStrings, i, messages) {
471 size_t beg = i->find("!=");
472 size_t end = i->find("=!");
473 if (beg == string::npos || end == string::npos)
474 continue;
475
476 string full_msg = i->substr(beg + 2, end - beg - 2);
477 CNcbiIstrstream in( full_msg );
478 TCase ncase;
479 int thread;
480 string msg;
481
482 in >> ncase.first >> ncase.second >> thread;
483 getline(in, msg);
484
485 if ( !m_CaseExpected.erase(make_pair(ncase, thread)) ) {
486 NcbiCout << "Error: unexpected message \""
487 << msg << "\" for thread " << thread << NcbiEndl;
488 result = false;
489 }
490
491 }
492
493 ITERATE(TThreads, i, m_CaseExpected) {
494 NcbiCout << "Error: message \"" << m_Messages[ i->first ]
495 << "\" for thread " << i->second << " not found"
496 << NcbiEndl;
497 result = false;
498 }
499
500 if (result) {
501 NcbiCout << "Test completed successfully!"
502 << NcbiEndl << NcbiEndl;
503 }
504
505 return result;
506 }
507
508
509
510 /////////////////////////////////////////////////////////////////////////////
511 // MAIN
512
main(int argc,const char * argv[])513 int main(int argc, const char* argv[])
514 {
515 return CTestDiagApp().AppMain(argc, argv);
516 }
517