Skip to content

Dynamic extension of suite with test cases #25

Open
whart222 opened this Issue Feb 4, 2012 · 1 comment

1 participant

@whart222
CxxTest member
whart222 commented Feb 4, 2012

A feature to extend an existing suite with dynamic test cases is required. This
is required, for example, to run same test case with different parameters or to
reuse test cases defined on a different suite.

Proposal

A solution to this problem with the existing cxxtest framework is to dynamically
create instances of the class derived from CxxTest::RealTestDescription and add
them to an existing suite. For example, the following defines a multi instance
test case

class TestWithMultipleParams : public CxxTest::RealTestDescription
{
public:
    TestWithMultipleParams(const char * suiteToAddTo, int TestParam)
    {   
        // Initialize RealTestDescription based on the suiteToAddTo
        CxxTest::SuiteDescription * pSuite;
        CxxTest::List * pList;
        GetSuiteInfo(suiteToAdd, &pSuite, &pList);
        initialize(*pList, *pSuite, __LINE__, "TestWithMultipleParams");

        // Store TestParam for later testing based on its value
        TestWithMultipleParams::TestParam = TestParam;
    }   

    ~TestWithMultipleParams() {}

    void runTest()
    {   
        // Test based on the value of TestParam
    }   

private:
    bool GetSuiteInfo(CxxTest::SuiteDescription ** ppSuite, CxxTest::List **ppList);
    int TestParam;
};

Instances of the above test case class can be added dynamically to a dynamically
created suite as below

class MyTestSuite : public CxxTest::TestSuite
{
    static MyTestSuite *createSuite() 
    {
        // Add as many instances of TestWithMultipleParams as needed which
        // gets added to this suite "MyTestSuite".
        new TestWithMultipleParams("MyTestSuite", 0);
        new TestWithMultipleParams("MyTestSuite", 1);

        return new MyTestSuite;
    }

    static void destroySuite( MyTestSuite *suite ) { delete suite; }

    // A single static test case is required to make sure that the generator
    // won't give error :-(
    void testDummy(void)
    {
    }
};

Now the hard part is the implementation of GetSuiteInfo which returns the suite
description and the tests list of the suite as below

bool TestWithMultipleParams::GetSuiteInfo(CxxTest::SuiteDescription **ppSuite, CxxTest::List ** ppList)
{
    CxxTest::RealWorldDescription wd;
    CxxTest::SuiteDescription * sd;
    for (sd = wd.firstSuite(); sd; sd = sd->next())
    {
        if (strcmp(sd->suiteName(), SuiteName) == 0)
        {
            *ppSuite = sd;

            // The following is incorrect because the member _tests is private
            // and also is part of a derived class of SuiteDescription. The
            // later problem is Ok because we can assume that the suite to add
            // dynamic test cases must be RealSuiteDescription. However the
            // former problem is severe.

            // *ppList = ((RealSuiteDescription *) sd)->_tests;

            // The simpler solution is to add a public method to return the
            // list instance of the suite
            *ppList = ((RealSuiteDescription *) sd)->tests();

            return true;
        }
    }

    return false;
}

As explained in the comments above, the non-accessibility of _tests requires
adding a patch to the class RealSuiteDescription in the existing cxxtest
framework as below.

class RealSuiteDescription : public SuiteDescription
{
public:
   ...
   ...
   List &tests() { return *_tests; }

private:
   ...
   ...
};

I am currently solving this problem without the above patch in a complicated way
through some macro trick, part of which is as given below.

#define DynamicSuiteDescription MyDynamicSuiteDescription

All the DynamicSuiteDescription in the generated mytestsuite.cc will be changed
to MyDynamicSuiteDescription through this macro. _tests pointer is stored during
initialization/constructor of MyDynamicSuiteDescription and then retrieved later
by GetSuiteInfo.

Please add the above simpler patch instead to the existing cxxtest framework.

Formal solution

Now comes the formal solution. The above solution tries to solve the problem of
dynamic addition of test cases without changing cxxtest. And it assumes
RealWorldDescription, RealSuiteDescription, RealTestDescription,
SuiteDescription, DynamicSuiteDescription, List classes to be interfaces of
cxxtest (which may not be the case) and also it is not simple for the user. The
simpler interface would be similar to the existing cxxtest generator interface.
Proposed solution is that all the dynamic test cases take parameters (currently
only int or void *) in the test suite as given below.

class MyTestSuite : public CxxTest::TestSuite
{
    // A dynamic test case which takes int parameter.
    void testWithIntParam(int Param)
    {
        // Test based on the value of Param
    }

    // A dynamic test case which takes void * parameter
    void testWithPointerParam(void * Param1)
    {
        // Test based on the value of Param
    }
};

Dynamic test cases will not be instantiated statically and hence will not run if
not instantiated explicitely. They can be instantiated by overriding the
createTests and using a new cxxtest interface function addTest

class MyTestSuite : public CxxTest::TestSuite
{
    void createTests() 
    {
        addTest("testWithIntParam", 10);
        addTest("testWithIntParam", 20);
        addTest("testWithPointerParam", NULL);
    }

    void destroyTests()
    {
        removeTest(NULL); // removes all the dynamically added tests
    }

    // test cases
    void testWithIntParam(int Param)
    ...
};

Whenever a dynamic testcase is instantiated, cxxtext implemetation allocates an
instance of the class specific to the test case derived from RealTestDescription
and adds it to the suite from which addTest is called. A patch is attached
implementing this feature. The following gives some interface decision details.

(1) Prototype for addTest

addTest implementation should identify the dynamic test case to be added i.e. it
should map the test case specified by the user to the class which needs to be
instantiated. There are 2 possibilities. One using test case name string and
another using member pointer. Both are possible to implement and it is just a
convenience.

addTest implementation should also identify the suite to which the test case
needs to be added. It is done by mapping the this pointer (TestSuite *) to the
corresponding test suite description. This allows the user convenience in not
specifying the suite explicitly.

There is an another interface function called addTestFromSuite, to allow reusing
test suite from another suite. This function takes an additional argument as
string to identify the suite which contains the test case.

(2) createTests/destroyTests

These functions shouldn't be really necessary because test cases can be added
from anywhere in the code e.g. constructor of the suite. However due to the fact
that suite is statically instantiated before the suite description, we do not
have a choice other than to have an explicit createTests function which is
called only when both the suite and suite description instances are ready.

(3)

With this patch, if there are no tests, the suite is still generated,
because of the possibility of addition of dynamic tests. Hence one of the
cxxtest self tests 'No tests' fails currently.

The patch provided is backward compatible. It doesn't change the interface of
the existing classes. Just adds to it. It is also compile time forward
compatible i.e. dynamic test suites will compile with older cxxtest except that
the dynamic cases will not run.

A sample DynamicTest.hpp is provided.

Issues to solve.

(1)

Dynamic tests depends on new and delete (memory management). However calls
to new/delete is made only from the generated code and unless dynamic test case
feature is used, these calls will not be generated. With the current
implementation of cxxtest there is no other option other than using dynamic
allocation because the classes of the test cases are generated.

(2)

The patch does not provide python generation yet for dynamic test cases.
However it should be compatible for static test cases.

(3)

The patch currently does not yet provide a way to add a suite dynamically.

(4)

The test count displayed by the printer is not proper, because the dynamic
test cases are added only during the running of the test cases, while the test
case count is calculated before the run. It requires moving the suite
description setUp() call before any test case is run.

@whart222
CxxTest member
whart222 commented Feb 4, 2012
diff -crN cxxtest_3_10_1//cxxtest/RealDescriptions.cpp cxxtest_dyntests//cxxtest/RealDescriptions.cpp
*** cxxtest_3_10_1//cxxtest/RealDescriptions.cpp    2004-07-05 23:45:26.000000000 +0530
--- cxxtest_dyntests//cxxtest/RealDescriptions.cpp  2011-11-02 17:05:50.719600500 +0530
***************
*** 32,38 ****
          _testName = argTestName;
          attach( argList );
      }
!         
      bool RealTestDescription::setUp()
      {
          if ( !suite() )
--- 32,45 ----
          _testName = argTestName;
          attach( argList );
      }
! 
!     void RealTestDescription::initLocation(unsigned argLine,
!                                            const char *argTestName )
!     {
!         _line = argLine;
!         _testName = argTestName;
!     }
!     
      bool RealTestDescription::setUp()
      {
          if ( !suite() )
***************
*** 97,103 ****
          _TS_CATCH_ABORT( {} )
              ___TSM_CATCH( file(), line(), "Exception thrown from test" );
      }
!         
      RealSuiteDescription::RealSuiteDescription() {}
      RealSuiteDescription::RealSuiteDescription( const char *argFile,
                                                  unsigned argLine,
--- 104,122 ----
          _TS_CATCH_ABORT( {} )
              ___TSM_CATCH( file(), line(), "Exception thrown from test" );
      }
! 
!     void DynamicTestDescription::initialize(int param, List &argList, SuiteDescription &argSuite)
!     {
!         intParam = param;
!         RealTestDescription::initialize(argList, argSuite, line(), testName());
!     }
! 
!     void DynamicTestDescription::initialize(void * param, List &argList, SuiteDescription &argSuite)
!     {
!         genericParam = param;
!         RealTestDescription::initialize(argList, argSuite, line(), testName());
!     }
! 
      RealSuiteDescription::RealSuiteDescription() {}
      RealSuiteDescription::RealSuiteDescription( const char *argFile,
                                                  unsigned argLine,
***************
*** 151,157 ****
          }
          return false;        
      }
!         
      StaticSuiteDescription::StaticSuiteDescription() {}
      StaticSuiteDescription::StaticSuiteDescription( const char *argFile, unsigned argLine,
                                                      const char *argSuiteName, TestSuite &argSuite,
--- 170,289 ----
          }
          return false;        
      }
! 
!     bool RealSuiteDescription::removeTest(const char *testName)
!     {
!         bool removed = false;
!         for ( TestDescription *td = firstTest(); td != 0; td = td->next() ) {
!             if ( testName == NULL || stringsEqual( td->testName(), testName ) ) {
!                 if ( ((RealTestDescription *) td)->IsDynamic() )
!                 {
!                     td->detach(*_tests);
!                     deleteTest((DynamicTestDescription *) td);
!                     removed = true;
!                 }
!             }
!         }
!         return removed;        
!     }
! 
!     RealSuiteDescription * suiteDescriptionFromSuite(TestSuite * pSuite)
!     {
!         RealWorldDescription wd;
!         SuiteDescription * sd;
!         for (sd = wd.firstSuite(); sd != 0; sd = sd->next() )
!         {
!             if (sd->suite() == pSuite) break;
!         }
! 
!         return (RealSuiteDescription *) sd;
!     }
! 
!     RealSuiteDescription * suiteDescriptionFromName(const char * suiteName)
!     {
!         RealWorldDescription wd;
!         SuiteDescription * sd;
!         for (sd = wd.firstSuite(); sd != 0; sd = sd->next() )
!         {
!             if (stringsEqual( sd->suiteName(), suiteName )) break;
!         }
! 
!         return (RealSuiteDescription *) sd;
!     }
! 
!     bool TestSuite::addTest(const char *testName, int param, const char * newName)
!     { 
!         RealSuiteDescription * sd;
!         sd = suiteDescriptionFromSuite(this);
!         if (sd == NULL) return false;
! 
!         DynamicTestDescription * pTest = sd->newTest(testName,
!             RealSuiteDescription::DYNAMIC_TESTTYPE_INT);
!         if (pTest == NULL) return false;
! 
!         pTest->initialize(param, sd->tests(), *sd);
!         if (newName) pTest->initName(newName);
!         return true;
!     }
! 
!     bool TestSuite::addTest(const char *testName, void * param, const char * newName)
!     {
!         RealSuiteDescription * sd;
!         sd = suiteDescriptionFromSuite(this);
!         if (sd == NULL) return false;
! 
!         DynamicTestDescription * pTest = sd->newTest(testName,
!             RealSuiteDescription::DYNAMIC_TESTTYPE_VOIDP);
!         if (pTest == NULL) return false;
! 
!         pTest->initialize(param, sd->tests(), *sd);
!         if (newName) pTest->initName(newName);
!         return true;
!     }
! 
!     bool TestSuite::addTestFromSuite(const char *suiteName, const char *testName,
!         int param, const char * newName)
!     {
!         RealSuiteDescription * sd, * testSuite;
!         sd = suiteDescriptionFromSuite(this);
!         testSuite = suiteDescriptionFromName(suiteName);
!         if (sd == NULL || testSuite == NULL) return false;
! 
!         DynamicTestDescription * pTest = testSuite->newTest(testName,
!             RealSuiteDescription::DYNAMIC_TESTTYPE_INT);
!         if (pTest == NULL) return false;
! 
!         pTest->initialize(param, sd->tests(), *sd);
!         if (newName) pTest->initName(newName);
!         return true;
!     }
! 
!     bool TestSuite::addTestFromSuite(const char *suiteName, const char *testName,
!         void *param, const char * newName)
!     {
!         RealSuiteDescription * sd, * testSuite;
!         sd = suiteDescriptionFromSuite(this);
!         testSuite = suiteDescriptionFromName(suiteName);
!         if (sd == NULL || testSuite == NULL) return false;
! 
!         DynamicTestDescription * pTest = testSuite->newTest(testName,
!             RealSuiteDescription::DYNAMIC_TESTTYPE_VOIDP);
!         if (pTest == NULL) return false;
! 
!         pTest->initialize(param, sd->tests(), *sd);
!         if (newName) pTest->initName(newName);
!         return true;
!     }
! 
!     bool TestSuite::removeTest(const char *testName)
!     {
!         RealSuiteDescription * sd;
!         sd = suiteDescriptionFromSuite(this);
!         if (sd == NULL) return false;
! 
!         return sd->removeTest(testName);
!     }
!     
      StaticSuiteDescription::StaticSuiteDescription() {}
      StaticSuiteDescription::StaticSuiteDescription( const char *argFile, unsigned argLine,
                                                      const char *argSuiteName, TestSuite &argSuite,
***************
*** 179,185 ****
          return _suite;
      }

!     bool StaticSuiteDescription::setUp() { return true; }
      bool StaticSuiteDescription::tearDown() { return true; }

      CommonDynamicSuiteDescription::CommonDynamicSuiteDescription() {}
--- 311,320 ----
          return _suite;
      }

!     bool StaticSuiteDescription::setUp() {
!         _suite->createTests();
!          return true;
!     }
      bool StaticSuiteDescription::tearDown() { return true; }

      CommonDynamicSuiteDescription::CommonDynamicSuiteDescription() {}
diff -crN cxxtest_3_10_1//cxxtest/RealDescriptions.h cxxtest_dyntests//cxxtest/RealDescriptions.h
*** cxxtest_3_10_1//cxxtest/RealDescriptions.h  2004-07-05 23:45:26.000000000 +0530
--- cxxtest_dyntests//cxxtest/RealDescriptions.h    2011-11-02 17:27:26.296692300 +0530
***************
*** 17,22 ****
--- 17,24 ----
          RealTestDescription();
          RealTestDescription( List &argList, SuiteDescription &argSuite, unsigned argLine, const char *argTestName );
          void initialize( List &argList, SuiteDescription &argSuite, unsigned argLine, const char *argTestName );
+         void initLocation(unsigned argLine, const char *argTestName);
+         void initName(const char *argTestName) { _testName = argTestName; }

          const char *file() const;
          unsigned line() const;
***************
*** 31,36 ****
--- 33,39 ----
          bool setUp();
          void run();        
          bool tearDown();
+         virtual bool IsDynamic() { return false; }

      private:
          RealTestDescription( const RealTestDescription & );
***************
*** 43,48 ****
--- 46,69 ----
          const char *_testName;
      };

+     class DynamicTestDescription : public RealTestDescription
+     {
+     public:
+         DynamicTestDescription(unsigned argLine, const char * argTestName)
+         {
+             RealTestDescription::initLocation(argLine, argTestName);
+         }
+         void initialize(int param, List &argList, SuiteDescription &argSuite);
+         void initialize(void * param, List &argList, SuiteDescription &argSuite);
+         virtual bool IsDynamic() { return true; }
+     protected:
+         union
+         {
+             int intParam;
+             void * genericParam;
+         };
+     };
+ 
      class RealSuiteDescription : public SuiteDescription
      {
      public:
***************
*** 60,74 ****
--- 81,102 ----
          SuiteDescription *next();
          const SuiteDescription *next() const;

+         List &tests() { return *_tests; }
          unsigned numTests() const;
          const TestDescription &testDescription( unsigned i ) const;

          void activateAllTests();
          bool leaveOnly( const char *testName );
+ 
+         enum { DYNAMIC_TESTTYPE_INT = 1, DYNAMIC_TESTTYPE_VOIDP };
+         bool removeTest(const char * /*testName*/);

      private:
          RealSuiteDescription( const RealSuiteDescription & );
          RealSuiteDescription &operator=( const RealSuiteDescription & );
+ 
+         virtual DynamicTestDescription * newTest(const char * /*testName*/, int /*TestType*/) { return 0; }
+         virtual void deleteTest(DynamicTestDescription * /*pTest*/) {}

          const char *_file;
          unsigned _line;
***************
*** 77,82 ****
--- 105,111 ----

          static List _suites;
          friend class RealWorldDescription;
+         friend class TestSuite;
      };

      class StaticSuiteDescription : public RealSuiteDescription
***************
*** 86,91 ****
--- 115,121 ----
          StaticSuiteDescription( const char *argFile, unsigned argLine,
                                  const char *argSuiteName, TestSuite &argSuite,
                                  List &argTests );
+         ~StaticSuiteDescription() { if (_suite) _suite->destroyTests(); }

          void initialize( const char *argFile, unsigned argLine,
                           const char *argSuiteName, TestSuite &argSuite,
***************
*** 178,183 ****
--- 208,214 ----
          _TS_TRY {
              _TSM_ASSERT_THROWS_NOTHING( file(), _createLine, "Exception thrown from createSuite()", createSuite() );
              _TSM_ASSERT( file(), _createLine, "createSuite() failed", suite() != 0 );
+             if (suite() != 0) suite()->createTests();
          }
          _TS_CATCH_ABORT( { return false; } );

***************
*** 189,194 ****
--- 220,226 ----
      {
          if ( !_suite )
              return true;
+         suite()->destroyTests();

          _TS_TRY {
              _TSM_ASSERT_THROWS_NOTHING( file(), _destroyLine, "destroySuite() failed", destroySuite() );
diff -crN cxxtest_3_10_1//cxxtest/TestSuite.h cxxtest_dyntests//cxxtest/TestSuite.h
*** cxxtest_3_10_1//cxxtest/TestSuite.h 2004-11-20 19:27:00.000000000 +0530
--- cxxtest_dyntests//cxxtest/TestSuite.h   2011-11-02 13:33:19.854337300 +0530
***************
*** 24,31 ****
          virtual ~TestSuite();
          virtual void setUp();
          virtual void tearDown();
      };
!     
      class AbortTest {};
      void doAbortTest();
  #   define TS_ABORT() CxxTest::doAbortTest()
--- 24,40 ----
          virtual ~TestSuite();
          virtual void setUp();
          virtual void tearDown();
+         virtual void createTests() {}
+         virtual void destroyTests() {}
+     public:
+         /* Dynamic addition of test cases. */
+         bool addTest(const char * testName, int param, const char * newName = 0);
+         bool addTest(const char * testName, void * param, const char * newName = 0);
+         bool addTestFromSuite(const char * suiteName, const char * testName, int param, const char * newName = 0);
+         bool addTestFromSuite(const char * suiteName, const char * testName, void * param, const char * newName = 0);
+         bool removeTest(const char * testName);
      };
! 
      class AbortTest {};
      void doAbortTest();
  #   define TS_ABORT() CxxTest::doAbortTest()
diff -crN cxxtest_3_10_1//cxxtestgen.pl cxxtest_dyntests//cxxtestgen.pl
*** cxxtest_3_10_1//cxxtestgen.pl   2004-12-02 00:48:54.000000000 +0530
--- cxxtest_dyntests//cxxtestgen.pl 2011-11-02 17:10:31.518365100 +0530
***************
*** 179,186 ****
         'file' => $file,
         'line' => $line,
         'generated' => $generated,
!        'create' => 0,
!        'destroy' => 0,
         'tests' => [],
         'lines' => [] };
  }
--- 179,187 ----
         'file' => $file,
         'line' => $line,
         'generated' => $generated,
!        'createSuite' => 0,
!        'destroySuite' => 0,
!        'dyntests' => 0,
         'tests' => [],
         'lines' => [] };
  }
***************
*** 193,206 ****
  sub scanLineForTest($$) {
    my ($lineNo, $line) = @_;
    if ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*(void)?\s*\)/ ) {
!     addTest( $2, $lineNo );
    }
  }

  sub addTest($$$) {
!   my ($name, $line) = @_;
    $test = { 'name' => $name,
!       'line' => $line };
    push @{suiteTests()}, $test;
  }

--- 194,214 ----
  sub scanLineForTest($$) {
    my ($lineNo, $line) = @_;
    if ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*(void)?\s*\)/ ) {
!     addTest( $2, $lineNo, 0 );
!   } elsif ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*int\s*\w*\s*\)/ ) {
!     addTest( $2, $lineNo, 1 );
!     $suite->{'dyntests'} = 1;
!   } elsif ( $line =~ m/^([^\/]|\/[^\/])*\bvoid\s+([Tt]est\w+)\s*\(\s*void\s*\*\s*\w*\s*\)/ ) {
!     addTest( $2, $lineNo, 2 );
!     $suite->{'dyntests'} = 1;
    }
  }

  sub addTest($$$) {
!   my ($name, $line, $dynamic) = @_;
    $test = { 'name' => $name,
!       'line' => $line,
!             'dynamic' => $dynamic };
    push @{suiteTests()}, $test;
  }

***************
*** 237,243 ****
  }

  sub closeSuite() {
!   if ( $suite && scalar @{suiteTests()} ) {
      verifySuite();
      rememberSuite();
    }
--- 245,251 ----
  }

  sub closeSuite() {
!   if ( $suite ) {
      verifySuite();
      rememberSuite();
    }
***************
*** 276,285 ****
--- 284,295 ----
  sub suiteTests() { return $suite->{'tests'}; }
  sub suiteCreateLine() { return $suite->{'createSuite'}; }
  sub suiteDestroyLine() { return $suite->{'destroySuite'}; }
+ sub suiteDynTests() { return $suite->{'dyntests'}; }
  sub fileName() { return $suite->{'file'}; }
  sub fileString() { return cstr(fileName()); }
  sub testName() { return $test->{'name'}; }
  sub testLine() { return $test->{'line'}; }
+ sub testDynamic() { return $test->{'dynamic'}; }

  sub suiteObject() { return "suite_".suiteName(); }

***************
*** 390,397 ****
--- 400,411 ----
      if ( $suite->{'generated'} ) { generateSuite(); }
      dynamicSuite() ? writeSuitePointer() : writeSuiteObject();
      writeTestList();
+     writeSuiteDescriptionClass();
      writeSuiteDescription();
      writeTestDescriptions();
+     if (suiteDynTests()) {
+         writeSuiteTestCreation();
+     }
    }
  }

***************
*** 450,455 ****
--- 464,497 ----
    }
  }

+ sub writeSuiteDescriptionClass() {
+     if (suiteDynTests())  {
+         if (dynamicSuite()) {
+             printf "\nclass %s : public CxxTest::DynamicSuiteDescription<%s> {\n", suiteClass(), suiteName();
+             printf "public:\n";
+             printf " %s() {}\n", suiteClass();
+             printf " %s(const char *argFile, unsigned argLine, const char *argSuiteName,\n", suiteClass();
+             printf "                     CxxTest::List &argTests, %s *&argSuite, unsigned argCreateLine,\n", suiteName();
+             printf "                     unsigned argDestroyLine) : CxxTest::DynamicSuiteDescription<%s>(argFile,\n", suiteName();
+             printf "                     argLine, argSuiteName, argTests, argSuite, argCreateLine, argDestroyLine) {}\n";
+             printf " CxxTest::DynamicTestDescription * newTest(const char *testName, int TestType);\n";
+             printf " void deleteTest(CxxTest::DynamicTestDescription * pTest);\n";
+             printf "};\n\n";
+         } else {
+             printf "\nclass %s : public CxxTest::StaticSuiteDescription {\n", suiteClass();
+             printf "public:\n";
+             printf " %s() {}\n", suiteClass();
+             printf " %s(const char *argFile, unsigned argLine, const char *argSuiteName,\n", suiteClass();
+             printf "                     CxxTest::TestSuite &argSuite, CxxTest::List &argTests) :\n";
+             printf "                     CxxTest::StaticSuiteDescription(argFile, argLine, argSuiteName,\n";
+             printf "                     argSuite, argTests) {}\n";
+             printf " CxxTest::DynamicTestDescription * newTest(const char *testName, int TestType);\n";
+             printf " void deleteTest(CxxTest::DynamicTestDescription * pTest);\n";
+             printf "};\n\n";
+         }
+     }
+ }
+ 
  sub writeTestDescriptions() {
    foreach (@{suiteTests()}) {
      $test = $_;
***************
*** 461,483 ****
    return "suiteDescription_" . suiteName();
  }

  sub writeTestDescription() {
    my $class = "TestDescription_" . suiteName() . "_" . testName();
!   printf "static class $class : public CxxTest::RealTestDescription {\n";
    printf "public:\n";
!   $noStaticInit or
!     printf " $class() : CxxTest::RealTestDescription( %s, %s, %s, \"%s\" ) {}\n",
!       testList(), suiteDescription(), testLine(), testName();
    printf " void runTest() { %s }\n", dynamicSuite() ? dynamicRun() : staticRun();
!   printf "} testDescription_%s_%s;\n\n", suiteName(), testName();
  }

  sub dynamicRun() {
!   return sprintf( "if ( %s ) %s->%s();", suiteObject(), suiteObject(), testName() );
  }

  sub staticRun() {
!   return sprintf( "%s.%s();", suiteObject(), testName() );
  }

  sub writeSuiteDescription() {
--- 503,549 ----
    return "suiteDescription_" . suiteName();
  }

+ sub suiteClass() {
+   if (suiteDynTests()) {
+       return "SuiteDescription_" . suiteName();
+   } elsif (dynamicSuite()) {
+       return "CxxTest::DynamicSuiteDescription<" . suiteName() . ">";
+   } else {
+       return "CxxTest::StaticSuiteDescription";
+   }
+ }
+ 
  sub writeTestDescription() {
    my $class = "TestDescription_" . suiteName() . "_" . testName();
!   my $base = testDynamic() ? "DynamicTestDescription" : "RealTestDescription";
!   printf "%sclass $class : public CxxTest::$base {\n", testDynamic() ? "" : "static ";
    printf "public:\n";
!   if (testDynamic()) {
!         printf " $class() : CxxTest::$base(%s, \"%s\" ) {}\n", testLine(), testName();
!   } else {
!       $noStaticInit or
!         printf " $class() : CxxTest::$base( %s, %s, %s, \"%s\" ) {}\n",
!           testList(), suiteDescription(), testLine(), testName();
!   }
    printf " void runTest() { %s }\n", dynamicSuite() ? dynamicRun() : staticRun();
!   if (testDynamic()) {
!     printf "};\n\n";
!   } else {
!     printf "} testDescription_%s_%s;\n\n", suiteName(), testName();
!   }
! }
! 
! sub dynamicParam() {
!     return testDynamic() ? sprintf("%sParam", testDynamic() == 1 ? "int" : "generic") : "";
  }

  sub dynamicRun() {
!   return sprintf( "if ( %s ) %s->%s(%s);", suiteObject(), suiteObject(), testName(),
!     dynamicParam());
  }

  sub staticRun() {
!   return sprintf( "%s.%s(%s);", suiteObject(), testName(), dynamicParam());
  }

  sub writeSuiteDescription() {
***************
*** 485,491 ****
  }

  sub writeDynamicDescription() {
!   printf "CxxTest::DynamicSuiteDescription<%s> %s", suiteName(), suiteDescription();
    if ( !$noStaticInit ) {
      printf "( %s, %s, \"%s\", %s, %s, %s, %s )",
        fileString(), $suite->{'line'}, suiteName(), testList(),
--- 551,557 ----
  }

  sub writeDynamicDescription() {
!   printf "%s %s", suiteClass(), suiteDescription();
    if ( !$noStaticInit ) {
      printf "( %s, %s, \"%s\", %s, %s, %s, %s )",
        fileString(), $suite->{'line'}, suiteName(), testList(),
***************
*** 495,507 ****
  }

  sub writeStaticDescription() {
!   printf "CxxTest::StaticSuiteDescription %s", suiteDescription();
    if ( !$noStaticInit ) {
      printf "( %s, %s, \"%s\", %s, %s )", fileString(), $suite->{'line'}, suiteName(), suiteObject(), testList();
    }
    print ";\n\n";
  }

  sub writeRoot() {
    print "#include <cxxtest/Root.cpp>\n";
  }
--- 561,612 ----
  }

  sub writeStaticDescription() {
!   printf "%s %s", suiteClass(), suiteDescription();
    if ( !$noStaticInit ) {
      printf "( %s, %s, \"%s\", %s, %s )", fileString(), $suite->{'line'}, suiteName(), suiteObject(), testList();
    }
    print ";\n\n";
  }

+ sub writeSuiteTestCreation() {
+   printf "CxxTest::DynamicTestDescription * %s::newTest(const char *testName, int TestType)\n", suiteClass();
+   printf "{\n";
+   printf "  switch (TestType)\n";
+   printf "  {\n";
+   my $i = 0;
+   for($i = 1; $i <= 2; $i++)
+   {
+     printf "  case %s:\n", ($i == 1 ? "RealSuiteDescription::DYNAMIC_TESTTYPE_INT" :
+        "RealSuiteDescription::DYNAMIC_TESTTYPE_VOIDP");
+     my $first = 1;
+     foreach (@{suiteTests()}) {
+       $test = $_;
+       if (testDynamic() == $i) {
+         writeTestCreation($first);
+         $first = 0;
+       }
+     }
+     printf "    break;\n";
+   }
+   printf "  }\n";
+   printf "\n  return NULL;\n";
+   printf "}\n\n";
+   printf "void %s::deleteTest(CxxTest::DynamicTestDescription * pTest)\n", suiteClass();
+   printf "{\n";
+   printf "  delete pTest;\n";
+   printf "}\n\n";
+ }
+ 
+ sub writeTestCreation($$) {
+    my $first = $_[0];
+    my $class = "TestDescription_" . suiteName() . "_" . testName();
+    printf "    %s (CxxTest::stringsEqual(testName, \"%s\"))\n",
+        $first ? "if" : "else if", testName();
+    printf "    {\n";
+    printf "      return new %s;\n", $class;
+    printf "    }\n";
+ }
+ 
  sub writeRoot() {
    print "#include <cxxtest/Root.cpp>\n";
  }
***************
*** 525,530 ****
--- 630,636 ----

      foreach (@{suiteTests()}) {
        $test = $_;
+       testDynamic() or
        printf "  testDescription_%s_%s.initialize( %s, %s, %s, \"%s\" );\n",
    suiteName(), testName(), testList(), suiteDescription(), testLine(), testName();
      }
diff -crN cxxtest_3_10_1//sample/DynamicTest.hpp cxxtest_dyntests//sample/DynamicTest.hpp
*** cxxtest_3_10_1//sample/DynamicTest.hpp  1970-01-01 05:30:00.000000000 +0530
--- cxxtest_dyntests//sample/DynamicTest.hpp    2011-11-02 17:33:38.515477800 +0530
***************
*** 0 ****
--- 1,38 ----
+ #ifndef __DYNAMICTEST_H
+ #define __DYNAMICTEST_H
+ 
+ #include <cxxtest/TestSuite.h>
+ 
+ class DynamicTest : public CxxTest::TestSuite
+ {
+ public:
+     void testWithParam(int Param)
+     {
+         if (Param == 1)
+         {
+             TS_TRACE("testWithParam invoked with param 1");
+         }
+         else if (Param == 2)
+         {
+             TS_TRACE("testWithParam invoked with param 2");
+         }
+         else
+         {
+             TS_ASSERT(false);
+         }
+     }
+ 
+     void createTests()
+     {
+         addTest("testWithParam", 1);
+         addTest("testWithParam", 2, "testWithParam(2)");
+     }
+ 
+     void destroyTests()
+     {
+         removeTest(NULL);
+     }
+ };
+ 
+ #endif
+ 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.