4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtCoderの問題で自作ライブラリを自動テストする方法

Last updated at Posted at 2024-09-20

はじめに

みなさん、競技プログラミングは楽しんでますでしょうか。

この記事では、AtCoder のテストケースを Dropbox から自動的にダウンロードし、競技プログラミング用のライブラリを検証するための GitHub Actions のワークフローを構築する手順を紹介します。

取り扱うこと

  • AtCoder の問題で verify を実行する方法

取り扱わないこと

  • online-judge-tools を使ったライブラリの実装
  • Aizu Online Judge や Yosupo Judge の問題で自動テストを実行する方法

1. Dropbox アカウントとアプリの作成

まず手順に従って Dropbox アカウントを作成します。

その後、Dropbox Developers の Create App からアプリを作成します。

graph7.png

graph8.png

2. Dropbox API を使用した OAuth2 認証フロー

App key と App secret の取得

アプリを作成したらまずは Settings タブから App keyApp secret を保管します。
この値は他者に知られないように保管してください。

graph10.png

スコープの変更

次に Permissions タブからスコープを変更します。
次の項目にチェックを入れ、変更を保存します。

graph9.png

OAuth2 認証フロー

そしてアクセスコードを取得するために次のリンクにアクセスしてください。$APP_KEY は先ほど保管した App Key に置き換えてください。認証を許可して次の画面に表示されるアクセスコードを保管します。

graph11.png

graph12.png

3. リフレッシュトークンの取得と使用

リフレッシュトークンの取得

リフレッシュトークンを取得するために、今まで保管した

  • $APP_KEY (App key)
  • $APP_SECRET (App secret)
  • $ACCESS_CODE (アクセスコード)

を使用して次の curl コマンドを実行します。

$ curl https://api.dropbox.com/oauth2/token \
    -d code=$ACCESS_CODE \
    -d grant_type=authorization_code \
    -u $APP_KEY:$APP_SECRET
{
    "access_token": ...,
    "token_type": "bearer",
    "expires_in": 14400,
    "refresh_token": ...,
    "scope": "account_info.read files.metadata.read sharing.read",
    "uid": ...,
    "account_id": ...
}

これでリフレッシュトークンが取得できます。
このリフレッシュトークンの役割は、アクセストークンの有効期限が切れた後に、新しいアクセストークンを取得するために使われるものです。Dropbox API のアクセストークンの有効期限は14400秒と短いため、継続的に API を使うためにはリフレッシュトークンを経由する必要があります。

リフレッシュトークンの使用

次のコマンドでリフレッシュトークンを使用してアクセストークンを取得することができます。適宜 $REFRESH_TOKEN を先ほど取得したリフレッシュトークンに置き換えてください。

$ curl https://api.dropbox.com/oauth2/token \
    -d grant_type=refresh_token \
    -d refresh_token=$REFRESH_TOKEN \
    -u $APP_KEY:$APP_SECRET
{
    "access_token": ...,
    "token_type": "bearer",
    "expires_in": 14400
}

この処理をこれから GitHub Actions のワークフローに組み込んでいきます。

4. GitHub Actions ワークフローの作成

GitHub Secrets の設定

まずは、GitHub の Secrets に先ほど取得した Dropbox のトークンを登録します。

GitHub のリポジトリ設定ページに移動し、

graph1.png

左メニューから「Secrets and variables」の「Actions」を選択します。

graph2.png

「New repository secret」ボタンをクリックし、

graph3.png

先ほど取得した Dropbox の App keyApp secret、リフレッシュトークンを登録します(名前は DROPBOX_APP_KEY など)

graph4.png

次のように登録されていれば設定完了です。

graph5.png

ワークフロー設定ファイルの設定

次にワークフローの設定ファイルを編集します。

.github/workflows/verify.yml
name: verify

on: push

jobs:
  verify:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: Set up Python
      uses: actions/setup-python@v1

    - name: Install dependencies
      run: pip3 install -U online-judge-verify-helper

+   - name: Refresh Dropbox Token
+     run: |
+      response=$(curl -X POST https://api.dropbox.com/oauth2/token \
+         -d grant_type=refresh_token \
+         -d refresh_token=${{ secrets.DROPBOX_REFRESH_TOKEN }} \
+         -u "${{ secrets.DROPBOX_APP_KEY }}:${{ secrets.DROPBOX_APP_SECRET }}" \
+         -H "Content-Type: application/x-www-form-urlencoded")
+       ACCESS_TOKEN=$(echo $response | jq -r '.access_token')
+       echo "::add-mask::$ACCESS_TOKEN"
+       echo "DROPBOX_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV

    - name: Run tests
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        YUKICODER_TOKEN: ${{ secrets.YUKICODER_TOKEN }}
+       DROPBOX_TOKEN: ${{ env.DROPBOX_TOKEN }}
        GH_PAT: ${{ secrets.GH_PAT }}
      run: oj-verify all

先ほど行ったアクセストークンを取得する操作を追加して、GitHub の環境変数にアクセストークンを追加しています。

各操作の説明をします。

.github/workflows/verify.yml
      run: |
        response=$(curl -X POST https://api.dropbox.com/oauth2/token \
          -d grant_type=refresh_token \
          -d refresh_token=${{ secrets.DROPBOX_REFRESH_TOKEN }} \
          -u "${{ secrets.DROPBOX_APP_KEY }}:${{ secrets.DROPBOX_APP_SECRET }}" \
          -H "Content-Type: application/x-www-form-urlencoded")
        ACCESS_TOKEN=$(echo $response | jq -r '.access_token')

curlコマンドでアクセストークンを含んだ json 形式のデータを取得、それに対してjqコマンドでアクセストークンだけ抜き出し、$ACCESS_TOKEN という変数に代入します。

.github/workflows/verify.yml
        echo "::add-mask::$ACCESS_TOKEN"

ここで $ACCESS_TOKEN::add-mask:: をすることにより、この文字列がログに流れることを防ぎます。

.github/workflows/verify.yml
        echo "DROPBOX_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV

$GITHUB_ENV$ACCESS_TOKEN を追加します。これでこの後のステップで env.DROPBOX_TOKEN を使うことが可能になります。

5. 実際に試してみる

ここまで作ったワークフローを実際に使って自動テストを試してみます。
今回はトポロジカルソートのライブラリを検証するため、ABC223のD問題を扱います。

まずはライブラリです。

graph/topological_sort.hpp
#pragma once

#include <vector>
#include <queue>
#include "graph_template.hpp"

/**
 * @brief Topological Sort (トポロジカルソート)
 */
template<class T> std::vector<int> topological_sort(Graph<T> &graph) {
    int n = (int)(graph.size());
    std::vector<int> res, indeg(n, 0);

    for (int i = 0; i < n; i++) {
        for (auto c: graph[i]) {
            indeg[c]++;
        }
    }

    std::priority_queue<int, std::vector<int>, std::greater<int>> que;
    for (int i = 0; i < n; i++) {
        if (indeg[i] == 0) que.push(i);
    }

    while (!que.empty()) {
        auto v = que.top(); que.pop();
        res.push_back(v);
        for (auto c: graph[v]) {
            indeg[c]--;
            assert(indeg[c] >= 0);
            if (indeg[c] == 0) que.push(c);
        }
    }

    return res;
}

次にテスト用コードです。一行目に #define PROBLEM "https://atcoder.jp/contests/abc223/tasks/abc223_d" と記述し、テストに使う問題を明示しておきます。

verify/graph/topological_sort_1.test.cpp
#define PROBLEM "https://atcoder.jp/contests/abc223/tasks/abc223_d"
#include <bits/stdc++.h>
#include "graph/graph_template.hpp"
#include "graph/topological_sort.hpp"
using namespace std;
typedef long long int ll;
#define rep(i, N) for(int i = 0; i < (int)N; i++)

int main() {
    ll N, M; cin >> N >> M;
    Graph<ll> graph(N);
    rep (i, M) {
        ll a, b; cin >> a >> b; a--; b--;
        graph[a].push_back(b);
    }

    auto V = topological_sort(graph);
    if ((int)(V.size()) != N) {
        cout << -1 << endl;
        return 0;
    }
    rep (i, N) {
        if (i == 0) cout << V[i] + 1;
        else cout << ' ' << V[i] + 1;
    }
    cout << endl;
    return 0;
}

このコードをリモートブランチにプッシュして、

graph6.png

問題なくテストできました!アクセストークンも --dropbox-token *** となっており、伏せられていることがわかります。

おわりに

この記事では、Dropbox API と GitHub Actions を活用し、AtCoder のテストケースを自動的にダウンロードして競技プログラミング用ライブラリをテストするワークフローの構築手順を紹介しました。これを導入することで、より幅広い問題でテストをすることができ、競技プログラミングのライブラリ整備がよりスムーズに進むようになると思います。

私自身もまだ始めたばかりですが、この自動化の一歩を踏み出すことで、より快適な自作ライブラリライフを一緒に送りましょう!

参考にさせていただいた記事

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?