diff options
author | Mike Buland <eichlan@xagasoft.com> | 2023-08-08 16:33:38 -0700 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2023-08-08 16:33:38 -0700 |
commit | c7e1277ecaf40c6d8ee945418a306f5b15189b97 (patch) | |
tree | 05a04473ffe90a76a4e7dd170c221141fea87b7e /src/tools | |
parent | 7c36f58654f1b238d1b416927c9485a151216b1b (diff) | |
download | libbu++-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 'src/tools')
-rw-r--r-- | src/tools/mkunit.cpp | 34 | ||||
-rw-r--r-- | src/tools/rununits.cpp | 186 |
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 | |||
11 | Bu::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 | |||
26 | Bu::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 | |||
51 | typedef Bu::List<Bu::Blob> BlobList; | ||
52 | |||
53 | BlobList 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 | |||
83 | int 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 | } | ||