FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

DSLでビルドツールを自作する (19日目 Rumy-Makeでテストを制御しよう)

この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の19日目の記事です。

19日目 Rumy-Makeでテストを制御しよう

最近のビルドツール、例えばCMakeやCargoでは、テストに関するルールや機能が追加されています。CMakeではctestというツールが付属しており、テストを定義しておけばそれを自動的にリグレッションとして実行することができます。また、Cargoにもcargo testという機能があり、Rustのコード内に仕込んだテストコードを実行するための仕組みが用意されています。

このように、ビルドとテストを同じ環境で実行できることについてはかなりのメリットがありそうです。そこで、今回はRumy-Makeにテスト環境を追加することを考えます。とりあえず目標とするのは、CMakeが用意しているadd_testと同様の機能で、テストを定義するとリグレッション実行時にそのテストを自動的に実行してくれる機能を作ってみます。

Rumy-Makeのテスト定義関数を追加する

Rumy-Makeのテストは、まずはCMakeのテストをまねてみることにします。つまり、

  • テストの名前
  • テストのコマンド
  • 結果を回収するログ

を受け取ってテストを定義し、rumy testコマンドで定義したテストをすべて実行する。という機能を実現します。テストの結果を一覧表示したり、分かりやすく統計情報としてまとめる、ということも考えられますが、とりあえず今回はテストを定義して実行できれば良しとします。

まずは、rumy-test.rbを作成し、テストのテンプレートとしてTestクラスを作成します。テストクラスでは、先ほど示したテストの名前、コマンド、回収ログファイル名を格納できるようにしときます。Rumyターゲットを作った時と同様に、テストクラスのインスタンスをすべてハッシュに格納しておき、実行時は順次実行していく、という形にしておけば良いのです。

  • src/rumy-test.rb
#!/usr/bin/ruby
# coding: utf-8

$test_list = Hash.new

class Test
  def initialize(name, command, logfile)
    @name = name
    @command = command
    @logfile = logfile
  end

  attr_reader :name
  attr_reader :command
  attr_reader :logfile
end

テストの追加は、add_testで行います。Testクラスをインスタンス化して、ハッシュに格納しておくだけです。

  • src/rumy-test.rb
def add_test(name, command, logfile)
  test = Test.new(name, command, logfile)
  $test_list[name] = test
end

テストを定義する際には、ビルドディレクトリでtest.rbを作成し、以下のように定義するわけです。

add_test "test_name", "commands", "test_log.log"

これをリグレッションテストとして実行するためのコマンドrun_testsを定義します。

  • src/rumy-test.rb
def run_tests
  $test_list.each{|key, value|
    puts value.command
    result = `#{value.command}`
    fp = File.open(value.logfile, "w") {|f|
      f.puts(result)
    }
  }
end

最後に、Rumyのメインファイルであるrumyに、testコマンドを追加しておきます。rumy testとコマンドで叩くと、そのディレクトリにあるtest.rbを読み込んでテストを実行するという仕組みにしておきます。

class RumyCLI < Thor
  default_command :build
...
  desc "rumy test", "Execute Tests"
  def test()
    load 'tests.rb'
    run_tests
  end
...

Swimmer-RISCVのテストをRumyに移植する

さて、次はSwimmer-RISCVのテストをRumyに移植します。これまで、Swimmer-RISCVのテストはMakefileシェルスクリプトを使って書いていました。Makefileでテストを定義して、実際のテストの内容は照すのの項目によってシェルスクリプトでオプションをコントロールしていました。

しかし、Makefileシェルスクリプトという2種類のファイルを管理するのが面倒なので、Rumyで一括管理してしまいます。というか、Rubyの力を使って、シェルの能力とMakefileの能力を一つのファイルにまとめ上げました。

  • `
load "rumy-test.rb"

target   = "./swimmer_riscv"
opts     = "--init_pc 0x80000000 --debug --stop-host --max 50000"

isa_dir = ENV["RISCV"] + "/riscv64-unknown-elf/share/riscv-tests/isa"

test_pats = Array.new
dir = Dir.open(isa_dir)
dir.each {|file|
  if not file.include?(".dump") then
    test_pats.push(file)
  end
}

test_pats.each{|pat|
  if pat.include?("rv64") then
    arch = "rv64imafdc"
  else
    arch = "rv32imafdc"
  end
  add_test pat, "#{target} #{opts} --arch #{arch} --binfile #{isa_dir}/#{pat}", "#{pat}.log"
}

Rubyのファイル制御機能を使用して、テストパタンをテストディレクトリから抽出し、パタン名に応じてオプションを切り替えます。そして、add_testコマンドでテストを定義すれば、それで終わりなわけです。

これで、rumy testコマンドを実行すると、テストが実行されます。

rumy test
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-breakpoint
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-csr
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-illegal
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-ma_addr
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-ma_fetch
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-mcsr
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-sbreak
./swimmer_riscv --init_pc 0x80000000 --debug --stop-host --max 50000 --arch rv32imafdc --binfile /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/isa/rv32mi-p-scall
...

テストが流れました。テストの結果の回収とか、テスト結果を見やすく表示するなどの機能は、これから考えていきます。