読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(macro-of-inline report) Rubyソースツリーをマクロ変換してビルドするRakefileを書いた

ソースコードを見て誤りに気づくのはもはや不可能に近い, 実際のソースコードをぶちこんでバグを洗い出すという方が現実的である.

私はビルドやテストを自動化するためにRakeを好む. Rakeはこのような用途に大変合う. 良い.

二種類のタスクを書いた.

  1. Rubyをcloneしてきて, 変換後コンパイルする (rake ruby 実行時間は30分くらい)
  2. それぞれの変換がどのようにして行われたのか単体で再現する(rake ruby$n)

出力:

~/macro-of-inline/tests$ rake -T | head
rake ruby     # test with ruby source tree
rake ruby1    # (./inits.c)
rake ruby10   # (./object.c)
rake ruby100  # (./missing/isinf.c)
rake ruby101  # (./missing/strstr.c)
rake ruby102  # (./missing/langinfo.c)
rake ruby103  # (./missing/hypot.c)
rake ruby104  # (./missing/strlcpy.c)
rake ruby105  # (./missing/lgamma_r.c)
rake ruby106  # (./missing/isnan.c)

macro-of-inlineはかなりバグってる. 変換に成功したと判断したものの出力がバグってたり, かなりカオスだ. 結局, この変換が正しいことを証明することはとても難しいので, 現実的なテストをパスしまくって正しさを実証するしかない. Rubyはその第一歩だ.

RUBY_SRC = "ruby-src"
RUBY_DIR = "ruby-src-macroize"
RUBY_FAIL_LOG = "ruby-failure.log"

desc "test with ruby source tree"
task "ruby" do
  dir = "ruby-src"
  sh "git clone https://github.com/ruby/ruby #{RUBY_SRC}" unless File.directory? dir
  root = "ruby-src-macroize"
  sh "rm -rf #{RUBY_DIR}" if File.directory? root
  sh "cp -r #{RUBY_SRC} #{RUBY_DIR}"

  Dir.chdir(RUBY_DIR) do
    sh "autoconf && ./configure"
    success_list = []
    failure_list = []
    Dir.glob("./**/*.c") do |f|     
      tmpfile = "/tmp/hoge.c"
      # Looked at the Makefile
      `macro-of-inline #{f} -o #{tmpfile} -I .ext/include/x86_64-linux ./include . --macroize-static-funs`
      e = $?.exitstatus
      if e == 0
        success_list << f
        `cp #{tmpfile} #{f}`
      else
        failure_list << f
        puts "[macroize] failed: #{f}"
      end
    end

    perc = success_list.size.to_f / (success_list.size + failure_list.size)

    File.open("../#{RUBY_FAIL_LOG}", "w") do |f|
      f.write """\
  #{failure.join("\n")}
  """
    end

    File.open("../ruby-info.log") do |f|
      f.write """\
  success: #{perc}%
  """
    end

    sh "./configure && make && make test"
  end
end

failures = []
if File.exists? RUBY_FAIL_LOG
  File.open(RUBY_FAIL_LOG).each do |line|
    failures << line.chomp
  end
end

Dir.chdir(RUBY_DIR) do
  testno = 0
  Dir.glob("./**/*.c") do |f|
    fail_mark = (failures.include? f) ? "[fail]" : ""
    testno += 1
    desc "#{fail_mark} (#{f})"
    task "ruby#{testno}" do
      Dir.chdir(RUBY_DIR) do
        sh "cp #{File.join("../#{RUBY_SRC}", f)} #{f}"
        sh "macro-of-inline #{f} -I .ext/include/x86_64-linux ./include . --macroize-static-funs --record ../record-macro-of-inline"
      end
    end
  end
end