テストデータの省力化
提供されるテストデータをコードの試し実行の度にコピペするのは面倒くさい。そこで、テストデータを Python のコメントとしてコードに埋め込み、それを自動的に抽出とリダイレクトしてテストを行うラッパーを作成した。
問題点
AtCoder では、作成したコードに問題がないかテストするためのデータがいくつか提供されます。このデータをプログラムに与えて、同時に提供される出力と答え合わせをします。
AtCoder のテストデータはファイルではなく、標準入力を通じて与えます。そのためテストデータ毎に
- コードを実行する。
- テストデータをターミナルに paste する。
- 出力を確認する。
を繰り返す必要があります。一度で済めばよいのですが、たいてい何回もこれを繰り返す必要があり面倒くさい。
解決方法
そこでこのプログラムにテストデータを与える操作を自動化することにしました。その方法には 2 つの方法が考えられます。
StringIO
簡単な方法は、Python と VSCode で競プロ - 標準入力の簡易化とサンプルケース判定の自動化 -のように io module の StringIO を使用することです。
しかし、この方法では 2 つ問題点があります。
- 複数のテストデータを自動的に与えてテスト出来ない。
- input()の入力元が変更されているので、作成したコードをそのまま AtCoder に回答として提出するとエラーになってしまう。
2 番目の問題に対しては、そのまま提出できるように try except でキャッチと条件分岐をするようにもしてみたのですが、やっぱり面倒くさい感じでした。
外部 wrapper
そこで、テストデータはプログラムに記述しつつ、作成したコードを実行する外部 wrapper スクリプトを ChatGPT と相談しながら作成しました。
具体的には、作成したコードからテストデータを取り出し、それをテスト毎に分割して作成したコードに入力します。
import re
import subprocess
import sys
def extract_test_data(filename):
with open(filename, 'r', encoding='utf-8') as file:
content = file.read()
match = re.search(r'"""TEST_DATA(.*?)"""', content, re.DOTALL)
if match:
return match.group(1)
else:
return None
def run_prog_with_data(prog_name, data):
blocks = data.strip().split("\n\n") # Split into blocks by empty lines
for index, block in enumerate(blocks):
print(f"Input {index}")
print(block)
process = subprocess.Popen(["python3", prog_name], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
stdout, stderr = process.communicate(input=block)
print("")
print(f"Output {index}")
print(stdout, stderr)
if process.returncode != 0:
break
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 validate.py <filename>")
sys.exit(1)
filename = sys.argv[1]
extracted_data = extract_test_data(filename)
if extracted_data is not None:
# Use filename as program name
run_prog_with_data(filename, extracted_data)
else:
print("TEST_DATA not found.")
この wrapper をvalidate.py
とすると、ABC_399/A.py
を試すには次のように実行します。文法ミスなど例外が発生した場合には、そこでテストを終了します。
$ python3 validate.py ABC_399/A.py
Input 0
6
abcarc
agcahc
Output 0
2
Input 1
7
atcoder
contest
Output 1
7
Input 2
8
chokudai
chokudai
Output 2
0
Input 3
10
vexknuampx
vzxikuamlx
Output 3
4