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