fix(diff): bugs on diff stdin and numerics (#16)
All checks were successful
build / build (push) Successful in 2m43s

fix problems in #15

Co-authored-by: Boming Zhang <bomingzh@sjtu.edu.cn>
Reviewed-on: #16
Reviewed-by: 张泊明518370910136 <bomingzh@sjtu.edu.cn>
Co-authored-by: jon-lee <jon-lee@sjtu.edu.cn>
Co-committed-by: jon-lee <jon-lee@sjtu.edu.cn>
This commit is contained in:
李衍志523370910113 2025-05-24 02:45:38 +08:00 committed by 张泊明518370910136
parent 6375a37069
commit b1ea7d9591
9 changed files with 71 additions and 59 deletions

View File

@ -6,6 +6,8 @@ DEFAULT_CPU_LIMIT = Time("1s")
DEFAULT_MEMORY_LIMIT = Memory("256m") DEFAULT_MEMORY_LIMIT = Memory("256m")
DEFAULT_FILE_LIMIT = Memory("32m") DEFAULT_FILE_LIMIT = Memory("32m")
DEFAULT_CASE_SCORE = 5 DEFAULT_CASE_SCORE = 5
DEFAULT_CLOCK_LIMIT_MULTIPLIER = 2
DEFAULT_PROC_LIMIT = 50
JOJ3_CONFIG_ROOT = Path("/home/tt/.config/joj") JOJ3_CONFIG_ROOT = Path("/home/tt/.config/joj")
TEAPOT_CONFIG_ROOT = Path("/home/tt/.config/teapot") TEAPOT_CONFIG_ROOT = Path("/home/tt/.config/teapot")

View File

@ -3,9 +3,11 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
from pydantic import BaseModel, ConfigDict, Field, field_validator from pydantic import BaseModel, ConfigDict, Field, field_validator
from joj3_config_generator.models.const import ( from joj3_config_generator.models.const import (
DEFAULT_CLOCK_LIMIT_MULTIPLIER,
DEFAULT_CPU_LIMIT, DEFAULT_CPU_LIMIT,
DEFAULT_FILE_LIMIT, DEFAULT_FILE_LIMIT,
DEFAULT_MEMORY_LIMIT, DEFAULT_MEMORY_LIMIT,
DEFAULT_PROC_LIMIT,
) )
@ -49,10 +51,13 @@ class Cmd(BaseModel):
stdout: Union[Collector, StreamOut] = Collector(name="stdout") stdout: Union[Collector, StreamOut] = Collector(name="stdout")
stderr: Union[Collector, StreamOut] = Collector(name="stderr") stderr: Union[Collector, StreamOut] = Collector(name="stderr")
cpu_limit: int = Field(DEFAULT_CPU_LIMIT, serialization_alias="cpuLimit") cpu_limit: int = Field(DEFAULT_CPU_LIMIT, serialization_alias="cpuLimit")
clock_limit: int = Field(2 * DEFAULT_CPU_LIMIT, serialization_alias="clockLimit") clock_limit: int = Field(
DEFAULT_CLOCK_LIMIT_MULTIPLIER * DEFAULT_CPU_LIMIT,
serialization_alias="clockLimit",
)
memory_limit: int = Field(DEFAULT_MEMORY_LIMIT, serialization_alias="memoryLimit") memory_limit: int = Field(DEFAULT_MEMORY_LIMIT, serialization_alias="memoryLimit")
stack_limit: int = Field(0, serialization_alias="stackLimit") stack_limit: int = Field(0, serialization_alias="stackLimit")
proc_limit: int = Field(50, serialization_alias="procLimit") proc_limit: int = Field(DEFAULT_PROC_LIMIT, serialization_alias="procLimit")
cpu_rate_limit: int = Field(0, serialization_alias="cpuRateLimit") cpu_rate_limit: int = Field(0, serialization_alias="cpuRateLimit")
cpu_set_limit: str = Field("", serialization_alias="cpuSetLimit") cpu_set_limit: str = Field("", serialization_alias="cpuSetLimit")
copy_in: Dict[str, InputFile] = Field({}, serialization_alias="copyIn") copy_in: Dict[str, InputFile] = Field({}, serialization_alias="copyIn")

View File

@ -7,6 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
from joj3_config_generator.models.common import Memory, Time from joj3_config_generator.models.common import Memory, Time
from joj3_config_generator.models.const import ( from joj3_config_generator.models.const import (
DEFAULT_CASE_SCORE,
DEFAULT_CPU_LIMIT, DEFAULT_CPU_LIMIT,
DEFAULT_FILE_LIMIT, DEFAULT_FILE_LIMIT,
DEFAULT_MEMORY_LIMIT, DEFAULT_MEMORY_LIMIT,
@ -51,6 +52,7 @@ class Outputs(BaseModel):
class ParserDiff(BaseModel): class ParserDiff(BaseModel):
output: Outputs = Outputs() output: Outputs = Outputs()
default_score: int = DEFAULT_CASE_SCORE
class Files(BaseModel): class Files(BaseModel):
@ -125,9 +127,14 @@ class Stage(BaseModel):
@classmethod @classmethod
def gather_cases(cls: Type["Stage"], values: Dict[str, Any]) -> Dict[str, Any]: def gather_cases(cls: Type["Stage"], values: Dict[str, Any]) -> Dict[str, Any]:
cases = {k: v for k, v in values.items() if k.startswith("case")} cases = {k: v for k, v in values.items() if k.startswith("case")}
for key in cases: limit = values.get("limit", {})
parsed_cases = {}
for key, case in cases.items():
case_with_limit = {**limit, **case.get("limit", {})}
case_for_parsing = {**case, "limit": case_with_limit}
parsed_cases[key] = case_for_parsing
values.pop(key) values.pop(key)
values["cases"] = {k: v for k, v in cases.items()} values["cases"] = parsed_cases
return values return values

View File

@ -4,10 +4,15 @@ from functools import partial
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, List, Set, Tuple from typing import Any, Callable, Dict, List, Set, Tuple
from joj3_config_generator.models import const, result, task from joj3_config_generator.models import result, task
from joj3_config_generator.models.common import Memory, Time from joj3_config_generator.models.common import Memory, Time
from joj3_config_generator.models.const import JOJ3_CONFIG_ROOT from joj3_config_generator.models.const import (
DEFAULT_CLOCK_LIMIT_MULTIPLIER,
DEFAULT_PROC_LIMIT,
JOJ3_CONFIG_ROOT,
)
from joj3_config_generator.models.task import Parser as ParserEnum from joj3_config_generator.models.task import Parser as ParserEnum
from joj3_config_generator.utils.logger import logger
def get_conf_stage( def get_conf_stage(
@ -194,9 +199,9 @@ def fix_diff(
), ),
args=shlex.split(case_stage.command) if case_stage.command else None, args=shlex.split(case_stage.command) if case_stage.command else None,
cpu_limit=case_stage.limit.cpu, cpu_limit=case_stage.limit.cpu,
clock_limit=2 * case_stage.limit.cpu, clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case_stage.limit.cpu,
memory_limit=case_stage.limit.mem, memory_limit=case_stage.limit.mem,
proc_limit=50, proc_limit=DEFAULT_PROC_LIMIT,
) )
if cmd.args == executor.with_.default.args: if cmd.args == executor.with_.default.args:
cmd.args = None cmd.args = None
@ -224,13 +229,17 @@ def fix_diff(
parser_cases.append(parser_case) parser_cases.append(parser_case)
for case in default_cases: for case in default_cases:
cmd = result.OptionalCmd( cmd = result.OptionalCmd(
stdin=result.LocalFile(src=str(base_dir / f"{case}.in")) stdin=result.LocalFile(src=str(base_dir / f"{case}.in")),
cpu_limit=None,
clock_limit=None,
memory_limit=None,
proc_limit=None,
) )
stage_cases.append(cmd) stage_cases.append(cmd)
parser_case = result.DiffCasesConfig( parser_case = result.DiffCasesConfig(
outputs=[ outputs=[
result.DiffOutputConfig( result.DiffOutputConfig(
score=const.DEFAULT_CASE_SCORE, score=task_stage.diff.default_score,
file_name="stdout", file_name="stdout",
answer_path=str(base_dir / f"{case}.out"), answer_path=str(base_dir / f"{case}.out"),
) )
@ -246,6 +255,9 @@ def get_testcases(
) -> Set[str]: # basedir here should be task_conf.root / task_conf.path ) -> Set[str]: # basedir here should be task_conf.root / task_conf.path
testcases = set() testcases = set()
for testcases_path in (task_root / task_path).parent.glob("**/*.in"): for testcases_path in (task_root / task_path).parent.glob("**/*.in"):
if not testcases_path.with_suffix(".out").exists():
logger.warning(f"Testcase {testcases_path} has no corresponding .out file")
continue
testcases.add( testcases.add(
str( str(
testcases_path.relative_to((task_root / task_path).parent) testcases_path.relative_to((task_root / task_path).parent)

View File

View File

View File

View File

@ -29,17 +29,17 @@
}, },
"stdout": { "stdout": {
"name": "stdout", "name": "stdout",
"max": 33554432, "max": 10485760,
"pipe": true "pipe": true
}, },
"stderr": { "stderr": {
"name": "stderr", "name": "stderr",
"max": 33554432, "max": 10485760,
"pipe": true "pipe": true
}, },
"cpuLimit": 1000000000, "cpuLimit": 3000000000,
"clockLimit": 2000000000, "clockLimit": 6000000000,
"memoryLimit": 68157440, "memoryLimit": 10485760,
"stackLimit": 0, "stackLimit": 0,
"procLimit": 50, "procLimit": 50,
"cpuRateLimit": 0, "cpuRateLimit": 0,
@ -71,6 +71,8 @@
"stdin": { "stdin": {
"src": "/home/tt/.config/joj/diff/case0.in" "src": "/home/tt/.config/joj/diff/case0.in"
}, },
"cpuLimit": 1000000000,
"clockLimit": 2000000000,
"memoryLimit": 2097152 "memoryLimit": 2097152
}, },
{ {
@ -87,35 +89,26 @@
{ {
"stdin": { "stdin": {
"src": "/home/tt/.config/joj/diff/case9.in" "src": "/home/tt/.config/joj/diff/case9.in"
},
"memoryLimit": 268435456
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/task1/subtask1/case11.in"
},
"memoryLimit": 268435456
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/task1/subtask1/case10.in"
},
"memoryLimit": 268435456
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/task1/case5.in"
},
"memoryLimit": 268435456
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/case2.in"
} }
}, },
{ {
"stdin": { "stdin": {
"src": "/home/tt/.config/joj/diff/case3.in" "src": "/home/tt/.config/joj/diff/task1/subtask1/case11.in"
}
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/task1/subtask1/case10.in"
}
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/task1/case5.in"
}
},
{
"stdin": {
"src": "/home/tt/.config/joj/diff/case2.in"
} }
}, },
{ {
@ -222,7 +215,7 @@
{ {
"outputs": [ "outputs": [
{ {
"score": 5, "score": 100,
"fileName": "stdout", "fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case2.out", "answerPath": "/home/tt/.config/joj/diff/case2.out",
"forceQuitOnDiff": false, "forceQuitOnDiff": false,
@ -234,19 +227,7 @@
{ {
"outputs": [ "outputs": [
{ {
"score": 5, "score": 100,
"fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/case3.out",
"forceQuitOnDiff": false,
"alwaysHide": false,
"compareSpace": false
}
]
},
{
"outputs": [
{
"score": 5,
"fileName": "stdout", "fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task1/case4.out", "answerPath": "/home/tt/.config/joj/diff/task1/case4.out",
"forceQuitOnDiff": false, "forceQuitOnDiff": false,
@ -258,7 +239,7 @@
{ {
"outputs": [ "outputs": [
{ {
"score": 5, "score": 100,
"fileName": "stdout", "fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case6.out", "answerPath": "/home/tt/.config/joj/diff/task2/case6.out",
"forceQuitOnDiff": false, "forceQuitOnDiff": false,
@ -270,7 +251,7 @@
{ {
"outputs": [ "outputs": [
{ {
"score": 5, "score": 100,
"fileName": "stdout", "fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case7.out", "answerPath": "/home/tt/.config/joj/diff/task2/case7.out",
"forceQuitOnDiff": false, "forceQuitOnDiff": false,
@ -282,7 +263,7 @@
{ {
"outputs": [ "outputs": [
{ {
"score": 5, "score": 100,
"fileName": "stdout", "fileName": "stdout",
"answerPath": "/home/tt/.config/joj/diff/task2/case8.out", "answerPath": "/home/tt/.config/joj/diff/task2/case8.out",
"forceQuitOnDiff": false, "forceQuitOnDiff": false,

View File

@ -8,13 +8,17 @@ release.begin_time = 2024-12-29 23:59:59+08:00
name = "[joj] ex2-asan" name = "[joj] ex2-asan"
command="./h7/build/ex2-asan -a" command="./h7/build/ex2-asan -a"
files.import = [ "h7/build/ex2-asan" ] files.import = [ "h7/build/ex2-asan" ]
limit.mem = "65m" limit.cpu = "3s"
limit.mem = "10m"
limit.stdout = "10m"
limit.stderr = "10m"
parsers = [ "diff", "result-detail" ] parsers = [ "diff", "result-detail" ]
result-detail.exitstatus = true result-detail.exitstatus = true
result-detail.stderr = true result-detail.stderr = true
# will be removed as long as the name is fixed diff.default_score = 100
case0.diff.output.score = 5 case0.diff.output.score = 5
case0.limit.cpu = "1s" case0.limit.cpu = "1s"
case0.limit.mem = "2m" case0.limit.mem = "2m"
@ -29,6 +33,7 @@ case1.diff.output.ignore_spaces = true
case1.command = "./h7/build/ex2" case1.command = "./h7/build/ex2"
case9.diff.output.score = 1232131 case9.diff.output.score = 1232131
case9.limit.mem = "10m"
case11.diff.output.score = 92321 case11.diff.output.score = 92321