refactor: do not require casex.in exists
This commit is contained in:
parent
d211d7a429
commit
45450b4451
|
@ -92,6 +92,7 @@ def convert(
|
|||
"""
|
||||
Convert given dir of JOJ3 toml config files to JOJ3 json config files
|
||||
"""
|
||||
app.pretty_exceptions_enable = False
|
||||
logger.info(f"Converting files in {root.absolute()}")
|
||||
for repo_toml_path in root.glob("**/repo.toml"):
|
||||
for task_toml_path in repo_toml_path.parent.glob("**/*.toml"):
|
||||
|
|
|
@ -43,14 +43,17 @@ class StreamOut(BaseModel):
|
|||
|
||||
|
||||
InputFile = Union[LocalFile, MemoryFile, PreparedFile, Symlink]
|
||||
Stdin = Union[InputFile, StreamIn]
|
||||
Stdout = Union[Collector, StreamOut]
|
||||
Stderr = Union[Collector, StreamOut]
|
||||
|
||||
|
||||
class Cmd(BaseModel):
|
||||
args: List[str] = []
|
||||
env: List[str] = [DEFAULT_PATH_ENV]
|
||||
stdin: Union[InputFile, StreamIn] = MemoryFile(content="")
|
||||
stdout: Union[Collector, StreamOut] = Collector(name="stdout")
|
||||
stderr: Union[Collector, StreamOut] = Collector(name="stderr")
|
||||
stdin: Stdin = MemoryFile(content="")
|
||||
stdout: Stdout = Collector(name="stdout")
|
||||
stderr: Stderr = Collector(name="stderr")
|
||||
cpu_limit: int = Field(DEFAULT_CPU_LIMIT, serialization_alias="cpuLimit")
|
||||
clock_limit: int = Field(
|
||||
DEFAULT_CLOCK_LIMIT_MULTIPLIER * DEFAULT_CPU_LIMIT,
|
||||
|
@ -77,9 +80,9 @@ class Cmd(BaseModel):
|
|||
class OptionalCmd(BaseModel):
|
||||
args: Optional[List[str]] = None
|
||||
env: Optional[List[str]] = None
|
||||
stdin: Optional[Union[InputFile, StreamIn]] = None
|
||||
stdout: Optional[Union[Collector, StreamOut]] = None
|
||||
stderr: Optional[Union[Collector, StreamOut]] = None
|
||||
stdin: Optional[Stdin] = None
|
||||
stdout: Optional[Stdout] = None
|
||||
stderr: Optional[Stderr] = None
|
||||
cpu_limit: Optional[int] = Field(None, serialization_alias="cpuLimit")
|
||||
clock_limit: Optional[int] = Field(None, serialization_alias="clockLimit")
|
||||
memory_limit: Optional[int] = Field(None, serialization_alias="memoryLimit")
|
||||
|
|
|
@ -130,8 +130,7 @@ class Parser(str, Enum):
|
|||
ELF = "elf"
|
||||
|
||||
|
||||
class Stage(BaseModel):
|
||||
name: str = "" # Stage name
|
||||
class Case(BaseModel):
|
||||
env: List[str] = []
|
||||
command: str = "" # Command to run
|
||||
files: StageFiles = StageFiles()
|
||||
|
@ -140,9 +139,16 @@ class Stage(BaseModel):
|
|||
copy_in_cwd: bool = Field(
|
||||
True, validation_alias=AliasChoices("copy-in-cwd", "copy_in_cwd")
|
||||
)
|
||||
score: int = 0
|
||||
parsers: List[Parser] = [] # list of parsers
|
||||
limit: Limit = Limit()
|
||||
score: int = 0
|
||||
diff: ParserDiff = ParserDiff()
|
||||
|
||||
|
||||
class Stage(Case):
|
||||
name: str = "" # stage name
|
||||
skip: List[str] = []
|
||||
|
||||
parsers: List[Parser] = [] # list of parsers
|
||||
dummy: ParserDummy = ParserDummy()
|
||||
result_status: ParserDummy = Field(
|
||||
ParserDummy(), validation_alias=AliasChoices("result-status", "result_status")
|
||||
|
@ -157,11 +163,8 @@ class Stage(BaseModel):
|
|||
validation_alias=AliasChoices("result-detail", "result_detail"),
|
||||
)
|
||||
file: ParserFile = ParserFile()
|
||||
skip: List[str] = []
|
||||
|
||||
# cases related
|
||||
cases: Dict[str, "Stage"] = {}
|
||||
diff: ParserDiff = ParserDiff()
|
||||
cases: Dict[str, Case] = {}
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import re
|
|||
import shlex
|
||||
from functools import partial
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Callable, Dict, List, Set, Tuple
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from joj3_config_generator.models import result, task
|
||||
from joj3_config_generator.models.common import Memory, Time
|
||||
|
@ -11,7 +11,6 @@ from joj3_config_generator.models.const import (
|
|||
DEFAULT_PATH_ENV,
|
||||
JOJ3_CONFIG_ROOT,
|
||||
)
|
||||
from joj3_config_generator.models.task import Parser as ParserEnum
|
||||
from joj3_config_generator.utils.logger import logger
|
||||
|
||||
|
||||
|
@ -53,18 +52,18 @@ def get_parser_handler_map(
|
|||
executor: result.Executor,
|
||||
task_root: Path,
|
||||
task_path: Path,
|
||||
) -> Dict[ParserEnum, Tuple[Callable[[Any, result.Parser], None], Any]]:
|
||||
) -> Dict[task.Parser, Tuple[Callable[[Any, result.Parser], None], Any]]:
|
||||
return {
|
||||
ParserEnum.ELF: (fix_keyword, task_stage.elf),
|
||||
ParserEnum.CLANG_TIDY: (fix_keyword, task_stage.clangtidy),
|
||||
ParserEnum.KEYWORD: (fix_keyword, task_stage.keyword),
|
||||
ParserEnum.CPPCHECK: (fix_keyword, task_stage.cppcheck),
|
||||
ParserEnum.CPPLINT: (fix_keyword, task_stage.cpplint),
|
||||
ParserEnum.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
|
||||
ParserEnum.DUMMY: (fix_dummy, task_stage.dummy),
|
||||
ParserEnum.RESULT_STATUS: (fix_dummy, task_stage.result_status),
|
||||
ParserEnum.FILE: (fix_file, task_stage.file),
|
||||
ParserEnum.DIFF: (
|
||||
task.Parser.ELF: (fix_keyword, task_stage.elf),
|
||||
task.Parser.CLANG_TIDY: (fix_keyword, task_stage.clangtidy),
|
||||
task.Parser.KEYWORD: (fix_keyword, task_stage.keyword),
|
||||
task.Parser.CPPCHECK: (fix_keyword, task_stage.cppcheck),
|
||||
task.Parser.CPPLINT: (fix_keyword, task_stage.cpplint),
|
||||
task.Parser.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
|
||||
task.Parser.DUMMY: (fix_dummy, task_stage.dummy),
|
||||
task.Parser.RESULT_STATUS: (fix_dummy, task_stage.result_status),
|
||||
task.Parser.FILE: (fix_file, task_stage.file),
|
||||
task.Parser.DIFF: (
|
||||
partial(
|
||||
fix_diff,
|
||||
task_stage=task_stage,
|
||||
|
@ -178,36 +177,31 @@ def fix_diff(
|
|||
task_path: Path,
|
||||
) -> None:
|
||||
base_dir = JOJ3_CONFIG_ROOT / task_path.parent
|
||||
# all intended testcases that is detected
|
||||
testcases = get_testcases(task_root, task_path)
|
||||
# all testcases that is not specified in the toml config
|
||||
default_cases = sorted(
|
||||
testcases.difference(
|
||||
[
|
||||
casei
|
||||
for casei in testcases
|
||||
if any(casei.endswith(casej) for casej in task_stage.cases)
|
||||
]
|
||||
)
|
||||
)
|
||||
# those in toml config that is not skipped
|
||||
valid_cases = [
|
||||
(casej, task_stage.cases[casei])
|
||||
for casei in task_stage.cases
|
||||
for casej in testcases
|
||||
if (casei not in task_stage.skip and casej.endswith(casei))
|
||||
# cases not specified in the toml config (auto-detected)
|
||||
unspecified_cases = get_unspecified_cases(task_root, task_path, task_stage.cases)
|
||||
# cases specified in toml config but not skipped
|
||||
specified_cases = [
|
||||
(case, task_stage.cases[case])
|
||||
for case in task_stage.cases
|
||||
if case not in task_stage.skip
|
||||
]
|
||||
stage_cases = []
|
||||
parser_cases = []
|
||||
for case, case_stage in valid_cases:
|
||||
for case_name, case in specified_cases:
|
||||
stdin, stdout = get_stdin_stdout(task_root, task_path, case_name, case)
|
||||
if stdout is None:
|
||||
logger.warning(
|
||||
f"In file {task_root / task_path}, "
|
||||
f"testcase {case_name} has no corresponding .out file, "
|
||||
"skipped"
|
||||
)
|
||||
continue
|
||||
cmd = result.OptionalCmd(
|
||||
stdin=result.LocalFile(
|
||||
src=str(base_dir / (case_stage.in_ or f"{case}.in"))
|
||||
),
|
||||
args=shlex.split(case_stage.command) if case_stage.command else None,
|
||||
cpu_limit=case_stage.limit.cpu,
|
||||
clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case_stage.limit.cpu,
|
||||
memory_limit=case_stage.limit.mem,
|
||||
stdin=stdin,
|
||||
args=shlex.split(case.command) if case.command else None,
|
||||
cpu_limit=case.limit.cpu,
|
||||
clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case.limit.cpu,
|
||||
memory_limit=case.limit.mem,
|
||||
proc_limit=task_stage.limit.proc,
|
||||
)
|
||||
if cmd.args == executor.with_.default.args:
|
||||
|
@ -224,22 +218,22 @@ def fix_diff(
|
|||
parser_case = result.DiffCasesConfig(
|
||||
outputs=[
|
||||
result.DiffOutputConfig(
|
||||
score=case_stage.diff.output.score,
|
||||
score=case.diff.output.score,
|
||||
file_name="stdout",
|
||||
answer_path=str(base_dir / (case_stage.out_ or f"{case}.out")),
|
||||
force_quit_on_diff=case_stage.diff.output.force_quit,
|
||||
always_hide=case_stage.diff.output.hide,
|
||||
compare_space=not case_stage.diff.output.ignore_spaces,
|
||||
max_diff_length=case_stage.diff.output.max_length,
|
||||
max_diff_lines=case_stage.diff.output.max_lines,
|
||||
hide_common_prefix=case_stage.diff.output.hide_common_prefix,
|
||||
answer_path=stdout,
|
||||
force_quit_on_diff=case.diff.output.force_quit,
|
||||
always_hide=case.diff.output.hide,
|
||||
compare_space=not case.diff.output.ignore_spaces,
|
||||
max_diff_length=case.diff.output.max_length,
|
||||
max_diff_lines=case.diff.output.max_lines,
|
||||
hide_common_prefix=case.diff.output.hide_common_prefix,
|
||||
)
|
||||
]
|
||||
)
|
||||
parser_cases.append(parser_case)
|
||||
for case in default_cases:
|
||||
for case_name in unspecified_cases:
|
||||
cmd = result.OptionalCmd(
|
||||
stdin=result.LocalFile(src=str(base_dir / f"{case}.in")),
|
||||
stdin=result.LocalFile(src=str(base_dir / f"{case_name}.in")),
|
||||
cpu_limit=None,
|
||||
clock_limit=None,
|
||||
memory_limit=None,
|
||||
|
@ -251,7 +245,7 @@ def fix_diff(
|
|||
result.DiffOutputConfig(
|
||||
score=task_stage.diff.default_score,
|
||||
file_name="stdout",
|
||||
answer_path=str(base_dir / f"{case}.out"),
|
||||
answer_path=str(base_dir / f"{case_name}.out"),
|
||||
)
|
||||
]
|
||||
)
|
||||
|
@ -260,9 +254,9 @@ def fix_diff(
|
|||
diff_parser.with_ = result.DiffConfig(name="diff", cases=parser_cases)
|
||||
|
||||
|
||||
def get_testcases(
|
||||
task_root: Path, task_path: Path
|
||||
) -> Set[str]: # basedir here should be task_conf.root / task_conf.path
|
||||
def get_unspecified_cases(
|
||||
task_root: Path, task_path: Path, cases: Dict[str, task.Case]
|
||||
) -> List[str]:
|
||||
testcases = set()
|
||||
for testcases_path in (task_root / task_path).parent.glob("**/*.in"):
|
||||
if not testcases_path.with_suffix(".out").exists():
|
||||
|
@ -279,4 +273,42 @@ def get_testcases(
|
|||
)
|
||||
).removesuffix(".in")
|
||||
)
|
||||
return testcases
|
||||
return sorted(
|
||||
testcases.difference(
|
||||
[
|
||||
casei
|
||||
for casei in testcases
|
||||
if any(casei.endswith(casej) for casej in cases)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_stdin_stdout(
|
||||
task_root: Path, task_path: Path, case_name: str, case: task.Case
|
||||
) -> Tuple[result.Stdin, Optional[str]]:
|
||||
case_stdout_name = case.out_ if case.out_ else f"{case_name}.out"
|
||||
stdin: result.Stdin = result.MemoryFile(content="")
|
||||
stdout = None
|
||||
for case_stdout_path in (task_root / task_path).parent.glob("**/*.out"):
|
||||
if case_stdout_path.name != case_stdout_name:
|
||||
continue
|
||||
stdout = str(JOJ3_CONFIG_ROOT / case_stdout_path.relative_to(task_root))
|
||||
case_stdin_path = case_stdout_path.with_suffix(".in")
|
||||
if case.in_:
|
||||
case_stdin_path = Path((task_root / task_path).parent / case.in_)
|
||||
if not case_stdin_path.exists():
|
||||
logger.warning(
|
||||
f"In file {task_root / task_path}, "
|
||||
f"testcase {case_stdout_path} has no .in file, "
|
||||
"use empty content as stdin"
|
||||
)
|
||||
else:
|
||||
stdin = result.LocalFile(
|
||||
src=str(
|
||||
JOJ3_CONFIG_ROOT
|
||||
/ PurePosixPath(case_stdin_path.relative_to(task_root))
|
||||
)
|
||||
)
|
||||
break
|
||||
return stdin, stdout
|
||||
|
|
Loading…
Reference in New Issue
Block a user