この記事は、ソフトウェアテスト #2 Advent Calendar 2018 - Qiita の1日目です。
github.com/kawasin73/bitset という Go 言語のビットベクトルのライブラリを作りました。このライブラリはバイトオーダーがビッグエンディアンのマシンでもリトルエンディアンのマシンでも同じ振る舞いをするように実装されています。
ビッグエンディアンとリトルエンディアンのそれぞれのマシンで正しく動くことを確認するテストを Travis CI で実行する手法を試行錯誤したので紹介します。
ビッグエンディアン、リトルエンディアンとは
エンディアン( Endianness )とはバイトの並び順のことで、ビッグエンディアン( Big Endian )とリトルエンディアン( Little Endian )の2種類があります。(ミドルエンディアンなど他のエンディアンもあるらしいですがごく少数派らしいです)また、バイトオーダーとも呼ばれます。
ビッグエンディアンは上位のバイトから並べていく方式で、リトルエンディアンは下位のバイトから並べていく方式です。
例えば Go 言語の uint32
の場合 32 ビットの整数であるためこれは 4 バイトで表されます。
16進数で 0x1234abcd
と表される数値は、ビッグエンディアンでは 0x12 | 0x34 | 0xab | 0xcd
と、リトルエンディアンでは 0xcd | 0xab | 0x34 | 0x12
と表されます。
エンディアンは CPU のアーキテクチャによって変わります。よく使われている amd64 のCPU(Intel や AMD などの CPU)では、リトルエンディアンです。
また、CPU によってはビッグエンディアンとリトルエンディアンを指定できる CPU もあるみたいです。
同一プロセスのメモリ上の値は全て同じエンディアンで扱われているため、通常のメモリ上の値を操作するプログラミングをする場合はエンディアンを気にする必要はありません。
しかし、マシンをまたいでバイナリデータをやり取りする場合はこのエンディアンに注意しないと、マシンによって全く異なる値に認識されてしまうことになります。(TCP/IP などのネットワークプロトコル操作やバイナリエンコードされたファイルを異なるマシンで扱うときなど)
両方のエンディアンでテストする
今回作った github.com/kawasin73/bitset は、ファイルに永続化されたビットベクトルが異なるマシンで利用されるユースケースを考慮して、どのエンディアンのマシンでも指定されたエンディアンでビットベクトルを扱うようにしています。
そのため、ビッグエンディアンとリトルエンディアンそれぞれのマシンで正しく動いているかをテストする必要がありました。
今回は自動テスト環境として Travis CI を利用することにしましたが、残念ながら Travis CI は amd64 のアーキテクチャの CPU にのみ対応しており、CPU のアーキテクチャの変更はできませんでした。
そのため、ビッグエンディアンの環境でのテストを何らかの方法でエミュレートして実行する必要があります。
今回は、 qemu-user-static
という CPU のエミュレータ を使ってビッグエンディアン環境でのテストを行うことにしました。
qemu から go test を呼び出す
qemu-user-static
は様々な CPU アーキテクチャをエミュレートして実行バイナリを実行してくれるソフトウェアです。
multiarch/qemu-user-static リポジトリにアーキテクチャごとのエミュレータがアップロードされているため、そこからダウンロードして利用することにしました。(apt-get
などでインストールできますが詳しくは後述)
以下のように qemu-<cpu architecture>-static
の引数にそのアーキテクチャ用の実行バイナリを渡すことで実行できます。
# ppc64 アーキテクチャの場合 $ qemu-ppc64-static ./some-ppc64-binary
go 言語自体(go build
や go test
などを行うツール群)は、Linux OS では amd64
, 386
, arm
, arm64
, s390x
, ppc64le
で提供されていますが、このうちビッグエンディアンなのは s390x
のみです。(Getting Started - The Go Programming Language)
しかし、qemu-s390x-static go test
を実行してもエラーが発生してしまうため go test
を直接実行する手法は断念 しました。
$ qemu-s390x-static go test Illegal instruction (core dumped)
テストの実行をコンパイルする
Go のテストは -c
オプションをつけることで、テストを実行するバイナリをコンパイルします。また、-o
をつけることでテストバイナリ名を指定できます。
$ GOOS=linux GOARCH=mips64 go test -c -o bitset.test
この手法を使い、コンパイルされたテスト実行バイナリを qemu を使って実行することにしました。
Go 言語では以下の CPU アーキテクチャに対応したバイナリをクロスコンパイルできます。
# go tool dist list | grep linux linux/386 linux/amd64 linux/arm linux/arm64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/s390x
これらのうちビッグエンディアンのアーキテクチャは ppc64
, mips
, mips64
, s390x
です。(https://github.com/tsuna/endian/blob/master/big.go#L6)
今回は、ppc64
アーキテクチャで正しく動くかをテストすることにしました。
以下の .travis.yml
は、Go 言語 1.11
1.10
1.9
の3つのバージョンで、それぞれリトルエンディアン(amd64
)とビッグエンディアン(ppc64
)で正しい挙動をすることをテストしています。
language: go os: - linux before_install: - wget https://github.com/multiarch/qemu-user-static/releases/download/v3.0.0/qemu-ppc64-static - chmod 755 qemu-ppc64-static go: - "1.11" - "1.10" - "1.9" script: - GOOS=linux GOARCH=ppc64 go test -c -o bitset.test - go test ./... -v - ./qemu-ppc64-static ./bitset.test -test.v
これによって、リトルエンディアンとビッグエンディアンの両方でテストすることが可能になりました。
qemu-user-static のインストール
今回は、multiarch/qemu-user-static というリポジトリにアップロードされている qemu-ppc64-static
を利用しましたが、qemu-user-static
自体は、apt-get
でインストールできるので、そちらでインストールすることが望ましいです。
$ sudo apt-get update $ sudo apt-get install -y qemu-user-static
しかし apt-get
でインストールした qemu-user-static
ではテストが途中で落ちる謎のバグがあるため、apt-get
でのインストールは現在も格闘中です。
最後に
何とかビッグエンディアンとリトルエンディアンでテストを行うことができました。よかったです。
これの試行錯誤の過程は github.com/kawasin73/bitset リポジトリの issue に記録しています。