はじめに
AtCoderはじめました。プロコンは頭の体操になるので楽しいですね!
ただやっていて思ったのは
テストケースを手打ちで入力して出力を確認するのめんどくさい!
ってことです。
かといってUnitテストなんて書いている余裕はない...
もっとお手軽に検証できないか...
そうだスクリプトを書けばいいじゃないか!!!!
ということでやってみました。
環境
環境は以下を満たしていればいいです。
- Bashを実行できる環境
- 今回はmacOSで実行
- コマンドラインから実行できる言語
- 今回はJava8を使用
- 他の言語でも対応可能なはず(未検証)
テストケースを自動検証する
今回は例として以下の問題を解きます。
A - はじめてのあっとこーだー(Welcome to AtCoder)
問題文
高橋君はデータの加工が行いたいです。
整数 $a,b,c$ と、文字列 $s$ が与えられます。
整数 $a+b+c$ と、文字列 $s$ を並べて表示しなさい。
###入力
入力は以下の形式で与えられる。
a
b c
s
- $1$ 行目は、整数 $a$ $(1≦a≦1,000)$ が与えられる。
- $2$ 行目は、整数 $b,c$ $(1≦b,c≦1,000)$ が与えられる。
- $3$ 行目は、文字列 $s$ が与えられる。この文字列の長さは $1$ 文字以上 $100$ 文字以下である。
出力
$a+b+c$ と $s$ を空白区切りで $1$ 行に出力せよ。また、出力の末尾には改行を入れること。
標準入力にテストケースを入力させるため、テストケースをtextファイルに記述します。
1
2 3
test
AtCoderの解答例を使って確認していきましょう。
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
// 整数の入力
int a = sc.nextInt();
// スペース区切りの整数の入力
int b = sc.nextInt();
int c = sc.nextInt();
// 文字列の入力
String s = sc.next();
// 出力
System.out.println((a+b+c) + " " + s);
}
}
あとはこのテストケースをパイプで標準入力に流し込んであげるだけ。
$ javac Main.java # Javaなのでコンパイルを行う
$ cat input.txt | java Main # 実行
# 出力結果
6 test
また出力例と比較したい場合はdiff
コマンドを使ってあげることで簡単に違う部分を発見できます。
まず出力例のtextファイルを作成します。
6 text
以下のコマンドを実行すると検証ができます。
$ javac Main.java # Javaなのでコンパイルを行う
$ cat input.txt | java Main > output.txt # 実行&ファイルに書き込み
$ diff -u expected.txt output.txt
# 完全一致の場合何も出力されない
あとはこれをshellスクリプトに書き込んでいい感じにまとめてあげれば完成です!
#!/bin/sh
javac Main.java # Javaなのでコンパイルを行う
cat input.txt | java Main > output.txt # 実行&ファイルに書き込み
diff -u expected.txt output.txt
ディレクトリ構成は以下のように全て同じディレクトリにまとめてください。
$ tree .
├── input.txt
├── expected.txt
└── test.sh
$ sh test.sh
完成スクリプト
以下、私が作ったshellスクリプトです。参考になれば。
GitHub
https://github.com/terappy/auto-test-tool
#!/bin/sh
CMDNAME=`basename $0`
# get option
while getopts e: OPT
do
case $OPT in
"e" ) FLG_E="TRUE" ; VALUE_E="$OPTARG" ;;
* ) echo "Usage: $CMDNAME [-e VALUE] PARAMETERS" 1>&2
exit 1 ;;
esac
done
# remove option value
shift `expr $OPTIND - 1`
# check the number of arguments
if [ $# -ne 2 ]; then
echo "Usage: $CMDNAME classname input-file-name" 1>&2
exit 1
fi
echo "---------------------"
echo "input is":
echo "---------------------"
cat $2
echo ""
echo "---------------------"
echo "output is":
echo "---------------------"
# decide output file name.
OUTFILE_NAME="out-$2"
# compile the target java file.
javac $1.java
# execute with input file and write result into $OUTFILE_NAME
cat $2 | java $1 > $OUTFILE_NAME
cat $OUTFILE_NAME
if [ "$FLG_E" = "TRUE" ]; then
echo ""
echo "---------------------"
echo "verification result":
echo "---------------------"
# verification
if diff -u $VALUE_E $OUTFILE_NAME ; then
echo "====================="
echo "All matched!!"
echo "====================="
else
echo "====================="
echo "Miss matched"
echo "====================="
fi
fi
使い方
以下を実行してください。引数は以下のものを入力してください。
- CLASS_NAME
- クラス名を入れる
- Main.javaなら
Main
- INPUT_TEXTFILE
- 入力したい文字列を格納したtextファイル
- EXPECTED_RESULT_TEXTFILE
- 出力例を格納したtextファイル
$ sh test.sh ${CLASS_NAME} ${INPUT_TEXTFILE}
or
$ sh test.sh -e ${EXPECTED_RESULT_TEXTFILE} ${CLASS_NAME} ${INPUT_TEXTFILE}
使用例
$ sh test.sh -e expected.txt Main input.txt
おまけ
JavaであればIDEとしてIntelliJを使う人も多いと思います。IntelliJにはプロコンで使えるCHelperという便利なプラグインがあって、今回やったテストケースの実行がお手軽にできる上、入出力に用いるクラスやメソッドを指定できます。Javaでプロコンやるならこれを使った方がいいです←。
CHelper参考サイト
最後に
プロコンにおいて解答時間は貴重なもの、そして検証は何度も行う作業なのでこうしてスクリプトでサクッと検証できるようにしておくのはマストだと思っています。
参考になれば幸いです。