root/OpenSceneGraph/trunk/examples/osgunittests/UnitTestFramework.cpp @ 6941

Revision 6941, 9.0 kB (checked in by robert, 7 years ago)

From Martin Lavery and Robert Osfield, Updated examples to use a variation of the MIT License

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* OpenSceneGraph example, osgunittests.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include "UnitTestFramework.h"
20
21#include <algorithm>
22
23namespace osgUtx
24{
25
26//////////////////////////////////////////////////////////////////////////////
27
28TestContext::TestContext()
29{
30}
31
32void TestContext::setTraceLevel(TraceLevel tl)
33{
34    _tout.setTraceLevel(tl);
35}
36
37TestContext::TraceLevel TestContext::getTraceLevel() const
38{
39    return _tout.getTraceLevel();
40}
41
42std::ostream& TestContext::tout(TraceLevel tl) const
43{
44    return _tout.stream(tl);
45}
46
47//////////////////////////////////////////////////////////////////////////////
48
49
50TestContext::TraceStream::TraceStream(std::ostream& o, TraceLevel tl):
51    _traceLevel(tl),
52    _outputStreamPtr(&o),
53#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
54    _nullStream("nul")
55#else
56    _nullStream("/dev/null")
57#endif
58{
59}
60
61TestContext::TraceStream::~TraceStream()
62{
63    _nullStream.close();
64}
65
66void TestContext::TraceStream::setTraceLevel(TraceLevel tl)
67{
68    _traceLevel = tl;
69}
70
71TestContext::TraceLevel TestContext::TraceStream::getTraceLevel() const
72{
73    return _traceLevel;
74}
75
76std::ostream& TestContext::TraceStream::stream(TestContext::TraceLevel tl)
77{
78    if(_traceLevel >= tl){
79        return *_outputStreamPtr;
80    }
81    return _nullStream;
82}
83
84//////////////////////////////////////////////////////////////////////////////
85
86TestGraph& TestGraph::instance()
87{
88    static TestGraph instance_;
89    return instance_;
90}
91
92TestSuite* TestGraph::root()
93{
94    return root_.get();
95}
96
97TestSuite* TestGraph::suite(const std::string& path, TestSuite* tsuite, bool createIfNecessary)
98{
99    using namespace std;
100
101    list<string> pathComponents;
102
103    std::string::const_iterator it1 = path.begin();
104    std::string::const_iterator it2 = it1;
105
106    // Dissect the path into it's constituent components
107    do{
108
109        while( it2 != path.end() && *it2 != '.' ) ++it2;
110
111        // Consider a check for "" empty strings?
112        pathComponents.push_back( std::string(it1,it2) );
113
114        if( it2 != path.end()) ++it2;
115
116        it1 = it2;
117
118    }while( it2 != path.end());
119
120    return suite(pathComponents.begin(), pathComponents.end(),
121            tsuite, createIfNecessary);
122
123}
124
125TestSuite* TestGraph::suite(
126        std::list<std::string>::iterator it,
127        std::list<std::string>::iterator end,
128        TestSuite* tsuite, bool createIfNecessary)
129{
130    using namespace std;
131
132    if( ! tsuite) tsuite = root();
133
134    // Make sure these tie up
135    if(*it != tsuite->name()) return 0;
136
137    ++it;
138    if(it == end) return tsuite;
139
140    Test* child = tsuite->findChild(*it);
141
142    if(child){
143
144        // We've found a child with the right name. But is it a
145        // test suite?
146
147        if(TestSuite* childSuite = dynamic_cast<TestSuite*>(child)){
148            return suite(it, end, childSuite, createIfNecessary);
149        }
150
151        // We could return 0 here, to indicate that someone is
152        // trying to add a TestSuite named 'xxx' to a suite with a
153        // Test already named 'xxx'. But we don't enforce uniqueness
154        // the other way round, so we don't do it this way round
155        // either. Carry on as normal, and create a TestSuite of
156        // the same name if createIfNecessary is true.
157
158    }
159
160    if(createIfNecessary){
161
162        TestSuite* childSuite = new TestSuite(*it);
163        tsuite->add(childSuite);
164        return suite(it, end, childSuite, createIfNecessary);
165    }
166
167    return 0;
168}
169
170TestGraph::TestGraph(): root_(new TestSuite("root"))
171{
172}
173
174
175//////////////////////////////////////////////////////////////////////////////
176
177bool TestQualifier::visitEnter( TestSuite* pSuite )
178{
179    _path.append( pSuite->name() );
180    _path += SEPCHAR;
181    return true;
182}
183
184// Leaving a composite: Pop its name from the Path
185bool TestQualifier::visitLeave( TestSuite* pSuite )
186{
187//    assert( _path.rfind( pSuite->name() + static_cast<const char>(SEPCHAR))
188//                == _path.size() - pSuite->name().size()  - 1);
189
190    _path.erase( _path.size() - pSuite->name().size() -1 );
191    return true;
192}
193
194// Provide read-only access to the current qualifier
195const std::string& TestQualifier::currentPath() const
196{
197    return _path;
198}
199
200//////////////////////////////////////////////////////////////////////////////
201
202osg::Timer TestRecord::timer_;
203
204void TestRecord::start()
205{
206    start_ = timer_.tick();
207}
208
209void TestRecord::stop()
210{
211    stop_ = timer_.tick();
212}
213
214void TestRecord::log(const TestFailureX& e)
215{
216    stop();
217    result_ = Failure;
218    problem_ = e.what();
219}
220
221void TestRecord::log(const TestErrorX& e)
222{
223    stop();
224    result_ = Error;
225    problem_ = e.what();
226}
227
228void TestRecord::log(const std::exception& e)
229{
230    stop();
231    result_ = Error;
232    problem_ = e.what();
233}
234
235void TestRecord::log(const std::string& s)
236{
237    stop();
238    result_ = Error;
239    problem_ = s;
240}
241
242TestRecord::TestRecord(const std::string& name):
243    name_(name),
244    start_(0),
245    stop_(0),
246    result_(Success),
247    problem_("No problem")
248{
249}
250
251std::ostream& operator<<(std::ostream& o,const TestRecord& tr)
252{
253    if(tr.result_ == TestRecord::Success)         o<<"pass";
254    else if(tr.result_ == TestRecord::Failure)    o<<"fail";
255    else                                          o<<"error";
256
257    o<<"\t"<<tr.name_;
258
259
260    //o<<tr.start_<<'\t'<<tr.stop_<<'\t'<<TestRecord::timer_.delta_s(tr.start_,tr.stop_);
261
262    // Just print out the duration
263    o<<'\t'<<TestRecord::timer_.delta_s(tr.start_,tr.stop_)<<'s';
264
265    if(tr.result_ != TestRecord::Success){
266        o<<'\t'<<tr.problem_;
267    }
268
269    return o;
270}
271
272//////////////////////////////////////////////////////////////////////////////
273
274TestRunner::TestRunner( TestContext& ctx ) : _ctx( ctx )
275{
276}
277
278void TestRunner::specify( const std::string& sQualifiedName )
279{
280    _tests.push_back( sQualifiedName );
281}
282
283bool TestRunner::visitEnter( TestSuite* pSuite )
284{
285    TestQualifier::visitEnter( pSuite );
286    return !_ctx.shouldStop();
287}
288
289#ifndef DOXYGEN_SHOULD_SKIP_THIS
290
291namespace osgUtx{
292
293struct isSpecified{
294
295    const std::string& pTestName_;
296
297    isSpecified(const std::string& s): pTestName_(s) {}
298
299    bool operator()(const std::string& specifiedTest){
300        return pTestName_.find(specifiedTest) == 0;
301    }
302};
303
304}
305
306#endif /* DOXYGEN_SHOULD_SKIP_THIS */
307
308bool TestRunner::visit( TestCase* pTest )
309{
310    if ( std::find_if(_tests.begin(),_tests.end(),
311                      osgUtx::isSpecified(currentPath() + pTest->name() ) ) != _tests.end()) perform( pTest );
312
313    return !_ctx.shouldStop();
314}
315
316bool TestRunner::visitLeave( TestSuite* pSuite )
317{
318    TestQualifier::visitLeave( pSuite );
319    return !_ctx.shouldStop();
320}
321
322void TestRunner::perform( TestCase* pTest )
323{
324    TestRecord& record = _db.createRecord( currentPath() + pTest->name() );
325
326    try
327    {
328        record.start();
329        pTest->run( _ctx );
330        record.stop();
331    }
332
333    catch ( const TestFailureX& e )
334    {
335        record.log( e );
336    }
337    catch ( const TestErrorX& e )
338    {
339        record.log( e );
340    }
341    catch ( const std::exception& e )
342    {
343        record.log( e );
344    }
345    catch ( ... )
346    {
347        record.log( std::string("Unknown") );
348    }
349
350
351    _ctx.tout(TestContext::Results) << record << std::endl;
352}
353
354//////////////////////////////////////////////////////////////////////////////
355
356TestSuite::TestSuite( const std::string& name ) : Test( name )
357{
358}
359
360void TestSuite::add( Test* pTest )
361{
362    _tests.push_back( pTest );
363}
364
365Test* TestSuite::findChild(const std::string& name)
366{
367    for(Tests::iterator it = _tests.begin();
368        it != _tests.end();
369        ++it){
370
371        if ((*it)->name() == name) return (*it).get();
372    }
373
374    return 0;
375}
376
377bool TestSuite::accept( Test::Visitor& v )
378{
379    if ( v.visitEnter( this ) )
380    {
381        Tests::iterator end = _tests.end();
382        for ( Tests::iterator at = _tests.begin(); at != end; ++at )
383            if ( !(*at)->accept( v ) )
384                break;
385    }
386
387    return v.visitLeave( this );   // continue with siblings?
388}
389
390//////////////////////////////////////////////////////////////////////////////
391
392bool QualifiedTestPrinter::visit( TestCase* pTest )
393{
394    osg::notify(osg::NOTICE) << currentPath() + pTest->name() << std::endl;
395    return true;
396}
397
398//////////////////////////////////////////////////////////////////////////////
399
400
401}
Note: See TracBrowser for help on using the browser.