root/OpenSceneGraph/trunk/examples/osgunittests/UnitTestFramework.h @ 5841

Revision 5841, 15.4 kB (checked in by robert, 8 years ago)

Removed now redundent OSG_EXPORT to solve build problems under Windows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#ifndef OSG_UNITTESTFRAMEWORK
15#define OSG_UNITTESTFRAMEWORK 1
16
17#include <osg/Export>
18#include <osg/Referenced>
19#include <osg/ref_ptr>
20#include <osg/Timer>
21#include <osg/Notify>
22
23#include <string>
24#include <vector>
25#include <list>
26#include <fstream>
27
28/**
29
30\namespace osgUtx
31
32The osgUtx is a unit test framework.
33*/
34
35namespace osgUtx{
36
37class TestVisitor;
38
39
40/**
41Test, an abstract base class, is the Composite pattern's \em component
42class for our graph of test cases, and defines the basic interface
43for all Test components. It is a referent, and may be pointed
44to by an osg::ref_ptr.
45*/
46class Test: public osg::Referenced
47{
48    public:
49
50    typedef TestVisitor Visitor;    // Test is redundant
51
52    Test( const std::string& sName ) : _name( sName ) {}
53
54    const std::string& name() const { return _name; }
55
56    virtual bool accept( Visitor& ) = 0;
57
58    protected:
59
60        virtual ~Test() {}
61
62    std::string _name;
63};
64
65
66/**
67TestContext wraps up information which is passed to tests as they are run,
68and may contain test-specific information or 'global' test objects, such
69as an output stream for verbose output during the running of tests.
70
71\todo Improve the output stream code by providing a filtering stream.
72*/
73class TestContext
74{
75public:
76
77    TestContext();
78
79    bool shouldStop()    { return false; }
80    bool isVerbose()    { return true; }
81
82    enum TraceLevel{
83        Off,        ///< All tracing turned off
84        Results,    ///< Output results only
85        Full        ///< Full test diagnostic output
86    };
87
88    void setTraceLevel(TraceLevel tl);
89    TraceLevel getTraceLevel() const;
90
91    std::ostream& tout(TraceLevel tl=Full) const;
92
93private:
94
95    TestContext(const TestContext&);
96    TestContext operator=(const TestContext&);
97
98#ifndef DOXYGEN_SHOULD_SKIP_THIS
99
100    class TraceStream{
101
102    public:
103        TraceStream(std::ostream& o=osg::notify(osg::NOTICE), TraceLevel tl=Results);
104        ~TraceStream();
105
106        void setTraceLevel(TraceLevel tl);
107        TraceLevel getTraceLevel() const;
108
109        std::ostream& stream(TraceLevel tl);
110
111    private:
112
113        TraceLevel    _traceLevel;
114        std::ostream*    _outputStreamPtr;
115        std::ofstream    _nullStream;
116    };
117
118#endif /* DOXYGEN_SHOULD_SKIP_THIS */
119
120    mutable TraceStream _tout;
121
122};
123
124
125class TestSuite;
126class TestCase;
127
128/**
129Visits while maintaining the current hierarchical context. Also allows
130the traversal to be short-circuited at any point during the visitation.
131*/
132class TestVisitor
133{
134    public:
135
136    //..Should we enter this node and its children?
137    virtual bool visitEnter( TestSuite* ) { return true; }
138
139    //..Returns true to continue to next Leaf
140    virtual bool visit( TestCase* ) = 0;
141
142    //..Returns true to continue to next Composite
143    virtual bool visitLeave( TestSuite* ) { return true; }
144       
145    protected:
146
147    TestVisitor() {}
148    TestVisitor( const TestVisitor& ) {}
149    virtual ~TestVisitor()    {}
150};
151
152/**
153TestCase, supplies the interface for a Composite pattern's
154\em leaf class, though it is not a leaf in itself.
155*/
156class TestCase : public Test
157{
158    public:
159
160    typedef TestContext Context; // Test in TestContext? is redundant
161
162    TestCase( const std::string& sName ) : Test( sName ) {}
163
164    virtual bool accept( Visitor& v ) { return v.visit( this ); }
165
166    virtual void run( const Context& ) = 0;  // Subclass OSGUTX_EXPORT Responsibility
167
168    protected:
169
170        virtual ~TestCase() {}
171};
172
173/**
174Base class catchable for the exceptions which may be thrown to
175indicate problems during the run of a TestCase.
176*/
177class TestX
178{
179    public:
180
181    TestX(const std::string& s):_what(s)    {}
182    virtual ~TestX() {}
183
184    const std::string& what() const { return _what; }
185
186    private:
187    std::string    _what;
188};
189
190/**
191A TestFailureX indicates a failure in the tested component.
192*/
193class TestFailureX: public TestX
194{
195    public:
196    TestFailureX(const std::string& s):TestX(s)    {}
197};
198
199/**
200A TestErrorX indicates an error while testing a component,
201which prevents the test from being run. It does not indicate
202a problem with the component, but rather a problem during the
203run which prevents the component from being tested.
204*/
205class TestErrorX: public TestX
206{
207    public:
208    TestErrorX(const std::string& s):TestX(s)    {}
209};
210
211/**
212TestCase_ is a class template for a leaf TestCase, which allows TestFixture
213classes to be easily collected into the tree of tests, and have their public
214test methods called. It is worth noting that, for a given TestCase_, an
215instance of the test fixture class will be constructed prior to the
216test method being called, and destructed afterwards. This prevents 'leakage'
217of information from one test case to the next.
218*/
219template< typename FixtureT >
220class TestCase_ : public TestCase
221{
222    typedef void (FixtureT::*TestMethodPtr)( const Context& );
223
224    public:
225
226    // Constructor adds the TestMethod pointer
227    TestCase_( const std::string& sName, TestMethodPtr pTestMethod ) :
228            TestCase( sName ),
229            _pTestMethod( pTestMethod )
230    {
231    }
232
233    // Create a TestFixture instance and invoke TestMethod?
234    virtual void run( const Context& ctx )
235    {
236        ( FixtureT().*_pTestMethod )( ctx );
237    }
238
239    protected:
240
241        virtual ~TestCase_() {}
242
243    TestMethodPtr _pTestMethod;
244};
245
246/**
247A TestSuite is the \em composite component of the Composite pattern,
248and allows aggregation of Tests into hierarchies.
249*/
250class TestSuite : public Test
251{
252    public:
253
254    TestSuite( const std::string& name );
255
256    /** Adds a Test to the suite. */
257    void add( Test* pTest );
258
259    /**
260    @returns    The immediate child denoted by name, or 0 if not found.
261    */
262    Test* findChild(const std::string& name);
263
264    virtual bool accept( Test::Visitor& v );
265
266    protected:
267
268        virtual ~TestSuite() {}
269
270    typedef std::vector< osg::ref_ptr<Test> > Tests;
271    Tests _tests;  // Collection of Suites and/or Cases
272};
273
274/**
275TestGraph is a singleton providing central access to the tree of tests;
276primarily, it provides access to the root suite.
277*/
278class TestGraph
279{
280
281    public:
282
283    static TestGraph& instance();
284
285    /**
286        @return a pointer to the root TestSuite.
287    */
288    TestSuite* root();
289
290    /**
291        A utility function for accessing an arbitrary suite by pathname, relative to
292        the suite 'tsuite' (defaults to root if null), and with the option of creating
293        the \em TestSuite designated by \em path, if it does not already exist.
294
295        This method may return 0 if the suite either cannot be found (and createIfNecssary
296        is 0), or the first component of \em path is not the same as the name of the
297        TestSuite \em tsuite.
298
299        This was written to aid the auto-registration of tests at specific points in
300        the test tree, where the tests' AutoRegistrationAgents may be distributed across
301        several files, and cannot be guaranteed to run in a given order. E.g. You cannot
302        register a test "root.osg.MyTest" unless you know that the the suite "root.osg"
303        already exists.
304       
305
306        @param path                    The name of the TestSuite to return.
307        @param tsuite                The suite to 'start from'. Path is relative to this
308                                    suite (defaults to root suite).
309        @param createIfNecessary    Optionally create the TestSuite(s) denoted by path if
310                                    they do not exist.
311    */
312    TestSuite* suite(const std::string& path, TestSuite* tsuite = 0,bool createIfNecessary = false);
313
314    private:
315
316    /**
317        Does the same job as the version of suite listed above, but the path
318        is passed in as components in a list, represented by the iterator parameters.
319    */
320    TestSuite* suite(
321        std::list<std::string>::iterator it,
322        std::list<std::string>::iterator end,
323        TestSuite* tsuite, bool createIfNecessary);
324
325    TestGraph();
326
327    TestGraph(const TestGraph&);
328    TestGraph& operator=(const TestGraph&);
329
330    osg::ref_ptr<TestSuite>    root_;
331
332};
333
334
335/**
336Maintains a string that when accessed in the "visit" member, returns the
337current qualified TestSuite path.
338*/
339class TestQualifier : public TestVisitor
340{
341    enum { SEPCHAR = '.' };
342
343    public:
344
345    // Entering a composite: Push its name on the Path
346    virtual bool visitEnter( TestSuite* pSuite );
347
348    // Leaving a composite: Pop its name from the Path
349    virtual bool visitLeave( TestSuite* pSuite );
350
351    // Provide read-only access to the current qualifier
352    const std::string& currentPath() const;
353
354    private:
355
356    std::string _path;    // Current qualifier
357};
358
359/**
360QualifiedTestPrinter prints to standard output a list of fully
361qualified tests.
362*/
363class QualifiedTestPrinter : public TestQualifier
364{
365public:
366
367
368    virtual bool visit( TestCase* pTest );
369};
370
371/**
372A TestRecord records the output of a given test case, i.e. its start/stop time,
373its result, and a textual description of any problems.
374
375\todo    Consider adding accessor methods if necessary, to get the details
376        stored in the TestRecord.
377*/
378class TestRecord
379{
380    public:
381
382        void start();
383        void stop();
384        void log(const TestFailureX& e);
385        void log(const TestErrorX& e);
386        void log(const std::exception& e);
387        void log(const std::string& s);
388
389        // Default copy construction and assignment are OK
390
391        // FIXME: Add accessors?
392
393    private:
394
395        // Onlye a TestReport can create a TestRecord
396        friend class TestReport;
397        TestRecord(const std::string& name);
398
399        enum Result{
400            Success,Failure,Error
401        };
402
403        friend std::ostream& operator<<(std::ostream& o,const TestRecord& tr);
404
405        static osg::Timer    timer_;    // To time tests
406
407        std::string        name_;
408        osg::Timer_t    start_;
409        osg::Timer_t    stop_;
410        Result            result_;
411        std::string        problem_;
412
413};
414
415/**
416A TestReport represents the complete set of results (TestRecords) for a
417given test run.
418
419\todo    Add support for printing the test report in various formats:
420        e.g. text, XML, CSV
421*/
422class TestReport
423{
424public:
425
426    TestRecord&    createRecord(const std::string& s){
427        _records.push_back(TestRecord(s));
428        return _records.back();
429    }
430
431private:
432    std::list<TestRecord>    _records;
433
434};
435
436
437
438
439
440
441
442/**
443A TestRunner is a visitor which will run specified tests as it traverses the
444test graph.
445
446\todo    Consider an accessor method to get at the TestReport if necessary.
447*/
448class TestRunner : public TestQualifier
449{
450public:
451
452    TestRunner( TestContext& ctx );
453
454    /**
455        Tests may be specified by partial names. E.g. specifiying "root"
456        will run all tests below root, i.e. all tests.
457        Specifiying    "root.osg" will run all tests below \em root.osg.
458        Specifying "root.osg.de" will run all tests (and suites) below
459        \em root.osg with names beginning with the \em de.
460    */
461    void specify( const std::string& sQualifiedName );
462
463    bool visitEnter( TestSuite* pSuite );
464    bool visit( TestCase* pTest );
465    bool visitLeave( TestSuite* pSuite );
466
467
468protected:
469
470    void perform( TestCase* pTest );
471
472private:
473
474    TestReport                   _db;            // Results
475    TestContext&                 _ctx;            // The Global Testing Context
476    std::vector<std::string>    _tests;          // Specified Tests
477};
478
479}
480
481/**
482Starts a TestSuite singleton function
483@see OSGUTX_ADD_TESTCASE, OSGUTX_END_TESTSUITE
484*/
485#define OSGUTX_BEGIN_TESTSUITE( tsuite ) \
486    osgUtx::TestSuite* tsuite##_TestSuite() \
487    { \
488        static osg::ref_ptr<osgUtx::TestSuite> s_suite = 0; \
489        if ( s_suite == 0 ) { \
490            s_suite = new osgUtx::TestSuite( #tsuite );
491
492
493
494/**
495Adds a test case to a suite object being created in a TestSuite singleton function.
496@see OSGUTX_BEGIN_TESTSUITE, OSGUTX_END_TESTSUITE
497*/
498#define OSGUTX_ADD_TESTCASE( tfixture, tmethod ) \
499            s_suite->add( new osgUtx::TestCase_<tfixture>(  \
500                                #tmethod, &tfixture::tmethod ) );
501
502/**
503Ends a TestSuite singleton function
504@see OSGUTX_BEGIN_TESTSUITE, OSGUTX_ADD_TESTCASE
505*/
506#define OSGUTX_END_TESTSUITE \
507        } \
508        return s_suite.get(); \
509    }
510
511/** Define a TestSuite accessor */
512#define OSGUTX_TESTSUITE( tsuite ) \
513    tsuite##_TestSuite()
514
515
516/**
517Adds a suite to a suite - allows composition of test suites.
518@see OSGUTX_BEGIN_TESTSUITE, OSGUTX_END_TESTSUITE
519*/
520#define OSGUTX_ADD_TESTSUITE( childSuite ) \
521    s_suite->add( childSuite##_TestSuite() );
522
523
524/** Autoregister a testsuite with the root suite at startup */
525#define OSGUTX_AUTOREGISTER_TESTSUITE( tsuite ) \
526    static osgUtx::TestSuiteAutoRegistrationAgent tsuite##_autoRegistrationObj__( tsuite##_TestSuite() );
527
528/** Auto register a testsuite with at designated point in the suite graph at startup */
529#define OSGUTX_AUTOREGISTER_TESTSUITE_AT( tsuite , path ) \
530    static osgUtx::TestSuiteAutoRegistrationAgent tsuite##_autoRegistrationObj__( tsuite##_TestSuite(), #path );
531
532namespace osgUtx{
533
534/**
535A helper struct to perform automatic registration at program startup; not for
536direct use, it should be used via the following macros. (It's a secret agent :-)
537
538@see OSGUTX_AUTOREGISTER_TESTSUITE, OSGUTX_AUTOREGISTER_TESTSUITE_AT
539*/
540struct TestSuiteAutoRegistrationAgent
541{
542    TestSuiteAutoRegistrationAgent(TestSuite* tsuite, const char* path = 0)
543    {
544        if( ! path ) path = "root";
545
546        // Find the suite named in 'path', create it if necessary
547        TestSuite *regSuite = osgUtx::TestGraph::instance().suite( path, 0, true );
548
549        if(!regSuite){
550            osg::notify(osg::WARN)<<"Warning, unable to register test suite named \""<<tsuite->name()<<"\" at "
551                              <<path<<", falling back to root suite."<<std::endl;
552            regSuite = osgUtx::TestGraph::instance().root();
553        }
554
555        regSuite->add(tsuite);
556    }
557};
558
559}
560
561/**
562OSGUTX_TEST_F is a convenience macro, analogous to assert(), which will
563throw an osgUtx::TestFailureX if \em expr evaluates to false; this should be
564used to test for failure in a given test, as opposed to an actual error
565in the test owing to some other reason than the tested code being faulty.
566
567The exception will indicate the file and line number of the failed expression,
568along with expression itself.
569*/
570#define OSGUTX_TEST_F( expr ) \
571    if( !(expr) ){ \
572        std::stringstream ss; \
573        ss<< #expr <<" failure: "<<__FILE__<<", line "<<__LINE__<<std::ends; \
574        throw osgUtx::TestFailureX(ss.str()); \
575    }
576
577/**
578OSGUTX_TEST_E is a convenience macro, analogous to assert(), which will
579throw an osgUtx::TestErrorX if \em expr evaluates to false; this should be
580used to test for an error in a given test, as opposed to a failure
581in the tested code.
582
583The exception will indicate the file and line number of the failed expression,
584along with expression itself.
585*/
586#define OSGUTX_TEST_E( expr ) \
587    if( !(expr) ){ \
588        std::stringstream ss; \
589        ss<< #expr <<" error: "<<__FILE__<<", line "<<__LINE__<<std::ends; \
590        throw osgUtx::TestErrorX(ss.str()); \
591    }
592
593
594#endif // OSG_UNITTESTFRAMEWORK
Note: See TracBrowser for help on using the browser.