5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NetBSDAdvent Calendar 2015

Day 20

Rump kernelを使ってATFテストを書く

Posted at

この記事はNetBSD Advent Calendar 2015の20日目として書かれました。

#はじめに

NetBSDにはATFというテストフレームワークが用意されており、ユーザランドプログラムやカーネルのためのテストが定期的に実行されされ、コード変更によってリグレッションを起こしていないかチェックされています。

またNetBSDにはrump kernelという仕組みがあり、ユーザプロセスとしてNetBSDカーネルを実行できます。ATFテストにはrump kernelを使ったテストがたくさんあります。

本記事では、特にNetBSDカーネルのネットワーク機能のATFテストをrump kernelを使って書く方法について説明します。

#ATFテストの書き方

基本的なATFテストの書き方はCreating atf-based tests for NetBSD srcに書いてあります。ATFテストはC言語とシェルスクリプトで書く方法がありますが、rump kernelを使ったテストは今ではシェルスクリプトで書くことが推奨されているので1、そちらの書き方の説明をします。

##シェルスクリプトでATFテストを書く
簡単なテストを書いてみます。

atf_test_case test1 cleanup
atf_test_case test2 cleanup

test1_head()
{
	atf_set "descr" "Tests for something of test1"
	atf_set "require.progs" "touch" "test"
}

test2_head()
{
	atf_set "descr" "Tests for something of test2"
}

test1_body()
{
	atf_check -s exit:0 touch testfile
	atf_check -s exit:0 test -f testfile
}

test2_body()
{
	// do some tests...
}

test1_cleanup()
{
	rm -f testfile
}

test2_cleanup()
{
	// cleanup
}

atf_init_test_cases()
{
	atf_add_test_case test1
	atf_add_test_case test2
}

ここではtest1とtest2という2つのテストを書きました。

まずatf_test_caseatf_init_test_casesatf_add_test_caseを使って、ATFにtest1とtest2の2つのテストを追加することを宣言します2atf_test_caseの第二引数のcleanupはこのテストにcleanup部があることを宣言しています3

テスト自体はheadとbodyとcleanupの3つで構成されています。headはテストのメタデータを書く場所で、テストの概要やテストに必要なコマンドを列挙します4。bodyはテスト本体です。テスト環境をセットアップしたり、atf_check等のassertionコマンドを用いてテストを記述します。cleanupはテストで作成したファイルを消したり、プロセスをkillしたり、デバッグ用の出力を行ったりする場所です。bodyの後に実行されます。上の例ではbodyで作ったファイルtestfileを消しています。

##atf_check
bodyで使われているatf_checkコマンドについて説明します。atf_checkはコマンドラインを引数に取り、コマンドを実行して期待する結果になっているかをチェックします。-s exit:0は指定したコマンドが成功する(exist statusが0)ことをassertionしています。逆に失敗することを期待する場合は、-s not-exit:0のように書きます。

atf_checkは他にも標準出力や標準エラー出力のチェックもできます。-s exit:0 -o match:"hoge"でコマンドが成功して、かつ標準出力にhogeという文字列が含まれているかをチェックします。-s not-exit:0 -e match:"fuga"でコマンドが失敗して、かつ標準エラー出力にfugaという文字列が含まれているかをチェックします。出力を無視したい時は-o ignore-e ignoreと書きます。

コマンドラインでパイプ等を使いたい場合は、atf_check ... -x "ls |grep hoge"のように書きます。

ほとんどの場合はatf_checkで事足りますが、単に文字列の一致をチェックをするatf_equalなどを使う場合もあります。

#Rump kernelを使ったATFテストの書き方

rump kernelを使って、NetBSDの通信機能が正しく動作するかpingを使ってチェックする、簡単なテスト(ping_test)を書いてみます。上記説明と重複する部分は省略しています。

##Rump kernelを使ったATFテスト例

inetserver="rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet"
inetserver="$inetserver -lrumpnet_netinet6 -lrumpnet_shmif"

SOCK1=unix://commsock1
SOCK2=unix://commsock2
BUS=bus

IP1=10.0.0.1
IP2=10.0.0.2
TIMEOUT=1

setup_rump_server
{
	local sock=$1
	local ip=$2
	local bus=$3

	atf_check -s exit:0 ${inetserver} $sock

	export RUMP_SERVER=$sock

	atf_check -s exit:0 rump.ifconfig shmif0 create
	atf_check -s exit:0 rump.ifconfig shmif0 linkstr $bus
	atf_check -s exit:0 rump.ifconfig shmif0 $ip
	atf_check -s exit:0 rump.ifconfig shmif0 up
	atf_check -s exit:0 rump.ifconfig -w 10
}

ping_test_body
{
	setup_rump_server $SOCK1 $IP1 $BUS
	setup_rump_server $SOCK2 $IP2 $BUS

	export RUMP_SERVER=$SOCK1
	atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT $IP2

	export RUMP_SERVER=$SOCK2
	atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT $IP1
}

ping_test_cleanup
{
	env RUMP_SERVER=$SOCK1 rump.halt
	env RUMP_SERVER=$SOCK2 rump.halt
}

このテストでやっていることは、2つのrump kernelプロセスを立ち上げネットワークインタフェースとIPを設定(setup_rump_server)、双方からpingが成功するかチェック、最後にcleanupでrump kernelプロセスを停止しています。

##rump_server

Rump kernelプロセスはrump_serverコマンドで立ち上げます。Rump kernelはモジュール化(共有ライブラリ化)されており、必要な機能だけをロードして使います。この例では、ネットワーク機能とrump kernel用ネットワークインタフェース(shmif)をロードしています。

rump_serverの第二引数には、rump_serverプロセスと他のユーザプロセスが通信するためのソケットを指定します。ATFテストではローカルで閉じているのでUnixドメインソケットを使っています。以降、当該rump_serverに対して何かコマンドを実行したい場合は、RUMP_SERVER環境変数にソケットを指定して、rump.XXXというコマンドを使って実行します。この例ではrump.ifconfigrump.pingコマンドを使っています5

##shmif

shmifはrump kernel用のネットワークインタフェースです。ファイルを介してEthernetフレームをrump kernelプロセス間でやりとりします。rump.ifconfig ... linkstr $busでファイル名を指定しています。

Rump kernel間で送受信されたEthernetフレームは、すべてファイルに保存されているので中身を見ることが簡単にできます。shmif_dumpbus -p - $bus 2>/dev/null| tcpdump -n -e -r -とやるとtcpdumpでフレームを表示させることができます。

#おわりに

簡単ですが、rump kernelを使ってATFテストを書く方法を説明してみました。

カーネルはコード規模が大きいので、既存の動作を壊さずコードを変更することが難しいです。Rump kernelでネットワーク機能をはじめとするカーネル機能のテストを書いておくことで、開発のコストが大幅に少なくなります。

カーネルのコードを改良することだけがカーネル開発ではありません。カーネルのためのATFテストを追加することもカーネル開発においてはとしても重要です。この記事がそのための一助になれば幸いです。

  1. ...とrump kernelの作者の人が言ってました。

  2. 重複しているように見えますが、atf_test_caseatf_init_test_cases(atf_add_test_case)は両方とも必要です。

  3. cleanupを書き忘れるとcleanup部が実行されないので忘れずに書きましょう。

  4. 例のためtouchとtestコマンドをrequire.progsに書いていますが、baseに常に入っているコマンドは書かなくても構いません。

  5. Rump kernelにはrump化されたコマンドを使う方法の他に、librumphijack.soというシステムコールをハイジャックする共有ライブラリを使って、rump化されていないユーザプログラムを使う方法もあります。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?