テストケース自動生成のプログラムをどうやって管理しようかなって考えた話

今日はテストケースの自動生成とそれを用いたジャッジを自動で行ってくれるツールの作成を行っていました。

そこで考えてたこととかを書いておきます。

まず普段競プロをしている環境

OS:Windows10
IDEVisual studio 2019

以前まではテストケースを生成するプログラムを別のプロジェクトとしてVisual studioで作っていたんですが、そのテストしようと思ったらそこに毎回コピペしないといけないのが面倒だったんですよね

以前までのコードはこんな感じ

void make_testcase(){
    ifstream ifs("in.txt");
    //あれこれ
}

#define cin ifs
#define cout ofs
void solve(){
    ifstream ifs("in.txt");
    ofstream ofs("out.txt");
    //書いてたコードをコピペする
}
#undef cin
#undef cout

void solve_naive(){
    ofstream ofs("out_naive.txt")
    //愚直解を必要に応じて書く。WAになるケースを探したいときとかに使う。
}

void judge(){
    //out.txtとout_naive.txtの中身が等しいか判定する。
}

int main(){
    make_testcase();
    solve();
    solve_naive();
    judge();
}

毎回コードをコピペしないといけなのもそうですが、cinやcoutをdefineでファイル入出力に書き換えてる辺りとかかすごく気に入らなかったんですよね。
まぁ、あるコンテストの時間中に必要に駆られて急いで書いたものだったので…
それもあって今回新しく作ってみました。

一番の目的はコピペする作業をなくして、元ファイルを更新すればテストの方のプログラムも書き換わる状態にすることです。

まず考えたのはテストを書いてるファイルで元ファイルをincludeすることです。
Pythonとかだったらこの方法で出来そうだなという気持ちになります。

提出用プログラム.py

def solve():
    #提出用コードを書く

if __name__ == "__main__":
    solve()

テスト用プログラム.py

from 提出用プログラム import solve
#さっきのc++で書いたやつっぽいのを書く

ただ同じような感じではC++だと出来なさそうな気持ちになりました。(できるのかもしれないけど私には思いつかなかった…)
includeしちゃうと提出用プログラムに書いてあるmain関数が実行対象になってしまうので出来なさそうなんですよね
提出用のソースコードとしての要件を満たしつつ、テスト用にも使えるように提出用プログラムを書くのは大変そうなので今回は諦めました。

そこで今回はbashを使うことでこの問題を解決することを考えました。
Windows環境なのでバッチファイルを作ってもよかったんですが、いかんせんとっつきにくいところがあるので…
そういうちょっとしたことをやりたいとき、なにかと便利なWSL(Windows Subsystem for Linux)にお世話になろうと思います。

ここでちょっとWSLについて軽く説明。
といっても原理とかではなく今回採用するに至った理由などが中心。
使ってるディストリビューションによって細かい所は違う可能性がありますがその時はごめんなさい。私はUbuntuを使ってます。
まず1つ目の利点はWindows上のファイルに簡単にアクセスできることです。/mnt/c配下にCドライブがマウントされてるっぽいイメージでアクセスできます。(細かい仕組みはまだ理解できてません)
2つ目の利点はWindowsにインストールされたVScodeからWSL上のファイルを編集できる所です。Linuxを使うならVimなりEmacsなりを使えよという指摘ももっともなのですが、慣れ親しんだエディタを使えることは素晴らしいので…
ちなみにこの機能は比較的最近のものです。(私がWSLを入れた頃はまだなかった)
詳しくは「Remote WSL」とかでググってみてください。

では本題に戻って。
まずは実際に書いたbashファイルを見てもらうことにします。

#!/usr/bin/bash
g++ -O2 -o  submit.out "Windows上の提出用プログラムのディレクトリ"
g++ -o naive.out naive.cpp
g++ -o testcase.out testcase.cpp
./testcase.out > in.txt
time cat in.txt | ./submit.out > out.txt
cat in.txt | ./naive.out > out_naive.txt
./judge.out

パイプとかリダイレクトとかの存在は知っていましたが、使うのは初めてだったので面白かったです。
リダイレクトを使用することで標準出力をファイルに書き込むことが出来るので、それを使ってさっきまでの問題を解決することができました。
あとおまけで実行時間も測っています。(環境が全然違うので参考程度ですが)
こういう小技に凝るのが趣味なので、本当にやるべきことが一向に進まないのが最近の悩みです。
では今日はこの辺で