Emacs で AtCoder のサンプルケースを一括テスト

どうも、退職アドカレを書こうと思ってエントリーだけして逃げた者です。

# 必要なもの

* Emacs
* 好きな言語の環境 (私の場合 Rust)
* [emacs-quickrun](https://github.com/syohex/emacs-quickrun)

# emacs-quickrun の標準入力

前述の emacs-quickrun では `ソースファイル名(含拡張子).qrinput` としてファイルを定義すると、そのファイルの内容が実行時に標準入力として渡されるようになります。しかし、この仕様では標準入力を1つまでしか用意できません。
そこで、 AtCoder が用意する2~3のテストケースを一括でテストする方法を考えました。

# テストスクリプト

詳しい実装はあまりにも自明なので説明しません。
カレントディレクトリから `input-*.txt` と `output-*.txt` を探してそれぞれにテストを実行します。

```bash
#!/usr/bin/env bash

set -u
IFS=$'\n\t'

# run-tests.sh --- Run AtCoder test cases
# author: Seong Yong-ju <sei40kr@gmail.com>

red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
bold="$(tput bold)"
reset="$(tput sgr0)"

cmd="$@"
buffile="$(mktemp)"
errfile="$(mktemp)"

shopt -s nullglob

for infile in input-*.txt; do
	outfile="output-${infile:6}"
	if [[ ! -f $outfile ]]; then
		continue
	fi

  echo -n "${infile} - "

	eval "${cmd[@]}" <"$infile" 1>"$buffile" 2>"$errfile"
	if [[ "$?" != 0 ]]; then
		cat <<EOM
${bold}${red}err${reset}

error output:
$(cat "$errfile")

EOM
		break
	fi

	if diff -q "$buffile" "$outfile" >/dev/null; then
		echo "${bold}${green}ok${reset}"
	else
		cat <<EOM
${bold}${yellow}ng${reset}

expected:
$(cat "$outfile")

actual:
$(cat "$buffile")

EOM
	fi
done

rm -f "$buffile" "$errfile"
```

本当はTLEのチェックもしたかったのですが、まあいいや。

# テストケースを用意する

こんな感じで。

```:input-1.txt
5 2
```

```:output-1.txt
1 7
```

```:input-2.txt
1 10 0
```

ちなみにこれを自動化するために、 Python でツールを書きました。
https://github.com/sei40kr/atcoder-scraper

```shell-session
$ atcoder-scraper https://atcoder.jp/contests/abc090/tasks/arc091_b
```

こんな感じです。ちなみにこれの完全上位互換のツールが存在することを作った後に知りました。
https://github.com/kyuridenamida/atcoder-tools

# Spacemacs の quickrun レイヤー

今言っても仕方ありませんが、以前は Spacemacs の quickrun 用のレイヤーを作っていました。
`ソースコードのファイル名(含拡張子).qrinput*`　形式のファイルが複数ある場合に helm で選べるようにするものです。
https://github.com/sei40kr/spacemacs-quickrun

# .dir-locals.el の設定

ソースコードを置いているプロジェクトの先祖ディレクトリのどこかに `.dir-locals.el` を用意して、 quickrun の実行コマンドを書き換えます。
プロジェクトのルートを特定するために `eval` を使ってます。適宜変更してください。
ちなみに `1.15.1` は　AtCoder　の　Rust のバージョンです。　`rustc --emit=mir` がないので同じバージョンでLinterを動かせない...。

```emacs-lisp
;;; .dir-locals.el ---                               -*- lexical-binding: t; -*-

;; Copyright (C) 2018 Seong Yong-ju

;; Author: Seong Yong-ju <sei40kr@gmail.com>

((rust-mode . ((racer-rust-src-path . "~/.rustup/toolchains/1.15.1-x86_64-apple-darwin/lib/rustlib/src/rust/src")
                 (eval . (setq quickrun-option-cmd-alist
                           (list
                             '(:command . "rustup")
                             (cons :exec
                               (list
                                 "%c run --install 1.15.1 rustc %o -o %e %s"
                                 (concat (projectile-project-root) "utils/run-tests.sh env RUST_BACKTRACE=1 %e %a")))
                             '(:compile-only . "%c run --install 1.15.1 rustc %o -o %e %s")
                             '(:remove . ("%e"))
                             '(:description . "Compile rust and execute via rustup")))))))
```

`[先程のスクリプト] [実行コマンド...]` といった感じですね。テストケースの数に依らず、コンパイルは1回です。

# 結果

![スクリーンショット](https://qiita-image-store.s3.amazonaws.com/0/102243/7d3d6f5c-ec46-09a5-5e84-d82002518a9b.png)