CppUnitテスト自動化スクリプトをERBで

eXtreme Programmingテスト技法―xUnitではじめる実践XPプログラミング (OOP foundations)で紹介されている、CppUnit-xのテストケースが書かれたヘッダファイルからTestRunnerを自動生成してくれるスクリプトを、ERBを使って実現してみました。

require 'erb'

#
# Template of TestSuite
#
class TestSuite
  TEST_SUITE_TEMPLATE = <<EOP
#include "<%= @classname %>"
class <%= @classname %>Suite : public <%= @classname %>
{
public:
    <%= @classname %>Suite(string name) : <%= @classname %>(name) {}
    static Test* suite() {
        TestSuite* suite = new TestSuite("<%= @classname %>");
<% @test_methods.each do |method| 
%>        suite->addTest(new TEST_CALLER(<%= @classname %>,<%= method %>));
<% end 
%>        return suite;
    }
};
EOP
  ADD_TEST_TEMPLATE = <<EOP
    runner.addTest("<%= @classname %>", <%= @classname %>Suite::suite());
EOP

  private
  def initializeTestMethods
    @test_methods = []
    File.readlines(@classname + ".h").each do |f|
      if /.*void (test.*)?(?).*/ =~ f then
        @test_methods.push($1)
      end
    end
  end

  public
  def initialize(classname)
    @classname = classname
    initializeTestMethods
  end
  
  #
  # return "runner.addTest(...)" code block
  #
  def addTest
    ERB.new(ADD_TEST_TEMPLATE).result(binding)
  end
  
  #
  # return concrete TestSuite code block
  #
  def suite
    ERB.new(TEST_SUITE_TEMPLATE).result(binding)
  end
end


#
# Automatic generator of TestRunner for CppUnit-x
#
class TestRunnerGenerator
  TEST_RUNNER_FILE_NAME = "Tester.cpp"
  COPYRIGHT = "Copyright (C) 2007 hogehoge. All rights reserved."
  AUTHOR = "hogehoge"
  CPPUNIT_HEADER_FILE = ["TestCase.h","TestSuite.h","TestRunner.h"]
  TEST_RUNNER_TEMPLATE = <<EOP
/*! @file <%= @out %>
    @brief UnitTest Main
    @author <%= AUTHOR %>

    <%= COPYRIGHT %>
*/
<% CPPUNIT_HEADER_FILE.each do |f| 
%>#include <cppunit/<%= f %>>
<% end %>
USING_NAMESPACE_CPPUNIT
using namespace std;
<% @test_suites.each do |test_suite| %>
<%= test_suite.suite %><% end %>
main(int argc, char* argv[]) {
    TestRunner runner;
<% @test_suites.each do |test_suite| %><%= 
  test_suite.addTest %><% end 
%>    return runner.run(argc, argv);
}
EOP

  private
  def initializeTestSuites
    Dir.open(Dir.pwd).each do |f|
      if /(.*Test)?.h/ =~ f then
        @test_suites.push TestSuite.new($1)
      end
    end
  end
  
  public
  def initialize
    @out = ""
    @test_suites = []
  end
  
  #
  # generate TestRunner
  #
  def generate(file_name = TEST_RUNNER_FILE_NAME)
    initializeTestSuites
    @out = file_name
    begin
      tester = File.new(@out, "w")
      tester.write ERB.new(TEST_RUNNER_TEMPLATE).result(binding)
    ensure
      tester.close      
    end
  end
end

TestRunnerGenerator.new.generate