日曜日の夜のことです.
翌日午後の授業に間に合わせるため,「プログラムの正誤判定プログラム」を,書いていました.
判定対象はC,判定プログラムはRubyです.学生が提出したCのソースファイルに対し,まずコンパイルを行い,実行ファイルが生成できるか確認します.生成されたら,コマンドライン引数を指定して実行し,その出力が,用意していた正解と合致しているかを判定します.テストケースは1つの課題につき数個を用意していて,その合致の状況を,コンパイルをはじめ各実行時のログファイルに,書き込みます.実行ファイルが作られ,すべてのテストケースでOKであれば,動作に関しては満点とします(ソースコードを目視して,減点や,学生への問い合わせを行います).
コンパイルについては,成功でも失敗でも,瞬時に終わってくれます.
考慮する必要があるのは,テストケースの実行時のことです.無限ループを検知しないといけません.判定プログラムにタイムアウト処理を加えて,5秒で実行が終わらなかったら例外を発生させました.
なのですが,日曜日の夜11時すぎには,このタイムアウト処理が,うまく機能していないことに気づきました.正誤判定プログラムの実行中にC-cを押し,停止させないといけませんでした.
外側に「Timeout.timeout」,その内側に「Open3.popen3」を書くのが,まずいのでしょう.
検索していくつか試し,以下のコードを採用しました.
しかしこれを取り入れても,ログファイルを見ると,思ったようにいきません.何人かの学生の提出したプログラムに対し,コンパイルは成功しているのですが,どのテストケースでも,標準出力と標準エラー出力の内容(ログの記録)が,空文字列なのです.
原因が判明したのは,月曜深夜2時過ぎでした.空文字列で,合っていました.提出したプログラムが未完成で,何を入力に与えても,何も出力しないのでした.
いったん横になって,日が昇る前に起きて判定プログラムを少し修正して実行し,テストケースの成功・失敗を含め,状況をログファイルに保存しました.なお提出したそれぞれへのフィードバックを行わず,授業ページに,課題ごとに提出者数○名,コンパイル成功○名,テストケース1の成功○名,…と書いていきました.
出勤して,成績評価と別に,提出されたプログラムの集計を試みました.課題で利用が想定されるライブラリルーチン(Manpageのセクション3の関数)をいくつか挙げて,grep -lで逐次検索し,使用しているファイル名(そして学生数)を求めてみました.
当初は,mallocよりもallocを使用している学生が少なくて困りました.いやgrepの引数は単純文字列なんだけどな思いながら,オプションを調べ直すと,grep -Lを使っていました.これは,パターンに一致しないファイル名を出力します.grep -lに変更することで,期待する動作になりました.
grep -L printfのあと,学生のファイルを並べて実行すると,1つだけファイル名を出力しました.printfを使用しないプログラムってどんなのだろう,低水準出力関数のwriteでごり押しするのかと思いながら,ファイルを開くと,作りかけのコードでした.