やりたいこと
LivebookにAtCoderの回答を書いて、テストボタンを押すと、問題にある入力例を入力して実行して、結果が正しいか判定する。
提出ボタンを押すと、回答が提出される
コマンドラインでは、この記事で紹介した方法でできてるんですが、Livebookでできたら、環境構築とかせず使えて面白そう。
要件
プログラムは変更せず、AtCoderに提出可能なものでテストを実行する。
必要な機能
- 入力例、出力例をAtCoderから取得 →なんとかなるだろう(未調査)
- stdioをfileにして実行 →課題
- 結果の比較 →問題ない ExUintで実装
:stdioをfileにして実行する方法
コマンドラインで実行すれば何とでもできますが、Livebookで実現する方法が課題です。
次のような方法をとってみました。
- MyIOを作って、IOの代わりにMyIOを使う。
- MyIOは環境変数の設定で、deviceをstdioかファイル化を切り替える
Livebookで作ってみた
入力をそのまま返すプログラムを作って、テストしてみました。
回答プログラムは、AtCoderのコードテストでも変更なしで動作しました。
回答プログラム
defmodule Main do
def input(), do: MyIO.read(:line) |> String.trim()
def main() do
s = input()
MyIO.puts(s)
end
end
defmodule MyIO do
def read(_device \\ nil, option) do
{file_in, _} = Application.get_env(:my_app, :file, {:stdio, :stdio})
IO.read(file_in, option)
end
def puts(_device \\ nil, item) do
{_, file_out} = Application.get_env(:my_app, :file, {:stdio, :stdio})
IO.puts(file_out, item)
end
end
テストプログラム
ExUnit.start(autorun: false)
defmodule MyTest do
use ExUnit.Case, async: true
test "test case 1" do
{:ok, file} = File.open("in_1.txt", [:write])
IO.write(file, "aaa\n")
File.close(file)
{:ok, file_in} = File.open("in_1.txt", [:read])
{:ok, file_out} = File.open("out_1.txt", [:write])
Application.put_env(:my_app, :file, {file_in, file_out})
Main.main()
File.close(file_out)
{:ok, file_out} = File.open("out_1.txt", [:read])
assert IO.read(file_out, :line) == "aaa\n"
end
end
ExUnit.run()
今後の課題
- MyIOを提出プログラムに追加しないといけない所がダサい。
- MyIOがread,putsだけしか実装されてないので限定的だが、多くの問題には対応できるので、実用性はなくはない。
- stdio自体をfileに置き換えれたら、任意のIO関数が使えて望ましいけど、不明。
- Moxなどモックを実現する方法でIOを置き換える方法はどうか?
いい方法のコメントも歓迎です