|
| 1 | +"""通常53〜60の別解と典型的な誤答を、実sandboxとjudgeで検査する。""" |
| 2 | + |
| 3 | +import asyncio |
| 4 | +import os |
| 5 | +from pathlib import Path |
| 6 | + |
| 7 | +import pytest |
| 8 | + |
| 9 | +from soj_backend.judge import JudgeVerdict, ShellgeiJudge |
| 10 | +from soj_runner.container_manager import ContainerManager |
| 11 | +from soj_runner.run_shellgei import ShellgeiDockerClient |
| 12 | +from soj_shared.models.execution import ExecutionStatus |
| 13 | +from soj_shared.problem_repository import build_problem_repository |
| 14 | + |
| 15 | + |
| 16 | +pytestmark = [ |
| 17 | + pytest.mark.docker, |
| 18 | + pytest.mark.skipif( |
| 19 | + os.getenv("SOJ_RUN_DOCKER_TESTS") != "1", |
| 20 | + reason="explicit isolated-host opt-in is required", |
| 21 | + ), |
| 22 | +] |
| 23 | +PROBLEMS = Path(__file__).resolve().parents[3] / "problems" |
| 24 | +CASES = [ |
| 25 | + ( |
| 26 | + 53, |
| 27 | + "awk '{last[$1]=$0} END{for(k in last)print last[k]}' input.txt | sort", |
| 28 | + "awk '!seen[$1]++' input.txt | sort", |
| 29 | + ), |
| 30 | + ( |
| 31 | + 54, |
| 32 | + "awk 'NR>1{print $1,$2-prev} {prev=$2}' input.txt", |
| 33 | + "awk 'NR>1{print $1,prev-$2} {prev=$2}' input.txt", |
| 34 | + ), |
| 35 | + ( |
| 36 | + 55, |
| 37 | + 'awk \'$0=="END"{inside=0} inside{print} $0=="BEGIN"{inside=1}\' input.txt', |
| 38 | + "sed -n '/BEGIN/,/END/{/BEGIN/d;/END/d;p;}' input.txt", |
| 39 | + ), |
| 40 | + ( |
| 41 | + 56, |
| 42 | + "awk 'BEGIN{FS=OFS=\"\\t\"} {print $1,$3}' input.txt", |
| 43 | + "awk 'BEGIN{OFS=\"\\t\"} {print $1,$3}' input.txt", |
| 44 | + ), |
| 45 | + ( |
| 46 | + 57, |
| 47 | + "awk -F. '{n[tolower($NF)]++} END{for(e in n)print e,n[e]}' input.txt | sort", |
| 48 | + "sed 's/.*\\.//' input.txt | sort | uniq -c | awk '{print $2,$1}'", |
| 49 | + ), |
| 50 | + ( |
| 51 | + 58, |
| 52 | + 'awk \'$1=="team"{people[$2,$3]=1} $1=="file"{files[$2,$3]=1} ' |
| 53 | + "END{for(p in people){split(p,a,SUBSEP);for(f in files){split(f,b,SUBSEP);" |
| 54 | + "if(a[1]==b[1])print a[1],a[2],b[2]}}}' input.txt | sort", |
| 55 | + "join <(sed -n 's/^team //p' input.txt | sort -u -k1,1) " |
| 56 | + "<(sed -n 's/^file //p' input.txt | sort) | sort", |
| 57 | + ), |
| 58 | + ( |
| 59 | + 59, |
| 60 | + "awk '{col=0;for(i=1;i<=length;i++){c=substr($0,i,1);" |
| 61 | + 'if(c=="\\t"){n=4-col%4;printf "%s",substr(">>>>",1,n);col+=n}' |
| 62 | + 'else{printf "%s",c;col++}}print ""}\' input.txt', |
| 63 | + "sed 's/\t/>>>>/g' input.txt", |
| 64 | + ), |
| 65 | + ( |
| 66 | + 60, |
| 67 | + "awk '{s[NR]=$2;e[NR]=$3} END{for(t=540;t<=660;t+=10){" |
| 68 | + 'k=sprintf("%02d:%02d",t/60,t%60);n=0;' |
| 69 | + "for(i=1;i<=NR;i++)if(s[i]<=k&&k<e[i])n++;print k,n}}' input.txt", |
| 70 | + "awk '{s[NR]=$2;e[NR]=$3} END{for(t=540;t<=660;t+=10){" |
| 71 | + 'k=sprintf("%02d:%02d",t/60,t%60);n=0;' |
| 72 | + "for(i=1;i<=NR;i++)if(s[i]<=k&&k<=e[i])n++;print k,n}}' input.txt", |
| 73 | + ), |
| 74 | +] |
| 75 | + |
| 76 | + |
| 77 | +def test_new_standard_solutions_and_mistakes() -> None: |
| 78 | + # 各問の参照解答・別解は正解、初出優先・空白分割・閉区間等の誤解は不正解になる。 |
| 79 | + repository = build_problem_repository( |
| 80 | + PROBLEMS / "v3", PROBLEMS / "image", PROBLEMS / "v3/manifest.json" |
| 81 | + ) |
| 82 | + manager = ContainerManager(pool_size=1) |
| 83 | + client = ShellgeiDockerClient( |
| 84 | + container_manager=manager, max_concurrent=1, problem_repository=repository |
| 85 | + ) |
| 86 | + judge = ShellgeiJudge(repository) |
| 87 | + try: |
| 88 | + manager.initialize_pool() |
| 89 | + for number, alternative, mistake in CASES: |
| 90 | + problem_id = f"STANDARD-{number:08}" |
| 91 | + reference = repository.require(problem_id).definition.reference_solution |
| 92 | + commands = [ |
| 93 | + (reference, True), |
| 94 | + (alternative, True), |
| 95 | + (mistake, False), |
| 96 | + ] |
| 97 | + if number == 59: |
| 98 | + # 元からあるスペースまで>に変換する誤答を区別する。 |
| 99 | + commands.append(("expand -t 4 input.txt | tr ' ' '>'", False)) |
| 100 | + if number == 60: |
| 101 | + # 記録に現れる時刻だけの出力と、11:00を落とす出力も区別する。 |
| 102 | + commands.extend( |
| 103 | + [ |
| 104 | + ( |
| 105 | + "awk '{print $2,1;print $3,-1}' input.txt | sort -k1,1 | " |
| 106 | + "awk 'NR>1&&$1!=t{print t,n} {t=$1;n+=$2} END{print t,n}'", |
| 107 | + False, |
| 108 | + ), |
| 109 | + (reference + " | head -n 12", False), |
| 110 | + ] |
| 111 | + ) |
| 112 | + for command, accepted in commands: |
| 113 | + execution = asyncio.run(client.run_with_timeout(command, problem_id)) |
| 114 | + context = (problem_id, command, execution) |
| 115 | + assert execution.status is ExecutionStatus.COMPLETED, context |
| 116 | + assert execution.exit_code == 0, context |
| 117 | + assert execution.stderr == "", context |
| 118 | + expected = ( |
| 119 | + JudgeVerdict.ACCEPTED if accepted else JudgeVerdict.WRONG_ANSWER |
| 120 | + ) |
| 121 | + assert judge.judge(execution, problem_id).verdict is expected, context |
| 122 | + finally: |
| 123 | + manager.shutdown_pool() |
| 124 | + client.close() |
0 commit comments