issue#11: cds: changed __CDS_ guard prefix to CDSLIB_ for all .h files
[libcds.git] / tests / cppunit / test_main.cpp
1 //$$CDS-header$$
2
3 /*
4  * Copyright (c) 2003, 2004
5  * Zdenek Nemec
6  *
7  * This material is provided "as is", with absolutely no warranty expressed
8  * or implied. Any use is at your own risk.
9  *
10  * Permission to use or copy this software for any purpose is hereby granted
11  * without fee, provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  *
16  */
17
18 /*
19 #if defined(_DEBUG) && _MSC_VER == 1500
20 #    define _CRTDBG_MAP_ALLOC
21 #    include <stdlib.h>
22 #    include <crtdbg.h>
23 #endif
24 */
25
26 #include "cppunit/cppunit_proxy.h"
27 #include "cppunit/file_reporter.h"
28
29 #include <cds/init.h>
30 #include <cds/gc/hp.h>
31 #include <cds/gc/dhp.h>
32 #include <cds/urcu/general_instant.h>
33 #include <cds/urcu/general_buffered.h>
34 #include <cds/urcu/general_threaded.h>
35 #include <cds/urcu/signal_buffered.h>
36 #include <cds/urcu/signal_threaded.h>
37 #include <cds/os/topology.h>
38
39 #include "stdio.h"
40 #include <fstream>
41 #include <iostream>
42 #include <set>
43 #include <boost/date_time/posix_time/posix_time.hpp>
44 #include <boost/thread/mutex.hpp>
45
46 // Visual leak detector (see http://vld.codeplex.com/)
47 #if defined(CDS_USE_VLD) && CDS_COMPILER == CDS_COMPILER_MSVC
48 #   ifdef _DEBUG
49 #       include <vld.h>
50 #   endif
51 #endif
52
53
54 std::ostream& operator << (std::ostream& s, const cds::gc::hp::GarbageCollector::InternalState& stat)
55 {
56     s << "\nHZP GC internal state:"
57         << "\n\t            HP record allocated=" << stat.nHPRecAllocated
58         << "\n\t                HP records used=" << stat.nHPRecUsed
59         << "\n\t        Total retired ptr count=" << stat.nTotalRetiredPtrCount
60         << "\n\t Retired ptr in free HP records=" << stat.nRetiredPtrInFreeHPRecs
61         << "\n\tEvents:"
62         << "\n\t              HPRec allocations=" << stat.evcAllocHPRec
63         << "\n\t            HPRec retire events=" << stat.evcRetireHPRec
64         << "\n\tnew HPRec allocations from heap=" << stat.evcAllocNewHPRec
65         << "\n\t                HPRec deletions=" << stat.evcDeleteHPRec
66         << "\n\t                Scan call count=" << stat.evcScanCall
67         << "\n\t            HelpScan call count=" << stat.evcHelpScanCall
68         << "\n\t       Scan calls from HelpScan=" << stat.evcScanFromHelpScan
69         << "\n\t       retired object deleting=" << stat.evcDeletedNode
70         << "\n\t        guarded object on Scan=" << stat.evcDeferredNode
71         << std::endl;
72
73     return s;
74 }
75
76 namespace CppUnitMini
77 {
78   int TestCase::m_numErrors = 0;
79   int TestCase::m_numTests = 0;
80   std::vector<std::string>  TestCase::m_arrStrings;
81   bool TestCase::m_bPrintGCState = false;
82   std::string TestCase::m_strTestDataDir(".");
83   Config TestCase::m_Cfg;
84   bool TestCase::m_bExactMatch = false;
85
86   TestCase * TestCase::m_pCurTestCase = nullptr;
87
88   TestCase *TestCase::m_root = 0;
89   Reporter *TestCase::m_reporter = 0;
90
91   void TestCase::registerTestCase(TestCase *in_testCase) {
92     in_testCase->m_next = m_root;
93     m_root = in_testCase;
94   }
95
96   int TestCase::run(Reporter *in_reporter, const char *in_testName, bool invert)
97   {
98     TestCase::m_reporter = in_reporter;
99
100     m_numErrors = 0;
101     m_numTests = 0;
102
103     TestCase *tmp = m_root;
104     while (tmp != 0) {
105       m_pCurTestCase = tmp;
106       try {
107         tmp->myRun(in_testName, invert);
108       } catch ( std::exception& ex ) {
109         in_reporter->message( "EXCEPTION: ");
110         in_reporter->message( ex.what() );
111       }
112       tmp = tmp->m_next;
113     }
114
115     return m_numErrors;
116   }
117
118   bool TestCase::shouldRunThis(const char *in_desiredTest, const char *in_className, const char *in_functionName,
119                        bool invert, bool explicit_test, bool &do_progress) 
120   {
121       if ((in_desiredTest) && (in_desiredTest[0] != '\0')) {
122         do_progress = false;
123         const char *ptr = strstr(in_desiredTest, "::");
124         if (ptr) {
125             bool match;
126             if ( m_bExactMatch ) {
127                 match = (strncmp( in_desiredTest, in_className, strlen( in_className )) == 0 && in_desiredTest[strlen( in_className )] == ':')
128                      && (strcmp( ptr + 2, in_functionName ) == 0);
129             }
130             else {
131                 match = (strncmp( in_desiredTest, in_className, strlen( in_className )) == 0 && in_desiredTest[strlen( in_className )] == ':')
132                      && (strncmp( ptr + 2, in_functionName, strlen( ptr + 2 ) ) == 0);
133             }
134             // Invert shall not make explicit test run:
135             return invert ? (match ? !match : !explicit_test)
136                           : match;
137         }
138         bool match = (strcmp(in_desiredTest, in_className) == 0);
139         do_progress = match;
140         return !explicit_test && (match == !invert);
141       }
142       do_progress = true;
143       return !explicit_test;
144   }
145
146
147   void TestCase::print_gc_state()
148   {
149       if ( m_bPrintGCState ) {
150           {
151               cds::gc::hp::GarbageCollector::InternalState stat;
152               std::cout << cds::gc::hp::GarbageCollector::instance().getInternalState( stat ) << std::endl;
153           }
154       }
155   }
156
157   void Config::load( const char * fileName )
158   {
159       std::ifstream s;
160       s.open( fileName );
161       if ( !s.is_open() ) {
162           std::cerr << "WARNING: Cannot open test cfg file " << fileName
163               << "\n\tUse default settings"
164               << std::endl;
165           return;
166       }
167
168       std::cout << "Using test config file: " << fileName << std::endl;
169
170       char buf[ 4096 ];
171
172       TestCfg * pMap = nullptr;
173       while ( !s.eof() ) {
174           s.getline( buf, sizeof(buf)/sizeof(buf[0]) );
175           char * pszStr = buf;
176           // trim left
177           while ( *pszStr != 0 && (*pszStr == ' ' || *pszStr == '\t' )) ++pszStr;
178           // trim right
179           char * pszEnd = strchr( pszStr, 0 );
180           if ( pszEnd == pszStr )    // empty srtring
181               continue;
182           --pszEnd;
183           while ( pszEnd != pszStr && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r' )) --pszEnd;
184
185           if ( pszStr == pszEnd  )    // empty string
186               continue;
187
188           pszEnd[1] = 0;
189
190           if ( *pszStr == '#' )    // comment
191               continue;
192
193           if ( *pszStr == '[' && *pszEnd == ']' ) {    // chapter header
194               *pszEnd = 0;
195               pMap = &( m_Cfg[ pszStr + 1 ] );
196               continue;
197           }
198
199           if ( !pMap )
200               continue;
201
202           char * pszEq = strchr( pszStr, '=' );
203           if ( !pszEq )
204               continue;
205           if ( pszEq == pszStr )
206               continue;
207
208           pszEnd = pszEq;
209           while ( pszStr <= --pszEnd && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r') );
210
211           if ( pszEnd <= pszStr )
212               continue;
213           pszEnd[1] = 0;
214           pMap->m_Cfg[ pszStr ] = pszEq + 1;
215       }
216       s.close();
217   }
218
219   std::vector<std::string> const &    TestCase::getTestStrings()
220   {
221       if ( m_arrStrings.empty() ) {
222           std::string strTestDir = m_strTestDataDir;
223
224           std::ifstream fDict;
225           char bufLine[1024];
226           std::cout << "Loading test data " << strTestDir << "/dictionary.txt..." << std::endl;
227           fDict.open( (strTestDir + "/dictionary.txt").c_str() );
228           if ( fDict.is_open() ) {
229               cds::OS::Timer timer;
230               std::string str;
231               fDict >> str    ;   // number of lines in file
232
233               // Assume that dictionary.txt does not contain doubles.
234               CppUnitMini::TestCase::m_arrStrings.reserve( atol(str.c_str()) );
235               while ( !fDict.eof() ) {
236                   fDict.getline( bufLine, sizeof(bufLine)/sizeof(bufLine[0]) );
237                   if ( bufLine[0] )
238                     m_arrStrings.push_back( bufLine );
239               }
240               fDict.close();
241
242               std::cout << "  Duration=" << timer.duration() << " String count " << CppUnitMini::TestCase::m_arrStrings.size() << std::endl;
243           }
244           else
245               std::cout << "  Failed, file not found" << std::endl;
246
247       }
248       return m_arrStrings;
249   }
250 }
251
252 static void usage(const char* name)
253 {
254   printf("Usage : %s [-t=<class>[::<test>]] [-x=<class>[::<test>]] [-f=<file>] [-m]\n", name);
255   printf("\t[-t=<class>[::<test>]] : test class or class::test to execute\n");
256   printf("\t[-x=<class>[::<test>]] : test class or class::test to exclude from execution\n");
257   printf("\t[-d=dir] : test data directory (default is .)\n");
258   printf( "\t[-f=<file>] : output file\n" );
259   //printf(";\n\t[-m] : monitor test execution, display time duration for each test\n");
260   printf( "\t[-exact-match] : class::test should be exactly matched to existing test\n" );
261   printf("\t[-gc_state] : print gc state after each test\n");
262   printf("\t[-cfg=<file>] : config file name for tests\n");
263 }
264
265 int main(int argc, char** argv)
266 {
267
268 #ifdef CDS_MSVC_MEMORY_LEAKS_DETECTING_ENABLED
269     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
270 #endif
271
272   // CppUnit(mini) test launcher
273   // command line option syntax:
274   // test [Options]
275   // where Options are
276   //  -t=CLASS[::TEST]    run the test class CLASS or member test CLASS::TEST
277   //  -x=CLASS[::TEST]    run all except the test class CLASS or member test CLASS::TEST
278   //  -d=dir              test data directory (default is .)
279   //  -f=FILE             save output in file FILE instead of stdout
280   //  -m                  monitor test(s) execution
281   //  -gc_state           print GC state after test
282   const char *fileName = 0;
283   const char *testName = "";
284   const char *xtestName = "";
285   const char *testDataDir = ".";
286   const char *cfgFileName =
287 #ifdef _DEBUG
288       "test-debug.conf"
289 #else
290       "test.conf"
291 #endif
292 ;
293   bool doMonitoring = false;
294
295   for (int i = 1; i < argc; ++i) {
296     if (argv[i][0] == '-') {
297       if ( strncmp(argv[i], "-t=", 3) == 0 ) {
298         testName = argv[i]+3;
299         continue;
300       }
301       else if ( strncmp(argv[i], "-f=", 3) == 0 ) {
302         fileName = argv[i]+3;
303         continue;
304       }
305       else if ( strncmp(argv[i], "-x=", 3) == 0 ) {
306         xtestName = argv[i]+3;
307         continue;
308       }
309       else if ( strncmp(argv[i], "-d=", 3) == 0 ) {
310           testDataDir = argv[i] + 3;
311           continue;
312       }
313       else if ( strncmp( argv[i], "-m", 2 ) == 0 ) {
314           doMonitoring = true;
315           continue;
316       }
317       else if ( strncmp( argv[i], "-exact-match", 12 ) == 0 ) {
318           CppUnitMini::TestCase::m_bExactMatch = true;
319           continue;
320       }
321       else if ( strncmp(argv[i], "-gc_state", 9) == 0 ) {
322           CppUnitMini::TestCase::m_bPrintGCState = true;
323           continue;
324       }
325       else if( strncmp(argv[i], "-cfg=", 5) == 0 ) {
326           cfgFileName = argv[i] + 5;
327           continue;
328       }
329     }
330
331     // invalid option, we display normal usage.
332     usage(argv[0]);
333     return 1;
334
335   }
336
337   {
338     boost::posix_time::ptime cur( boost::posix_time::second_clock::local_time());
339
340     std::cout << "libcds version " << CDS_VERSION_STRING << "\n";
341     std::cout << "Test started " << cur << std::endl;
342   }
343
344
345   CppUnitMini::TestCase::m_strTestDataDir = testDataDir;
346
347   CppUnitMini::Reporter* reporter;
348   if (fileName != 0)
349     reporter = new CppUnitMini::FileReporter(fileName, doMonitoring);
350   else
351     reporter = new CppUnitMini::FileReporter(stdout, doMonitoring);
352
353   // Load config params
354   CppUnitMini::TestCase::m_Cfg.load( cfgFileName );
355
356   // Init CDS runtime
357   cds::Initialize();
358
359   int num_errors;
360   {
361       size_t nHazardPtrCount = 0;
362       {
363         CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
364         nHazardPtrCount = cfg.getULong( "hazard_pointer_count", 0 );
365       }
366
367       // Safe reclamation schemes
368       cds::gc::HP hzpGC( nHazardPtrCount );
369       cds::gc::DHP dhpGC;
370
371       // RCU varieties
372       typedef cds::urcu::gc< cds::urcu::general_instant<> >    rcu_gpi;
373       rcu_gpi   gpiRCU;
374
375       typedef cds::urcu::gc< cds::urcu::general_buffered<> >    rcu_gpb;
376       rcu_gpb   gpbRCU;
377
378       typedef cds::urcu::gc< cds::urcu::general_threaded<> >    rcu_gpt;
379       rcu_gpt   gptRCU;
380
381 #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
382       typedef cds::urcu::gc< cds::urcu::signal_buffered<> >    rcu_shb;
383       rcu_shb   shbRCU( 256, SIGUSR1 );
384
385       typedef cds::urcu::gc< cds::urcu::signal_threaded<> >    rcu_sht;
386       rcu_sht   shtRCU( 256, SIGUSR2 );
387 #endif
388
389       // System topology
390       {
391           std::cout
392               << "System topology:\n"
393               << "    Logical processor count: " << cds::OS::topology::processor_count() << "\n";
394           std::cout << std::endl;
395       }
396
397       {
398         CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
399         std::string strHZPScanStrategy = cfg.get( "HZP_scan_strategy", std::string("classic") );
400         if ( strHZPScanStrategy == "inplace" )
401             hzpGC.setScanType( cds::gc::HP::scan_type::inplace );
402         else if ( strHZPScanStrategy == "classic" )
403             hzpGC.setScanType( cds::gc::HP::scan_type::classic );
404         else {
405             std::cout << "Error value of HZP_scan_strategy in General section of test config\n";
406         }
407
408         switch (hzpGC.getScanType()) {
409         case cds::gc::HP::scan_type::inplace:
410             std::cout << "Use in-place scan strategy for Hazard Pointer memory reclamation algorithm\n";
411             break;
412         case cds::gc::HP::scan_type::classic:
413             std::cout << "Use classic scan strategy for Hazard Pointer memory reclamation algorithm\n";
414             break;
415         default:
416             std::cout << "ERROR: use unknown scan strategy for Hazard Pointer memory reclamation algorithm\n";
417             break;
418         }
419
420         std::cout << "     Hazard Pointer count: " << hzpGC.max_hazard_count() << "\n"
421                   << "  Max thread count for HP: " << hzpGC.max_thread_count() << "\n"
422                   << "Retired HP array capacity: " << hzpGC.retired_array_capacity() << "\n";
423       }
424
425       if ( CppUnitMini::TestCase::m_bPrintGCState ) {
426         cds::gc::hp::GarbageCollector::InternalState stat;
427         cds::gc::hp::GarbageCollector::instance().getInternalState( stat );
428
429         std::cout << "HP constants:"
430             << "\n\tHP count per thread=" << stat.nHPCount
431             << "\n\tMax thread count=" << stat.nMaxThreadCount
432             << "\n\tMax retired pointer count per thread=" << stat.nMaxRetiredPtrCount
433             << "\n\tHP record size in bytes=" << stat.nHPRecSize
434             << "\n" << std::endl;
435       }
436
437     // Attach main thread to CDS GC
438     cds::threading::Manager::attachThread();
439
440     if (xtestName[0] != 0)
441         num_errors = CppUnitMini::TestCase::run(reporter, xtestName, true);
442     else
443         num_errors = CppUnitMini::TestCase::run(reporter, testName);
444
445     // Detach main thread from CDS GC
446     cds::threading::Manager::detachThread();
447
448   }
449
450   // Finalize CDS runtime
451   cds::Terminate();
452
453   reporter->printSummary();
454   delete reporter;
455
456   return num_errors;
457 }
458
459 // See doc/README.intel for explanation about this code
460 #if defined (STLPORT) && defined (__ICL) && (__ICL >= 900) && \
461             (_STLP_MSVC_LIB < 1300) && defined (_STLP_USE_DYNAMIC_LIB)
462 #  include <exception>
463
464 #  undef std
465 namespace std
466 {
467   void _STLP_CALL unexpected() {
468     unexpected_handler hdl;
469     set_unexpected(hdl = set_unexpected((unexpected_handler)0));
470     hdl();
471   }
472 }
473 #endif