aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--src/experimental/xmlreader.cpp2
-rw-r--r--src/stable/unitsuite.cpp337
-rw-r--r--src/stable/unitsuite.h73
-rw-r--r--src/tools/mkunit.cpp34
-rw-r--r--src/tools/rununits.cpp186
-rw-r--r--src/unit/file.unit5
-rw-r--r--src/unit/xml.unit6
-rw-r--r--src/unstable/blob.cpp2
9 files changed, 554 insertions, 93 deletions
diff --git a/.gitignore b/.gitignore
index c6cd97f..e9625b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,12 +6,14 @@
6/mkunit.exe 6/mkunit.exe
7/myriad.exe 7/myriad.exe
8/jsontool.exe 8/jsontool.exe
9/rununits.exe
9/autoconfig 10/autoconfig
10/bin2cpp 11/bin2cpp
11/mkunit 12/mkunit
12/myriad 13/myriad
13/viewcsv 14/viewcsv
14/jsontool 15/jsontool
16/rununits
15.*.swp 17.*.swp
16.*.un~ 18.*.un~
17/src/autoconfig.h 19/src/autoconfig.h
diff --git a/src/experimental/xmlreader.cpp b/src/experimental/xmlreader.cpp
index e1d9ca8..107b76d 100644
--- a/src/experimental/xmlreader.cpp
+++ b/src/experimental/xmlreader.cpp
@@ -48,7 +48,7 @@ void Bu::XmlReader::cleanupBuffer( int iUsed )
48 } 48 }
49 } 49 }
50 50
51 printf("--Deleting %d bytes from front of buffer.\n", iUsed ); 51 //printf("--Deleting %d bytes from front of buffer.\n", iUsed );
52 sBuf.trimFront( iUsed ); 52 sBuf.trimFront( iUsed );
53} 53}
54 54
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
19Bu::UnitSuite::UnitSuite() : 20Bu::UnitSuite::UnitSuite() :
20 iOptions( 0 ), 21 iOptions( 0 ),
21 iNameWidth( 0 ) 22 pReport( NULL )
22{ 23{
23} 24}
24 25
25Bu::UnitSuite::UnitSuite( int iOptions ) : 26Bu::UnitSuite::UnitSuite( int iOptions ) :
26 iOptions( iOptions ), 27 iOptions( iOptions )
27 iNameWidth( 0 )
28{ 28{
29} 29}
30 30
31Bu::UnitSuite::~UnitSuite() 31Bu::UnitSuite::~UnitSuite()
32{ 32{
33 delete pReport;
33} 34}
34 35
35int Bu::UnitSuite::run( int argc, char *argv[] ) 36int 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
198void Bu::UnitSuite::setName( const String &sName ) 154void 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
159Bu::String Bu::UnitSuite::getName() const
160{
161 return sSuiteName;
162}
163
164void Bu::UnitSuite::cleanup()
165{
166}
167
203void Bu::UnitSuite::dispProgress() 168void 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
219int Bu::UnitSuite::onPrintName( StrArray )
220{
221 Bu::println("%1").arg( sSuiteName );
222 exit( 0 );
223 return 0;
224}
225
254int Bu::UnitSuite::onAddTest( StrArray aParam ) 226int 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
250Bu::UnitSuite::Report::Report()
251{
252}
253
254Bu::UnitSuite::Report::~Report()
255{
256}
257
258/////////
259// Bu::UnitSuite::ReportConsole
260////
261
262Bu::UnitSuite::ReportConsole::ReportConsole() :
263 iTestCount( 0 ),
264 iNameWidth( 0 ),
265 iEPass( 0 ),
266 iEFail( 0 ),
267 iUPass( 0 ),
268 iUFail( 0 )
269{
270}
271
272Bu::UnitSuite::ReportConsole::~ReportConsole()
273{
274}
275
276void 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
287void Bu::UnitSuite::ReportConsole::testStarting( const TestInfo &rTest )
288{
289 sio << Fmt( iNameWidth+3, Fmt::Left ).fill('.') << rTest.sName
290 << sio.flush;
291}
292
293void 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
300void 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
316void 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
342void 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
360void 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
384Bu::UnitSuite::ReportJson::ReportJson() :
385 pReport( new Bu::Json( Bu::Json::Object ) )
386{
387}
388
389Bu::UnitSuite::ReportJson::~ReportJson()
390{
391 delete pReport;
392}
393
394void 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
402void Bu::UnitSuite::ReportJson::testStarting( const TestInfo & )
403{
404}
405
406void Bu::UnitSuite::ReportJson::updateProgress( int, int )
407{
408}
409
410void 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
418void 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
431void 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
442void Bu::UnitSuite::ReportJson::suiteEnded()
443{
444 Bu::println("%1").arg( pReport->toString() );
445}
446
diff --git a/src/stable/unitsuite.h b/src/stable/unitsuite.h
index ea5e389..b1e2953 100644
--- a/src/stable/unitsuite.h
+++ b/src/stable/unitsuite.h
@@ -17,6 +17,7 @@
17 17
18namespace Bu 18namespace Bu
19{ 19{
20 class Json;
20 /** 21 /**
21 * Provides a unit testing framework. This is pretty easy to use, probably 22 * Provides a unit testing framework. This is pretty easy to use, probably
22 * the best way to get started is to use ch to generate a template, or use 23 * the best way to get started is to use ch to generate a template, or use
@@ -96,6 +97,8 @@ namespace Bu
96 protected: 97 protected:
97 void add( Test fTest, const Bu::String &sName, Expect e=expectPass ); 98 void add( Test fTest, const Bu::String &sName, Expect e=expectPass );
98 void setName( const String &sName ); 99 void setName( const String &sName );
100 String getName() const;
101 virtual void cleanup();
99 102
100 void dispProgress(); 103 void dispProgress();
101 void setStepCount( int iSteps ); 104 void setStepCount( int iSteps );
@@ -104,6 +107,7 @@ namespace Bu
104 107
105 private: 108 private:
106 int onListCases( Bu::Array<Bu::String> aParam ); 109 int onListCases( Bu::Array<Bu::String> aParam );
110 int onPrintName( Bu::Array<Bu::String> aParam );
107 int onAddTest( Bu::Array<Bu::String> aParam ); 111 int onAddTest( Bu::Array<Bu::String> aParam );
108 112
109 private: 113 private:
@@ -128,6 +132,75 @@ namespace Bu
128 time_t tLastUpdate; 132 time_t tLastUpdate;
129 133
130 Bu::Hash<Bu::String, bool> hSelTests; 134 Bu::Hash<Bu::String, bool> hSelTests;
135
136 public:
137 class Report
138 {
139 public:
140 Report();
141 virtual ~Report();
142
143 virtual void suiteStarting( const UnitSuite &rSuite,
144 const TestList &lTests )=0;
145 virtual void testStarting( const TestInfo &rTest )=0;
146 virtual void updateProgress( int iProgress, int iStepCount )=0;
147 virtual void testEnded( const TestInfo &rTest )=0;
148 virtual void testEnded( const TestInfo &rTest,
149 const Failed &rFail )=0;
150 virtual void testException( const TestInfo &rTest,
151 std::exception &e )=0;
152 virtual void suiteEnded()=0;
153 };
154
155 class ReportConsole : public Report
156 {
157 public:
158 ReportConsole();
159 virtual ~ReportConsole();
160
161 virtual void suiteStarting( const UnitSuite &rSuite,
162 const TestList &lTests );
163 virtual void testStarting( const TestInfo &rTest );
164 virtual void updateProgress( int iProgress, int iStepCount );
165 virtual void testEnded( const TestInfo &rTest );
166 virtual void testEnded( const TestInfo &rTest,
167 const Failed &rFail );
168 virtual void testException( const TestInfo &rTest,
169 std::exception &e );
170 virtual void suiteEnded();
171
172 private:
173 int iTestCount;
174 int iNameWidth;
175 int iEPass;
176 int iEFail;
177 int iUPass;
178 int iUFail;
179 };
180
181 class ReportJson : public Report
182 {
183 public:
184 ReportJson();
185 virtual ~ReportJson();
186
187 virtual void suiteStarting( const UnitSuite &rSuite,
188 const TestList &lTests );
189 virtual void testStarting( const TestInfo &rTest );
190 virtual void updateProgress( int iProgress, int iStepCount );
191 virtual void testEnded( const TestInfo &rTest );
192 virtual void testEnded( const TestInfo &rTest,
193 const Failed &rFail );
194 virtual void testException( const TestInfo &rTest,
195 std::exception &e );
196 virtual void suiteEnded();
197
198 private:
199 class Bu::Json *pReport;
200 };
201
202 private:
203 Report *pReport;
131 }; 204 };
132 205
133Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::UnitSuite::Expect &e ); 206Bu::Formatter &operator<<( Bu::Formatter &f, const Bu::UnitSuite::Expect &e );
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}
diff --git a/src/unit/file.unit b/src/unit/file.unit
index ee69995..b8cb73f 100644
--- a/src/unit/file.unit
+++ b/src/unit/file.unit
@@ -14,6 +14,11 @@
14 14
15suite File 15suite File
16{ 16{
17 cleanup
18 {
19 unlink("testfile1");
20 }
21
17 test writeFull 22 test writeFull
18 { 23 {
19 Bu::File sf("testfile1", Bu::File::WriteNew ); 24 Bu::File sf("testfile1", Bu::File::WriteNew );
diff --git a/src/unit/xml.unit b/src/unit/xml.unit
index 0d62b8b..21757cb 100644
--- a/src/unit/xml.unit
+++ b/src/unit/xml.unit
@@ -12,10 +12,4 @@
12 12
13suite Xml 13suite Xml
14{ 14{
15 test declaration
16 {
17 Bu::String sXml("<?xml ?> <hi />");
18 Bu::MemBuf buf( sXml );
19 Bu::XmlReader xr( buf );
20 }
21} 15}
diff --git a/src/unstable/blob.cpp b/src/unstable/blob.cpp
index a9cb99d..d343963 100644
--- a/src/unstable/blob.cpp
+++ b/src/unstable/blob.cpp
@@ -813,7 +813,7 @@ template<> void Bu::__tracer_format<Bu::Blob>( const Bu::Blob &v )
813#include "bu/formatter.h" 813#include "bu/formatter.h"
814Bu::Formatter &Bu::operator<<( Bu::Formatter &rOut, const Bu::Blob &b ) 814Bu::Formatter &Bu::operator<<( Bu::Formatter &rOut, const Bu::Blob &b )
815{ 815{
816 rOut.write( b.getData(), b.getSize() ); 816 rOut.writeAligned( b.getData(), b.getSize() );
817 return rOut; 817 return rOut;
818} 818}
819 819