VSCodeでAtCoderのサンプルケースをサクッとテストする

[AtCoder](https://atcoder.jp/) に取り組む中で、サンプルケースのテストを自動化してくれる [online-judge-tools](https://github.com/online-judge-tools/oj/blob/master/docs/getting-started.ja.md) という便利なツールを知りました。VSCodeから手軽に online-judge-tools を使うための設定を行ったので、その内容を共有します。

![oj-test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496366/e795a526-5322-d069-f753-56381ad4f2ff.gif)

操作方法は次のとおりです。

- Ctrl + Shift + P のコマンドパレットから「テスト タスクの実行」を選択
- ウィンドウ上部に表示される問題のURLを入力ボックスに、
    - URLを入力してEnterキーを押すと、URLからテストケースをダウンロードし、テストを実行
    - URLを空のままEnterキーを押すと、直前にダウンロードしたテストケースでテストを実行


## 動作確認済みの環境

- Windows 10
- VSCode 1.53.2
- WSL2 (Ubuntu 20.04)

※ 非WSL環境でも、bashにパスが通っていれば動作すると思われます。

## 事前準備

やることは以下の2つです。

1. `online-judge-tools`のセットアップ
2. VSCodeのワークスペースにtaskを定義

### 1. `online-judge-tools`のセットアップ

基本は以下のコマンドでOK。[公式ドキュメント](https://github.com/online-judge-tools/oj/blob/master/docs/getting-started.ja.md)が分かりやすいです。

```bash
$ pip3 install --user online-judge-tools  # ojコマンドのインストール
$ oj login https://atcoder.jp/  # ログイン
```

### 2. VSCodeのtaskを定義

ワークスペースのタスク定義ファイル`.vscode/tasks.json`に以下のタスクを追加します。

- ビルドタスク
- テストケースのダウンロードタスク
- テスト実行タスク

<details><summary>👉**\_記載例の全体をはここをクリックして表示\_**</summary><div>

**注意💡: 以下の例では、`./test/`以下の \*.in ファイルおよび \*.out ファイルが自動で削除される設定になっています。**

```json:.vscode/tasks.json
{
    "tasks": [
        {
            "type": "shell",
            "label": "C/C++: g++ build active file",
            "command": "/usr/bin/g++",
            "args": [
                "-g",
                "${file}",
                "-std=c++17",
                "-o",
                "${fileDirname}/a.out"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "download test cases",
            "type": "process",
            "command": "bash",
            "args": [
                "-c",
                "test ${input:problemUrl} && rm -f test/*.in test/*.out && oj d ${input:problemUrl} || true",
            ],
            "problemMatcher": []
        },
        {
            "type": "shell",
            "label": "do oj test",
            "command": "oj",
            "args": ["t"],
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "problemMatcher": [],
            "dependsOn": ["C/C++: g++ build active file", "download test case"]
        }
    ],
    "inputs": [
        {
            "id": "problemUrl",
            "type": "promptString",
            "description": "URL of Problem",
        }
    ],
    "version": "2.0.0"
}
```

</div></details>

#### ビルドタスク

[Microsoft C/C++ extension](https://code.visualstudio.com/docs/languages/cpp)が自動で生成してくれたものをほぼそのまま使っています。

```jsonc:ビルドタスクの例
{
    "tasks": [
        // 省略...        {
            "type": "shell",
            "label": "C/C++: g++ build active file",
            "command": "/usr/bin/g++",
            "args": [
                "-g",
                "${file}",
                "-std=c++17",
                "-o",
                "a.out"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    // 省略...
    ]
}
```

#### テストケースのダウンロードタスク

bashの引数に色々渡していますが、やっていることは大きく次の3つです。

1. ユーザーが入力したURLが空でなければ、
2. `test`以下の *.in と *.out を削除して、
3. `oj d`コマンドでテストケースをダウンロードする

```jsonc:テストケースのダウンロードタスクの例
{
    "tasks": [
        // 省略...
        {
            "label": "download test cases",
            "type": "process",
            "command": "bash",
            "args": [
                "-c",
                "test ${input:problemUrl} && rm -f test/*.in test/*.out && oj d ${input:problemUrl} || true",
            ],
            "problemMatcher": []
        }
       // 省略...
    ],
    "inputs": [
        {
            "id": "problemUrl",
            "type": "promptString",
            "description": "URL of Problem",
        }
    ]
}
```

#### テスト実行タスク

基本的には`oj t`を実行するだけです。`dependsOn`にビルドタスクとテストケースのダウンロードタスクを設定しておくことで、本タスク実行前にこれらのタスクが実行されます。

```jsonc:テスト実行タスクの例
{
    "tasks": [
        // 省略...
        {
            "type": "shell",
            "label": "do oj test",
            "command": "oj",
            "args": ["t"],
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "problemMatcher": [],
            "dependsOn": ["C/C++: g++ build active file", "download test case"]
        }
        // 省略...
    ]
}
```

## その他

### C++以外の場合

`oj`コマンドに-cオプションを追加すればOKです。テスト前のビルドも不要になります。
以下はPyhtonの例です。

```diff_json:.vscode/tasks.json
        {
            "type": "shell",
            "label": "do oj test",
            "command": "oj",
-            "args": ["t"],
+            "args": ["t", "-c python3 ${file}"], 
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "problemMatcher": [],
-           "dependsOn": ["C/C++: g++ build active file", "download test case"]
+           "dependsOn": ["download test case"]
        }
```

### まとめ

コードのビルド、サンプルケースのダウンロード、テストの実行の3つのタスクについて、`.vscode/tasks.json`に定義して、VSCodeでテストをサクッと実行する方法を紹介しました。
`inputs`にユーザーからの入力を定義することで柔軟にタスクを定義できるので、ぜひ活用してみてください。