diff options
Diffstat (limited to 'src/stable/unitsuite.cpp')
-rw-r--r-- | src/stable/unitsuite.cpp | 337 |
1 files changed, 255 insertions, 82 deletions
diff --git a/src/stable/unitsuite.cpp b/src/stable/unitsuite.cpp index a38c77a..b2544f2 100644 --- a/src/stable/unitsuite.cpp +++ b/src/stable/unitsuite.cpp | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "bu/file.h" | 9 | #include "bu/file.h" |
10 | #include "bu/sio.h" | 10 | #include "bu/sio.h" |
11 | #include "bu/optparser.h" | 11 | #include "bu/optparser.h" |
12 | #include "bu/json.h" | ||
12 | #include <stdlib.h> | 13 | #include <stdlib.h> |
13 | #include <time.h> | 14 | #include <time.h> |
14 | 15 | ||
@@ -18,28 +19,33 @@ using namespace Bu; | |||
18 | 19 | ||
19 | Bu::UnitSuite::UnitSuite() : | 20 | Bu::UnitSuite::UnitSuite() : |
20 | iOptions( 0 ), | 21 | iOptions( 0 ), |
21 | iNameWidth( 0 ) | 22 | pReport( NULL ) |
22 | { | 23 | { |
23 | } | 24 | } |
24 | 25 | ||
25 | Bu::UnitSuite::UnitSuite( int iOptions ) : | 26 | Bu::UnitSuite::UnitSuite( int iOptions ) : |
26 | iOptions( iOptions ), | 27 | iOptions( iOptions ) |
27 | iNameWidth( 0 ) | ||
28 | { | 28 | { |
29 | } | 29 | } |
30 | 30 | ||
31 | Bu::UnitSuite::~UnitSuite() | 31 | Bu::UnitSuite::~UnitSuite() |
32 | { | 32 | { |
33 | delete pReport; | ||
33 | } | 34 | } |
34 | 35 | ||
35 | int Bu::UnitSuite::run( int argc, char *argv[] ) | 36 | int Bu::UnitSuite::run( int argc, char *argv[] ) |
36 | { | 37 | { |
37 | bool bCleanup = true; | 38 | bool bCleanup = true; |
39 | bool bInterop = false; | ||
38 | OptParser p; | 40 | OptParser p; |
39 | p.addOption( Bu::slot( this, &Bu::UnitSuite::onListCases ), 'l', "list", | 41 | p.addOption( Bu::slot( this, &Bu::UnitSuite::onListCases ), 'l', "list", |
40 | "List available test cases." ); | 42 | "List available test cases." ); |
43 | p.addOption( Bu::slot( this, &Bu::UnitSuite::onPrintName ), "print-name", | ||
44 | "Print the internal name of this test suite."); | ||
41 | p.addOption( bCleanup, "no-cleanup", "Don't erase temp files."); | 45 | p.addOption( bCleanup, "no-cleanup", "Don't erase temp files."); |
42 | p.setOverride( "no-cleanup", false ); | 46 | p.setOverride( "no-cleanup", false ); |
47 | p.addOption( bInterop, "interop", "Output machine parsable json."); | ||
48 | p.setOverride( "interop", true ); | ||
43 | p.setNonOption( Bu::slot( this, &Bu::UnitSuite::onAddTest ) ); | 49 | p.setNonOption( Bu::slot( this, &Bu::UnitSuite::onAddTest ) ); |
44 | p.addHelpOption(); | 50 | p.addHelpOption(); |
45 | p.parse( argc, argv ); | 51 | p.parse( argc, argv ); |
@@ -56,108 +62,58 @@ int Bu::UnitSuite::run( int argc, char *argv[] ) | |||
56 | lTests = lSub; | 62 | lTests = lSub; |
57 | } | 63 | } |
58 | 64 | ||
59 | int iEPass = 0; | 65 | if( bInterop ) |
60 | int iEFail = 0; | 66 | pReport = new ReportJson(); |
61 | int iUPass = 0; | 67 | else |
62 | int iUFail = 0; | 68 | pReport = new ReportConsole(); |
69 | pReport->suiteStarting( *this, lTests ); | ||
70 | |||
63 | for( TestList::iterator i = lTests.begin(); i != lTests.end(); i++ ) | 71 | for( TestList::iterator i = lTests.begin(); i != lTests.end(); i++ ) |
64 | { | 72 | { |
65 | sio << Fmt( iNameWidth+3, Fmt::Left ).fill('.') << i->sName | ||
66 | << sio.flush; | ||
67 | try | 73 | try |
68 | { | 74 | { |
69 | iStepCount = -1; | 75 | iStepCount = -1; |
70 | iProgress = 0; | 76 | iProgress = 0; |
77 | if( pReport ) | ||
78 | pReport->testStarting( *i ); | ||
71 | (this->*(i->fTest))(); | 79 | (this->*(i->fTest))(); |
72 | switch( i->eExpect ) | 80 | if( pReport ) |
73 | { | 81 | pReport->testEnded( *i ); |
74 | case expectPass: | ||
75 | sio << "pass." << sio.nl; | ||
76 | iEPass++; | ||
77 | break; | ||
78 | |||
79 | case expectFail: | ||
80 | sio << "unexpected pass." << sio.nl; | ||
81 | iUPass++; | ||
82 | break; | ||
83 | } | ||
84 | } | 82 | } |
85 | catch( Failed &e ) | 83 | catch( Failed &e ) |
86 | { | 84 | { |
87 | switch( i->eExpect ) | 85 | if( pReport ) |
88 | { | 86 | pReport->testEnded( *i, e ); |
89 | case expectPass: | ||
90 | sio << "unexpected "; | ||
91 | iUFail++; | ||
92 | break; | ||
93 | |||
94 | case expectFail: | ||
95 | sio << "expected "; | ||
96 | iEFail++; | ||
97 | break; | ||
98 | } | ||
99 | if( e.bFile ) | ||
100 | { | ||
101 | sio << "fail in unitTest(" << e.str << "). (" << e.sFile | ||
102 | << ":" << e.nLine << ")." << sio.nl; | ||
103 | } | ||
104 | else | ||
105 | { | ||
106 | sio << "fail in unitTest(" << e.str << ")." << sio.nl; | ||
107 | } | ||
108 | 87 | ||
109 | if( (iOptions & optStopOnError) ) | 88 | if( (iOptions & optStopOnError) ) |
89 | { | ||
110 | return 0; | 90 | return 0; |
91 | } | ||
111 | } | 92 | } |
112 | catch( std::exception &e ) | 93 | catch( std::exception &e ) |
113 | { | 94 | { |
114 | switch( i->eExpect ) | 95 | if( pReport ) |
115 | { | 96 | pReport->testException( *i, e ); |
116 | case expectPass: | ||
117 | sio << "unexpected "; | ||
118 | iUFail++; | ||
119 | break; | ||
120 | |||
121 | case expectFail: | ||
122 | sio << "expected "; | ||
123 | iEFail++; | ||
124 | break; | ||
125 | } | ||
126 | sio << "fail with unknown exception. what: " << e.what() << sio.nl; | ||
127 | 97 | ||
128 | if( (iOptions & optStopOnError) ) | 98 | if( (iOptions & optStopOnError) ) |
99 | { | ||
129 | return 0; | 100 | return 0; |
101 | } | ||
130 | } | 102 | } |
131 | catch( ... ) | 103 | catch( ... ) |
132 | { | 104 | { |
133 | switch( i->eExpect ) | ||
134 | { | ||
135 | case expectPass: | ||
136 | sio << "unexpected "; | ||
137 | iUFail++; | ||
138 | break; | ||
139 | |||
140 | case expectFail: | ||
141 | sio << "expected "; | ||
142 | iEFail++; | ||
143 | break; | ||
144 | } | ||
145 | sio << "fail with external exception." << sio.nl; | 105 | sio << "fail with external exception." << sio.nl; |
106 | return -1; | ||
146 | 107 | ||
147 | if( (iOptions & optStopOnError) ) | 108 | if( (iOptions & optStopOnError) ) |
109 | { | ||
148 | return 0; | 110 | return 0; |
111 | } | ||
149 | } | 112 | } |
150 | } | 113 | } |
151 | 114 | ||
152 | sio << sio.nl | 115 | if( pReport ) |
153 | << "Report:" << sio.nl | 116 | pReport->suiteEnded(); |
154 | << "\tTotal tests run: " << lTests.getSize() << sio.nl | ||
155 | << "\tExpected passes: " << iEPass << sio.nl | ||
156 | << "\tExpected failures: " << iEFail << sio.nl | ||
157 | << "\tUnexpected passes: " << iUPass << sio.nl | ||
158 | << "\tUnexpected failures: " << iUFail << sio.nl << sio.nl; | ||
159 | if( iUPass == 0 && iUFail == 0 ) | ||
160 | sio << "\tNothing unexpected." << sio.nl << sio.nl; | ||
161 | 117 | ||
162 | if( bCleanup ) | 118 | if( bCleanup ) |
163 | { | 119 | { |
@@ -165,6 +121,8 @@ int Bu::UnitSuite::run( int argc, char *argv[] ) | |||
165 | { | 121 | { |
166 | unlink( (*i).getStr() ); | 122 | unlink( (*i).getStr() ); |
167 | } | 123 | } |
124 | |||
125 | cleanup(); | ||
168 | } | 126 | } |
169 | 127 | ||
170 | return 0; | 128 | return 0; |
@@ -191,8 +149,6 @@ void Bu::UnitSuite::add( Test fTest, const Bu::String &sName, Expect e ) | |||
191 | } | 149 | } |
192 | ti.fTest = fTest; | 150 | ti.fTest = fTest; |
193 | lTests.append( ti ); | 151 | lTests.append( ti ); |
194 | if( iNameWidth < ti.sName.getSize() ) | ||
195 | iNameWidth = ti.sName.getSize(); | ||
196 | } | 152 | } |
197 | 153 | ||
198 | void Bu::UnitSuite::setName( const String &sName ) | 154 | void Bu::UnitSuite::setName( const String &sName ) |
@@ -200,12 +156,21 @@ void Bu::UnitSuite::setName( const String &sName ) | |||
200 | sSuiteName = sName; | 156 | sSuiteName = sName; |
201 | } | 157 | } |
202 | 158 | ||
159 | Bu::String Bu::UnitSuite::getName() const | ||
160 | { | ||
161 | return sSuiteName; | ||
162 | } | ||
163 | |||
164 | void Bu::UnitSuite::cleanup() | ||
165 | { | ||
166 | } | ||
167 | |||
203 | void Bu::UnitSuite::dispProgress() | 168 | void Bu::UnitSuite::dispProgress() |
204 | { | 169 | { |
205 | if( tLastUpdate == time( NULL ) ) | 170 | if( tLastUpdate == time( NULL ) ) |
206 | return; | 171 | return; |
207 | sio << Fmt(3) << (iProgress*100/iStepCount) << "%" << "\b\b\b\b" | 172 | if( pReport ) |
208 | << sio.flush; | 173 | pReport->updateProgress( iProgress, iStepCount ); |
209 | tLastUpdate = time( NULL ); | 174 | tLastUpdate = time( NULL ); |
210 | } | 175 | } |
211 | 176 | ||
@@ -251,6 +216,13 @@ int Bu::UnitSuite::onListCases( StrArray ) | |||
251 | return 0; | 216 | return 0; |
252 | } | 217 | } |
253 | 218 | ||
219 | int Bu::UnitSuite::onPrintName( StrArray ) | ||
220 | { | ||
221 | Bu::println("%1").arg( sSuiteName ); | ||
222 | exit( 0 ); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
254 | int Bu::UnitSuite::onAddTest( StrArray aParam ) | 226 | int Bu::UnitSuite::onAddTest( StrArray aParam ) |
255 | { | 227 | { |
256 | hSelTests.insert( aParam[0], true ); | 228 | hSelTests.insert( aParam[0], true ); |
@@ -262,12 +234,213 @@ Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::UnitSuite::Expect &e | |||
262 | switch( e ) | 234 | switch( e ) |
263 | { | 235 | { |
264 | case Bu::UnitSuite::expectPass: | 236 | case Bu::UnitSuite::expectPass: |
265 | return f << "expect pass"; | 237 | return f << "pass"; |
266 | 238 | ||
267 | case Bu::UnitSuite::expectFail: | 239 | case Bu::UnitSuite::expectFail: |
268 | return f << "expect fail"; | 240 | return f << "fail"; |
269 | } | 241 | } |
270 | 242 | ||
271 | return f << "**error**"; | 243 | return f << "**error**"; |
272 | } | 244 | } |
273 | 245 | ||
246 | ///////// | ||
247 | // Bu::UnitSuite::Report | ||
248 | //// | ||
249 | |||
250 | Bu::UnitSuite::Report::Report() | ||
251 | { | ||
252 | } | ||
253 | |||
254 | Bu::UnitSuite::Report::~Report() | ||
255 | { | ||
256 | } | ||
257 | |||
258 | ///////// | ||
259 | // Bu::UnitSuite::ReportConsole | ||
260 | //// | ||
261 | |||
262 | Bu::UnitSuite::ReportConsole::ReportConsole() : | ||
263 | iTestCount( 0 ), | ||
264 | iNameWidth( 0 ), | ||
265 | iEPass( 0 ), | ||
266 | iEFail( 0 ), | ||
267 | iUPass( 0 ), | ||
268 | iUFail( 0 ) | ||
269 | { | ||
270 | } | ||
271 | |||
272 | Bu::UnitSuite::ReportConsole::~ReportConsole() | ||
273 | { | ||
274 | } | ||
275 | |||
276 | void Bu::UnitSuite::ReportConsole::suiteStarting( const UnitSuite & /*rSuite*/, | ||
277 | const TestList &lTests ) | ||
278 | { | ||
279 | iTestCount = lTests.getSize(); | ||
280 | for( TestList::const_iterator i = lTests.begin(); i; i++ ) | ||
281 | { | ||
282 | if( iNameWidth < i->sName.getSize() ) | ||
283 | iNameWidth = i->sName.getSize(); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | void Bu::UnitSuite::ReportConsole::testStarting( const TestInfo &rTest ) | ||
288 | { | ||
289 | sio << Fmt( iNameWidth+3, Fmt::Left ).fill('.') << rTest.sName | ||
290 | << sio.flush; | ||
291 | } | ||
292 | |||
293 | void Bu::UnitSuite::ReportConsole::updateProgress( int iProgress, | ||
294 | int iStepCount ) | ||
295 | { | ||
296 | sio << Fmt(3) << (iProgress*100/iStepCount) << "%" << "\b\b\b\b" | ||
297 | << sio.flush; | ||
298 | } | ||
299 | |||
300 | void Bu::UnitSuite::ReportConsole::testEnded( const TestInfo &rTest ) | ||
301 | { | ||
302 | switch( rTest.eExpect ) | ||
303 | { | ||
304 | case expectPass: | ||
305 | sio << "pass." << sio.nl; | ||
306 | iEPass++; | ||
307 | break; | ||
308 | |||
309 | case expectFail: | ||
310 | sio << "unexpected pass." << sio.nl; | ||
311 | iUPass++; | ||
312 | break; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | void Bu::UnitSuite::ReportConsole::testEnded( const TestInfo &rTest, | ||
317 | const Bu::UnitSuite::Failed &rFail ) | ||
318 | { | ||
319 | switch( rTest.eExpect ) | ||
320 | { | ||
321 | case expectPass: | ||
322 | sio << "unexpected "; | ||
323 | iUFail++; | ||
324 | break; | ||
325 | |||
326 | case expectFail: | ||
327 | sio << "expected "; | ||
328 | iEFail++; | ||
329 | break; | ||
330 | } | ||
331 | if( rFail.bFile ) | ||
332 | { | ||
333 | sio << "fail in unitTest(" << rFail.str << "). (" << rFail.sFile | ||
334 | << ":" << rFail.nLine << ")." << sio.nl; | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | sio << "fail in unitTest(" << rFail.str << ")." << sio.nl; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | void Bu::UnitSuite::ReportConsole::testException( const TestInfo &rTest, | ||
343 | std::exception &e ) | ||
344 | { | ||
345 | switch( rTest.eExpect ) | ||
346 | { | ||
347 | case expectPass: | ||
348 | sio << "unexpected "; | ||
349 | iUFail++; | ||
350 | break; | ||
351 | |||
352 | case expectFail: | ||
353 | sio << "expected "; | ||
354 | iEFail++; | ||
355 | break; | ||
356 | } | ||
357 | sio << "fail with unknown exception. what: " << e.what() << sio.nl; | ||
358 | } | ||
359 | |||
360 | void Bu::UnitSuite::ReportConsole::suiteEnded() | ||
361 | { | ||
362 | sio << sio.nl | ||
363 | << "Report:" << sio.nl | ||
364 | << "\tTotal tests run: " << iTestCount << sio.nl; | ||
365 | |||
366 | if( iEPass > 0 ) | ||
367 | sio << "\tExpected passes: " << iEPass << sio.nl; | ||
368 | if( iEFail > 0 ) | ||
369 | sio << "\tExpected failures: " << iEFail << sio.nl; | ||
370 | if( iUPass > 0 ) | ||
371 | sio << "\tUnexpected passes: " << iUPass << sio.nl; | ||
372 | if( iUFail > 0 ) | ||
373 | sio << "\tUnexpected failures: " << iUFail << sio.nl; | ||
374 | sio << sio.nl; | ||
375 | if( iUPass == 0 && iUFail == 0 ) | ||
376 | sio << "\tNothing unexpected." << sio.nl << sio.nl; | ||
377 | } | ||
378 | |||
379 | |||
380 | ///////// | ||
381 | // Bu::UnitSuite::Report | ||
382 | //// | ||
383 | |||
384 | Bu::UnitSuite::ReportJson::ReportJson() : | ||
385 | pReport( new Bu::Json( Bu::Json::Object ) ) | ||
386 | { | ||
387 | } | ||
388 | |||
389 | Bu::UnitSuite::ReportJson::~ReportJson() | ||
390 | { | ||
391 | delete pReport; | ||
392 | } | ||
393 | |||
394 | void Bu::UnitSuite::ReportJson::suiteStarting( const UnitSuite &rSuite, | ||
395 | const TestList & /*lTests*/ ) | ||
396 | { | ||
397 | pReport->insert("type", "report"); | ||
398 | pReport->insert("suite", rSuite.getName() ); | ||
399 | pReport->insertArray("tests"); | ||
400 | } | ||
401 | |||
402 | void Bu::UnitSuite::ReportJson::testStarting( const TestInfo & ) | ||
403 | { | ||
404 | } | ||
405 | |||
406 | void Bu::UnitSuite::ReportJson::updateProgress( int, int ) | ||
407 | { | ||
408 | } | ||
409 | |||
410 | void Bu::UnitSuite::ReportJson::testEnded( const TestInfo &rTest ) | ||
411 | { | ||
412 | Bu::Json &rOb = (*pReport)["tests"].appendObject(); | ||
413 | rOb.insert("name", rTest.sName ); | ||
414 | rOb.insert("expected", Bu::String("%1").arg( rTest.eExpect ) ); | ||
415 | rOb.insert("result", "pass"); | ||
416 | } | ||
417 | |||
418 | void Bu::UnitSuite::ReportJson::testEnded( const TestInfo &rTest, | ||
419 | const Bu::UnitSuite::Failed &rFail ) | ||
420 | { | ||
421 | Bu::Json &rOb = (*pReport)["tests"].appendObject(); | ||
422 | rOb.insert("name", rTest.sName ); | ||
423 | rOb.insert("expected", Bu::String("%1").arg( rTest.eExpect ) ); | ||
424 | rOb.insert("result", "fail"); | ||
425 | Bu::Json &rFailOb = rOb.insertObject("fail"); | ||
426 | rFailOb.insert("action", rFail.str ); | ||
427 | rFailOb.insert("file", rFail.sFile ); | ||
428 | rFailOb.insert("line", (double)rFail.nLine ); | ||
429 | } | ||
430 | |||
431 | void Bu::UnitSuite::ReportJson::testException( const TestInfo &rTest, | ||
432 | std::exception &e ) | ||
433 | { | ||
434 | Bu::Json &rOb = (*pReport)["tests"].appendObject(); | ||
435 | rOb.insert("name", rTest.sName ); | ||
436 | rOb.insert("expected", Bu::String("%1").arg( rTest.eExpect ) ); | ||
437 | rOb.insert("result", "fail"); | ||
438 | Bu::Json &rFail = rOb.insertObject("fail"); | ||
439 | rFail.insert("what", e.what() ); | ||
440 | } | ||
441 | |||
442 | void Bu::UnitSuite::ReportJson::suiteEnded() | ||
443 | { | ||
444 | Bu::println("%1").arg( pReport->toString() ); | ||
445 | } | ||
446 | |||