aboutsummaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2023-08-08 16:33:38 -0700
committerMike Buland <eichlan@xagasoft.com>2023-08-08 16:33:38 -0700
commitc7e1277ecaf40c6d8ee945418a306f5b15189b97 (patch)
tree05a04473ffe90a76a4e7dd170c221141fea87b7e /src/tools
parent7c36f58654f1b238d1b416927c9485a151216b1b (diff)
downloadlibbu++-c7e1277ecaf40c6d8ee945418a306f5b15189b97.tar.gz
libbu++-c7e1277ecaf40c6d8ee945418a306f5b15189b97.tar.bz2
libbu++-c7e1277ecaf40c6d8ee945418a306f5b15189b97.tar.xz
libbu++-c7e1277ecaf40c6d8ee945418a306f5b15189b97.zip
Unit test augmentations and harness.
Added some features to the mkunit program, including cleanup routine support. Added reporting modes for the UnitSuite class, and it can now generate machine readable reports. Added a new program, rununits that runs all unit tests and generates a synopsis of what you really care about at the end, issues!
Diffstat (limited to '')
-rw-r--r--src/tools/mkunit.cpp34
-rw-r--r--src/tools/rununits.cpp186
2 files changed, 217 insertions, 3 deletions
diff --git a/src/tools/mkunit.cpp b/src/tools/mkunit.cpp
index f6ea669..5711c42 100644
--- a/src/tools/mkunit.cpp
+++ b/src/tools/mkunit.cpp
@@ -42,6 +42,7 @@ enum TokType
42 tokTest, 42 tokTest,
43 tokChar, 43 tokChar,
44 tokBlock, 44 tokBlock,
45 tokCleanup,
45 tokEof 46 tokEof
46}; 47};
47 48
@@ -54,6 +55,7 @@ Bu::Formatter &operator<<( Bu::Formatter &f, TokType t )
54 case tokTest: return f << "tokTest"; 55 case tokTest: return f << "tokTest";
55 case tokChar: return f << "tokChar"; 56 case tokChar: return f << "tokChar";
56 case tokBlock: return f << "tokBlock"; 57 case tokBlock: return f << "tokBlock";
58 case tokCleanup: return f << "tokCleanup";
57 case tokEof: return f << "tokEof"; 59 case tokEof: return f << "tokEof";
58 } 60 }
59 61
@@ -193,6 +195,8 @@ public:
193 { 195 {
194 if( sTok == "test" ) 196 if( sTok == "test" )
195 return tokTest; 197 return tokTest;
198 else if( sTok == "cleanup" )
199 return tokCleanup;
196 else 200 else
197 { 201 {
198 v = sTok; 202 v = sTok;
@@ -361,6 +365,15 @@ public:
361 s.lTest.append( t ); 365 s.lTest.append( t );
362 } 366 }
363 break; 367 break;
368
369 case tokCleanup:
370 {
371 if( nextToken( v, sWs, iL, iC ) != tokBlock )
372 throw Bu::ExceptionBase("%d:%d: Expected "
373 "{...} block.",
374 iL, iC );
375 }
376 break;
364 377
365 case tokChar: 378 case tokChar:
366 if( v.get<char>() == '}' ) 379 if( v.get<char>() == '}' )
@@ -438,7 +451,7 @@ public:
438 for( TestList::iterator i = s.lTest.begin(); i; i++ ) 451 for( TestList::iterator i = s.lTest.begin(); i; i++ )
439 { 452 {
440 f << "\t\tadd( static_cast<Bu::UnitSuite::Test>(" 453 f << "\t\tadd( static_cast<Bu::UnitSuite::Test>("
441 "&" << sClass << "::" << (*i).sName << "), \"" 454 "&" << sClass << "::_test_" << (*i).sName << "), \""
442 << (*i).sName << "\", Bu::UnitSuite::" 455 << (*i).sName << "\", Bu::UnitSuite::"
443 "expectPass );" << f.nl; 456 "expectPass );" << f.nl;
444 } 457 }
@@ -481,9 +494,24 @@ public:
481 "{...} block.", 494 "{...} block.",
482 iL, iC ); 495 iL, iC );
483 496
484 f << "\tvoid " << t.sName << "()" 497 f << "void _test_" << t.sName << "()"
498 << f.nl << "#line " << iL
499 << " \"" << sIn << "\"" << f.nl << " "
500 << v << f.nl;
501 }
502 break;
503
504 case tokCleanup:
505 {
506 fOut.write( sWs );
507 if( nextToken( v, sWs, iL, iC ) != tokBlock )
508 throw Bu::ExceptionBase("%d:%d: Expected "
509 "{...} block.",
510 iL, iC );
511
512 f << "void cleanup()"
485 << f.nl << "#line " << iL 513 << f.nl << "#line " << iL
486 << " \"" << sIn << "\"" << f.nl 514 << " \"" << sIn << "\"" << f.nl << " "
487 << v << f.nl; 515 << v << f.nl;
488 } 516 }
489 break; 517 break;
diff --git a/src/tools/rununits.cpp b/src/tools/rununits.cpp
new file mode 100644
index 0000000..769d3ab
--- /dev/null
+++ b/src/tools/rununits.cpp
@@ -0,0 +1,186 @@
1#include <bu/sio.h>
2#include <bu/json.h>
3#include <bu/process.h>
4#include <bu/optparser.h>
5#include <bu/blob.h>
6#include <bu/blobbuilder.h>
7
8#include <sys/types.h>
9#include <dirent.h>
10
11Bu::Blob getSuiteName( const Bu::Blob &bSuiteExec )
12{
13 Bu::Process proc(
14 Bu::Process::StdOut,
15 bSuiteExec.getData(),
16 bSuiteExec.getData(),
17 "--print-name",
18 NULL
19 );
20
21 Bu::String sResult = proc.readAll().trimWhitespace();
22 Bu::Blob bRet( sResult.getStr(), sResult.getSize() );
23 return bRet;
24}
25
26Bu::Json *runSuite( const Bu::Blob &bSuiteExec )
27{
28 Bu::Process proc(
29 Bu::Process::StdOut,
30 bSuiteExec.getData(),
31 bSuiteExec.getData(),
32 "--interop",
33 NULL
34 );
35 Bu::BlobBuilder bbReport;
36 while( proc.isRunning() )
37 {
38 int iRead = 0;
39 char dRead[4096];
40 iRead = proc.read( dRead, 4096 );
41 if( iRead > 0 )
42 {
43 bbReport.append( dRead, iRead );
44 }
45 }
46 Bu::Json *pReport = new Bu::Json();
47 pReport->parse( bbReport.getBlob() );
48 return pReport;
49}
50
51typedef Bu::List<Bu::Blob> BlobList;
52
53BlobList getSuitePaths( const Bu::Blob &bDir )
54{
55 BlobList lPaths;
56 DIR *dir = opendir( bDir.getData() );
57 if( dir == NULL )
58 {
59 Bu::println("Could not open directory: %1").arg( bDir );
60 return lPaths;
61 }
62 struct dirent *de = NULL;
63 while( (de = readdir( dir )) != NULL )
64 {
65 if( de->d_type != DT_REG )
66 continue;
67
68 Bu::BlobBuilder bbPath;
69 bbPath.append( bDir );
70 bbPath.append("/");
71 bbPath.append( de->d_name );
72 Bu::Blob bPath = bbPath.getBlob();
73 if( access( bPath.getData(), X_OK ) != 0 )
74 continue;
75
76 lPaths.append( bPath );
77 }
78 closedir( dir );
79
80 return lPaths;
81}
82
83int main( int /*argc*/, char * /*argv*/[] )
84{
85 Bu::Blob bDir("unit");
86 BlobList lPaths = getSuitePaths( bDir );
87
88 int iNumLen = Bu::String("%1").arg( lPaths.getSize() ).end().getSize();
89 int iMaxSuiteName = 0;
90 Bu::Hash<Bu::Blob, Bu::Blob> hName;
91 for( BlobList::iterator i = lPaths.begin(); i; i++ )
92 {
93 Bu::Blob bSuiteName = getSuiteName( *i );
94 if( iMaxSuiteName < bSuiteName.getSize() )
95 iMaxSuiteName = bSuiteName.getSize();
96 hName.insert( *i, bSuiteName );
97 }
98
99 Bu::List<Bu::Json *> lReport;
100
101 int iTest = 0;
102 for( BlobList::iterator i = lPaths.begin(); i; i++ )
103 {
104 iTest++;
105 Bu::Blob bSuiteName = hName.get( *i );
106 Bu::print("\rRunning test suite [%1/%2]: %3")
107 .arg( iTest, Bu::Fmt().width( iNumLen ).right() )
108 .arg( lPaths.getSize(), Bu::Fmt().width( iNumLen ) )
109 .arg( bSuiteName, Bu::Fmt().width( iMaxSuiteName ).fill(' ').left() );
110 Bu::sio << Bu::sio.flush;
111
112 Bu::Json *pReport = runSuite( (*i) );
113 int iUnexpected = 0;
114 int iTotal = 0;
115 iTotal = (*pReport)["tests"].getSize();
116 for( int j = 0; j < iTotal; j++ )
117 {
118 if( !((*pReport)["tests"][j]["expected"].getString() ==
119 (*pReport)["tests"][j]["result"].getString()) )
120 {
121 iUnexpected++;
122 }
123 }
124 if( iUnexpected == 0 )
125 {
126 delete pReport;
127 }
128 else
129 {
130 lReport.append( pReport );
131 }
132 }
133 Bu::println("\rCompleted %1 unit test suites.%2")
134 .arg( lPaths.getSize(), Bu::Fmt().width( iNumLen ) )
135 .arg( Bu::Blob(""), Bu::Fmt().width( iMaxSuiteName ).fill(' ').left() );
136
137 if( lReport.getSize() == 0 )
138 {
139 Bu::println("\nNothing unexpected in unit tests.");
140 }
141 else
142 {
143 for( Bu::List<Bu::Json *>::iterator i = lReport.begin(); i; i++ )
144 {
145 Bu::println("\nUnexpected results in: %1")
146 .arg( (**i)["suite"].getString().get() );
147
148 for( Bu::Json::iterator iTest = (**i)["tests"].begin();
149 iTest; iTest++ )
150 {
151 if( (**iTest)["expected"].getString() ==
152 (**iTest)["result"].getString() )
153 {
154 continue;
155 }
156
157 Bu::println(" %1: unexpected %2")
158 .arg( (**iTest)["name"].getString().get() )
159 .arg( (**iTest)["result"].getString().get() );
160 if( (**iTest).has("fail") )
161 {
162 if( (**iTest)["fail"].has("action") )
163 {
164 Bu::println(" unitTest( %1 );")
165 .arg( (**iTest)["fail"]["action"].getString().get() );
166 Bu::println(" at %1:%2")
167 .arg( (**iTest)["fail"]["file"].getString().get() )
168 .arg( (int)(**iTest)["fail"]["line"].getNumber() );
169 }
170 else if( (**iTest)["fail"].has("what") )
171 {
172 Bu::println(" Unexpected exception: %1")
173 .arg( (**iTest)["fail"]["what"].getString().get() );
174 }
175 }
176 else
177 {
178 Bu::println(" No further details.");
179 }
180 }
181 delete *i;
182 }
183 }
184
185 return 0;
186}