1

主要问题是如何实现 startTest() 以便它在所有子类中调用 runTest 。谢谢!

/*******************
COMPILER TEST
*******************/

class archeTest
  {
  protected:
    short verbosity_;

  public:

    void setVerbosity(short v)
      {
      if( ((v == 1) || (v == 0) ) 
        {  
        verbosity_ = v;
        }
      else 
        {
        cout << " Verbosity Level Invalid " << endl;
        }
      }

    virtual void runTest() = 0;
      {
      }

    void startTest()
      {
      }
  };

class testNatives : public archeTest 
  {
  public:

    void runTest()
      {
      testInts<short>();
      testInts<int>();
      testInts<long>();
      testInts<unsigned short>();
      testInts<unsigned int>();
      testInts<unsigned long>();
      }      

    void reportResults() const
      {
      }

  protected:

    template<class T> void testFloats()

    template<class T> void testInts()
      {

      verbosity_ = 1;  

      T     failMax;
      short passState;
      short bitDepth;

      const char* a = typeid(T).name();
      bool signedType = ((*a == 't') || (*a == 'j') || (*a == 'm'));

      /* Bit Depth - Algorithm */

      T pow2 = 1, minValue = 0, maxValue = 0, bitCount = 0, failValue = 0;  
      while(pow2 > 0)
        {
        pow2 *= 2;
        maxValue = pow2-1;
        bitCount++;
        }
      failValue = pow2;

      int native1 = bitCount;
      int native2 = sizeof(T)*8;
      int native3 = numeric_limits<T>::digits;  
      if( !signedType )
        {
        native1++;
        native3++;
        }       
      if(verbosity_)
        {
        cout << endl << "**********\n" << reportType(a) << "\n**********" << endl << endl;
        cout << "Bit Depth - Algorithm:\t" << native1 << endl;
        cout << "Bit Depth - Sizeof:\t" << native2 << endl;
        cout << "Bit Depth - File:\t" << native3 << endl;
        }   
        if (native1 == native2 && native1 == native3)
          {
          cout << "Correlation:\t\tPass" << endl ;
          }
        else
          {
          cout << "Correlation:\t\tFail" << endl ;
          }
        cout << "Max Value:\t\t" << maxValue << endl;
        cout << "Max+1 Value:\t\t" << failValue << endl;
      } 

    string reportType(const char* c1)
      { 
      string s1;
      switch(*c1)
        {
        case 't':
          s1 = "Unsigned short";
          break;
        case 'j':
          s1 = "Unsigned int";
          break;
        case 'm':
          s1 = "Unsigned long";
          break;
        case 's':
          s1 = "Short";
          break;
        case 'i':
          s1 = "Int";
          break;
        case 'l':
          s1 = "Long";
          break;
        default:
          s1 = "Switch failed";
        }
      return s1;
      }
  };

int main()
  {
  testNatives A;
  A.runTest();
  } 
4

6 回答 6

3

这是可能的,但要做到这一点,您必须使用抽象工厂模式。请阅读本文以了解抽象模式是什么以及它如何满足您的需求。

如果您可以在项目中使用 boost,那么您可以使用boost::factory 模板来实现自己的抽象工厂。

如果您不想推出自己的抽象工厂,您可以使用许多其他实现。是一个这样的实现的链接。

编辑:在这种情况下,您还需要一些机制来在编译时向工厂注册新的测试用例。这可以通过利用 c++ 预处理器或模板来实现。是一种使用模板的方法。

于 2011-05-18T00:03:03.577 回答
3

嗯,第一单责任原则。请记住,您archeTest不应该管理所有测试对象。只要有一个(不)著名的Manager这样做!

#include <vector>

class TestManager{
  std::vector<archeTest*> _tests;
public:
  // either
  void AddTest(archeTest* test){
    _tests.push_back(test);
  }

  // OR
  archeTest* CreateTest(/*here_be_params*/){
    archeTest* test = new archeTest(/*params*/);
    // do whatever
    _tests.push_back(test);
    return test;        
  }

  void RunAllTests() const{
    for(int i=0; i < _tests.size(); ++i)
      _tests[i]->runTests(); 
  }

  // if you create tests in here, you also need to release them at the end
  // ONLY do this if your created the tests with CreateTest
  // or if you transfer the ownership of the test pointer to TestManager
  ~TestManager(){
    for(int i=0; i < _tests.size(); ++i)
      delete _tests[i];
  }
};

运行

TestManager tmgr;
// create all your tests, either with
// archeTest* p = tmgr.CreateTest();
// OR
// archeTest* p = new archeTest();
// tmg.AddTest(p);
// and then run with
tmgr.RunAllTests();

同样,请参阅实现中的注释TestManager


现在,如果你真的不想要一个额外的课程......这实际上更容易,但它有点代码味道。只需将您的类添加archeTest到静态链表的构造函数中 - 问题就解决了!当然,在销毁时再次删除它。这是因为每个派生类 xxxstructor 都会自动调用基类版本—— * con *structor 在它自己之前, * de *structor 在它自己之后:

#include <list>

class archeTest{
private:
  typedef std::list<archeTest*> TestList;
  static TestList _all_tests;

  // to erase the right test on destruction
  TestList::iterator _this_test;

public:
  archeTest(){
    _all_tests.push_back(this);
  }

  ~archeTest(){
    _all_tests.erase(_this_test);
  }

  static void RunAllTests(){
    for(TestList::iterator it = _all_tests.begin(); it != _all_tests.end(); ++it)
      (*it)->runTests();
  }
};

// in some TestManager.cpp
#include "TestManager.h"

TestManager::TestList TestManager::_all_tests;

用一个简单的方法运行它

// create all your tests;
// ...
archeTest::RunAllTests();

因为它是一个静态成员函数,所以它不需要实例。

请注意,我使用了一个链接列表,因为它允许我安全地删除列表中间的测试,而不会使存储在其他测试对象中的引用失效。

于 2011-05-18T00:26:56.617 回答
2

由于很多人发现很难阅读我列出的所有帖子。我使用 boost 实现了这个版本。

诀窍在于,当使用派生类定义(新测试用例)扩展 TestTemplate 的定义时,由于静态 const 的初始化,它会强制调用 TestManager::Register 方法。

template<typename TestCase> 
const unsigned Test<TestCase>::m_uTestID = TestManager::Register(boost::factory<TestCase*>());

这确保派生类的构造函数的函子存储在 TestManager 的映射中。现在在 TestManager 中,我只需遍历地图并使用仿函数来实例化每个 Testcase 并在新创建的实例上调用 run 方法。

任何从 TestTemplate 派生的类都将自动注册,无需手动列出所有类。

#include <map>
#include <iostream>
#include <boost/function.hpp>
#include <boost/functional/factory.hpp>

class ITest {
public:
    virtual void run() {
        runTest();
    }
    virtual void runTest() = 0;
};


typedef boost::function< ITest* ()> TestFactory;

class TestManager {
public:
    static int Register(TestFactory theFactory) {
    std::cout<<"Registering Test Case"<<std::endl;
        m_NumTests++;
        m_mapAllTests[m_NumTests] = theFactory;
        return m_NumTests;
    }

  void run() {
        for(unsigned uTestID = 1; uTestID <= m_NumTests; uTestID++) {
            ITest* theTestCase = m_mapAllTests[uTestID]();
            theTestCase->run();
            delete theTestCase;
        }
    }

private:
    static unsigned m_NumTests;
    static std::map<unsigned,  TestFactory> m_mapAllTests;
};

unsigned TestManager::m_NumTests = 0;
std::map<unsigned,  TestFactory> TestManager::m_mapAllTests;


template<typename TestCase>
class Test : public ITest {
public:
  unsigned getID() const {
    return m_uTestID;
  }
private:
    static const unsigned m_uTestID;
};
template<typename TestCase> 
const unsigned Test<TestCase>::m_uTestID = TestManager::Register(boost::factory<TestCase*>());



class Test1 : public Test<Test1> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

class Test2 : public Test<Test2> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;

    }
};


class Test3 : public Test<Test3> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

class Test4 : public Test<Test4> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

int main() {
    TestManager theManager;
    theManager.run();
}

我确实在 VS05 和 VS10 上测试了该解决方案。下面是预期的输出。

Registering Test Case
Registering Test Case
Registering Test Case
Registering Test Case
Test Id:1
Test Id:2
Test Id:3
Test Id:4

希望它能消除一些混乱。

于 2011-05-18T18:46:22.913 回答
1

首先,您将在 archeTest 中像这样声明 startTest。

virtual void startTest() = 0;

这使它成为必须在子类中实现的纯虚函数。要在子类上调用此方法,您必须实例化该特定类的对象。然后,您可以通过基类指针或指向子类的指针调用 startTest。请注意,无论哪种情况,指针都必须指向子类的实例化(具体对象)。

于 2011-05-17T23:56:38.203 回答
1

您可能想查看UnitTest++(您可以在此处浏览源代码)。特别注意TestMacros.hand CheckMacros.h,顾名思义,它实现了宏来自动收集和运行测试。

例如,以下代码是“通过 UnitTest++ 运行失败测试的最小 C++ 程序”。

// test.cpp
#include <UnitTest++.h>

TEST(FailSpectacularly)
{
    CHECK(false);
}

int main()
{
    return UnitTest::RunAllTests();
}

您可以在(简短的)UnitTest++ 概述中阅读更多内容。我没有深入研究如何实现TESTCHECK宏的细节,但它们确实允许您在许多不同的 CPP 文件中声明测试,然后通过对UnitTest::RunAllTests(). 也许这与您想要做的足够接近?

于 2011-05-18T04:29:08.130 回答
0

编辑:没关系,我的第一个答案没有任何意义。在 C++ 中,您无法获取一个类的所有子类的列表来实例化它们,因此我认为您不能按照您想要的方式实现 runTests()。

于 2011-05-18T00:00:30.180 回答