test "hello, world" (で各種ミスってyak shaving ww)~EHW2018「MVPを簡単にテスト」


概要

このエントリは、「Enterprise "hello, world" 2018 Advent Calendar 2018」の12/4向けのものです。このAdvent Calendarでは、複数個のエントリにまたがる話の流れも鑑みつつ、なるべく1エントリで1つのトピックをカバーできるようにする予定です。

このエントリで記載するトピックは、簡単なテストです。といって余裕で書き終わる予定だったのですがいくつかミスったので後半にポイントを残しておきます。

前提

おことわり

想定読者

「Enterprise "hello, world" 2018」的なネタとしては、下記のような状況を想定しています。

超簡単版のHello Worldコマンドはできたけれども、テストケースを作らなければならない。
現時点ではコマンドとして実行しているので、シェルでテストを書いておく。

テスト

テストケース

第1日目で確認した旧バージョンの"hello"と、第3日目で作った新バージョンのHelloWorld.classが同じく「hello, world」という文字列を標準出力に出力すること、がテストケースです。
これで現新比較的にOK、ということにします。

動作環境

  • Windows Subsystem for Linux (以下WSL)
  • Windows10 Pro 1803 (64bit)
  • openjdk version "11.0.1" 2018-10-16(on Windows10)
  • openjdk version "10.0.1" 2018-04-17 (on Ubuntu on Windows Subsystem for Linux)

テスト

超簡単版用の超簡単テストなので、特にテストフレームワークなど使わず、シェルで書いておきます。

#!/bin/bash

#JVM=/mnt/c/Java/jdk-11.0.1/bin/java.exe
JVM=/usr/bin/java

ACTUAL=`${JVM} HelloWorld`
EXPECTED="hello, world"
echo $ACTUAL

if [[ ${ACTUAL} = ${EXPECTED} ]]; then
        echo "OK"
else
        echo "NG"
fi

実行結果

$ bash -x test.sh
+ JVM=/usr/bin/java
++ /usr/bin/java HelloWorld
+ ACTUAL='hello, world'
+ EXPECTED='hello, world'
+ [[ hello, world = hello, world ]]
+ echo OK
OK

いい感じですね。新バージョンは、このケースが通ることでOKとします。

ミスしたところ

Windows Subsystem for Linuxで32bitプログラムが走らない(インストール直後だと)

第1日目のHello Worldプログラム、レガシィ感を出すためにRed Hat 7の32bit版で書きました。

今日のエントリは、古いバイナリからの出力と新しいバイナリからの出力をシェル上で比較しようと思って書き始めました。

Bash on WindowsってばLinuxのバイナリが動かせるえらい子、と思ってRed Hat 7 のVMからhelloのバイナリをscpでWindows側に持ってきて、さて実行。

$ ./hello
-bash: ./hello: cannot execute binary file: Exec format error

あちゃー。あたりまえですが、

$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.0.0, not stripped

Windows Subsystem for Linuxさんは、

Run common command-line free software such as grep, sed, awk, or other ELF-64 binaries.

(MSのWSLサイトより引用)なのでした。
が、これは動かす方法はありました(後述)。

Javaのline.separator

エントリを書き始めた時点では、WSLでWindows10側にインストールしたJava VMで実行するつもりでした。(上記シェル内のコメントアウト)

で、Windows側で実行されているので、Javaの"system.lineseparator"は"\r\n”であるからして、当然ですがこうなるわけです。

$ bash -x test.sh
+ JVM=/mnt/c/Java/jdk-11.0.1/bin/java.exe
++ /mnt/c/Java/jdk-11.0.1/bin/java.exe HelloWorld
' ACTUAL='hello, world
+ EXPECTED='hello, world'
 = hello, world ]]
+ echo NG
NG

理由:

$ /mnt/c/Java/jdk-11.0.1/bin/java.exe HelloWorld | od -c
0000000   h   e   l   l   o   ,       w   o   r   l   d  \r  \n
0000016

あちゃー。
新版書く時の仕様、出力する改行コードまで規定してなかったもんな。。。

で、ちょっと日和ってWSL(こっちはLinuxなんでline.separatorは'\n')にもJava入れるかっていって次は、、、

Ubuntuのopenjdk-11-jreは現時点ではJava10

WSL側でJavaを入れて、

sudo apt install openjdk-11-jre-headless

Hello.classを動かそうとすると

$ java HelloWorld
Error: LinkageError occurred while loading main class HelloWorld
        java.lang.UnsupportedClassVersionError: HelloWorld has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 54.0

あれ?"openjdk-11-jre-headless"って名前のもの入れたんだけどな。。と思って

$ java -version
openjdk version "10.0.1" 2018-04-17
OpenJDK Runtime Environment (build 10.0.1+10-Ubuntu-3ubuntu1)
OpenJDK 64-Bit Server VM (build 10.0.1+10-Ubuntu-3ubuntu1, mixed mode)

あちゃー。そりゃ.classファイルのバージョン違ったら新しいほうは動かせませんがな、はわかるのですがでもなんで?って思って調べてみると、

Our plan is to release OpenJDK 10 as the default JRE/JDK 1 for Bionic, and then move the default JRE/JDK in main to OpenJDK 11 in September/October 2018 as an SRU.
(引用:askubuntuのサイトより)

あちゃー。

ってことで、JREだけじゃなくてJDKも入れてコンパイルしなおして実行。ちょっとずるですが、まあ目をつぶろう。(第2日目で、ネタでJava11の機能使いまくらなくてよかった。。。)

補足

WSLで32bit ELFなLinuxバイナリを動かす試みとして、下記のサイトでは、「qemu-user-static」を使っている例が紹介されていました。(QEMUは皆さんご存知のOSSのエミュレータ)

これはいけるかも!と思ってapt-get installしようと思ったけれどもqemu-user-staticがありませぬ。ということで先に「apt get update」してパッケージを更新してから、

$ sudo apt-get install qemu-user-static

でqemuを入れて、サイトにならってバイナリのハンドラを登録して、

$sudo update-binfmts --install qemu-i386 /usr/bin/qemu-i386-static --magic '\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'

実行!

$ ./hello
/lib/ld-linux.so.2: No such file or directory

あちゃー。。第1日目で、ダイナミックリンクじゃなくてスタティックリンクにしておけばよかった。。。ld-linux.so.2をパッケージで用意します。(サイトにちゃんと書いてあった)

$ sudo apt-get install build-essential libc6-dev-i386
$ ./hello
hello, world

おー動いた動いた。

以上、Red Hat7で作った32bit ELFバイナリを↓で動かす、の補足でした。

$ uname -a
Linux ThinkPad-X270 4.4.0-17134-Microsoft #345-Microsoft Wed Sep 19 17:47:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
$ arch
x86_64

まとめ

このエントリでは、「Enterprise "hello, world" 2018 Advent Calendar 2018」(EHW2018)の4日目として、

簡単なテストをトピックとして取り上げました。クロスプラットフォームでかつ新しめのものなど混ぜたので、思ったよりもハマってしまいました。

EHW2018のネタとしては、このあと、このプログラムと同じような目的を持つものを現代的なツール群を用いて、なるべくめんどくさく実現していくことを考えています。