この記事は「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 ...
テストが流れました。テストの結果の回収とか、テスト結果を見やすく表示するなどの機能は、これから考えていきます。