blob: 3f80c8c131eeda474a26c2b37cd5c442f7cb3dc4 [file] [edit]
############################################################################
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
############################################################################
"""Verify sacct selects jobs by job name."""
import os
import pytest
import atf
test_name = os.path.splitext(os.path.basename(__file__))[0]
name1 = f"{test_name}_job1"
name2 = f"{test_name}_job2"
@pytest.fixture(scope="module", autouse=True)
def setup():
atf.require_version(
(26, 11),
"bin/sacct",
reason="Ticket 25236: sacct -J/--job-name was added in 26.11",
)
atf.require_accounting()
atf.require_slurm_running()
@pytest.fixture(scope="module")
def jobs():
job1 = atf.submit_job_sbatch(f"--job-name={name1} --wrap 'true'", fatal=True)
job2 = atf.submit_job_sbatch(f"--job-name={name2} --wrap 'true'", fatal=True)
atf.wait_for_job_accounted(job1, fatal=True)
atf.wait_for_job_accounted(job2, fatal=True)
return job1, job2
@pytest.mark.parametrize("opt", ["-J", "--job-name", "--name"])
def test_job_name_filter(jobs, opt):
"""Verify sacct name filters report only the matching job"""
job1, job2 = jobs
job_ids = []
for _ in atf.timer():
result = atf.run_command(
f"sacct {opt} {name1} -X -P -n -o JobID --starttime=now-15minutes",
quiet=True,
)
assert result["exit_code"] == 0, f"sacct should accept {opt}"
job_ids = result["stdout"].split()
if str(job1) in job_ids:
break
assert str(job1) in job_ids, f"sacct {opt} {name1} should report job {job1}"
assert str(job2) not in job_ids, f"sacct {opt} {name1} should not report job {job2}"
@pytest.mark.parametrize("opt", ["-J", "--job-name", "--name"])
def test_job_name_list(jobs, opt):
"""Verify sacct name filters accept a comma separated name list"""
job1, job2 = jobs
job_ids = []
for _ in atf.timer():
result = atf.run_command(
f"sacct {opt} {name1},{name2} -X -P -n -o JobID"
" --starttime=now-15minutes",
quiet=True,
)
assert result["exit_code"] == 0, f"sacct should accept {opt}"
job_ids = result["stdout"].split()
if str(job1) in job_ids and str(job2) in job_ids:
break
assert str(job1) in job_ids, f"sacct {opt} {name1},{name2} should report job {job1}"
assert str(job2) in job_ids, f"sacct {opt} {name1},{name2} should report job {job2}"
@pytest.mark.parametrize("opt", ["-J", "--job-name", "--name"])
def test_job_name_list_partial_match(jobs, opt):
"""Verify sacct name filters report the subset whose names are used"""
job1, _ = jobs
job_ids = []
for _ in atf.timer():
result = atf.run_command(
f"sacct {opt} {name1},{test_name}_absent -X -P -n -o JobID"
" --starttime=now-15minutes",
quiet=True,
)
assert result["exit_code"] == 0, f"sacct should accept {opt}"
job_ids = result["stdout"].split()
if str(job1) in job_ids:
break
assert job_ids == [
str(job1)
], f"sacct {opt} should report only job {job1} when one name in the list is unused"
def test_job_id_option_prefix(jobs):
"""Verify sacct --job stays an exact match after --job-name was added
Adding --job-name makes --job a non-unique prefix of --jobs, so --job
resolves only through the explicit long_options entry that came with it.
"""
job1, _ = jobs
job_ids = atf.run_command_output(
f"sacct --job={job1} -X -P -n -o JobID --starttime=now-15minutes", fatal=True
).split()
assert job_ids == [str(job1)], f"sacct --job={job1} should select only job {job1}"