From f68a33f88ef9183d101f8a048e28e093f60552d1 Mon Sep 17 00:00:00 2001 From: Troy Comi Date: Thu, 14 Sep 2023 10:29:13 -0400 Subject: [PATCH 1/3] fix: memory efficiency for multi-task jobs Fixed estimation of memory from sacct by multiplying by NTasks Improved test_reportseff by moving several string constants into conftest --- conftest.py | 88 +++++++ src/reportseff/job.py | 4 +- src/reportseff/output_renderer.py | 2 +- tests/test_job.py | 13 +- tests/test_output_renderer.py | 4 +- tests/test_reportseff.py | 424 +++++++++--------------------- 6 files changed, 227 insertions(+), 308 deletions(-) diff --git a/conftest.py b/conftest.py index 44f8a6a..0756750 100644 --- a/conftest.py +++ b/conftest.py @@ -30,6 +30,7 @@ def to_sacct_dict(sacct_line: str) -> dict: "State", "Timelimit", "TotalCPU", + "NTasks", ) return dict(zip(columns, sacct_line.split("|"))) @@ -316,3 +317,90 @@ def multinode_job(): "|36|12-14:16:39|6196869.batch|6196869.batch|33824748K|1|191846Mn|COMPLETED||451-06:00:24" ), ] + + +@pytest.fixture +def issue_41(): + """job run on multiple nodes, with multiple tasks.""" + return [ + to_sacct_dict( + "|8|00:00:53|131042|131042||1|16000M|COMPLETED|00:01:00|06:57.815|8" + ), + to_sacct_dict( + "|8|00:00:53|131042.batch|131042.batch|20264K|1||COMPLETED||00:00.034|8" + ), + to_sacct_dict( + "|8|00:00:53|131042.extern|131042.extern|1052K|1||COMPLETED||00:00.001|8" + ), + to_sacct_dict( + "|8|00:00:53|131042.0|131042.0|1947276K|1||COMPLETED||06:57.779|8" + ), + ] + + +@pytest.fixture +def console_jobs(): + """collection of sacct outputs for test_reportseff.""" + + # indexed on job id + return { + "25569410_notime": ( + "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^1^|^4000Mc^|^" + "COMPLETED^|^19:28:36\n" + "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^" + "1^|^1^|^4000Mc^|^COMPLETED^|^00:00:00\n" + "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" + "^|^1^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + ), + "24418435_notime": ( + "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1^|^1Gn^|^" + "COMPLETED^|^01:27:29\n" + "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^" + "1^|^1^|^1Gn^|^COMPLETED^|^01:27:29\n" + "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^" + "1^|^1^|^1Gn^|^COMPLETED^|^00:00:00\n" + ), + "24418435": ( + "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1^|^1Gn^|^" + "COMPLETED^|^03:00:00^|^01:27:29\n" + "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^" + "1^|^1^|^1Gn^|^COMPLETED^|^^|^01:27:29\n" + "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^" + "1^|^1^|^1Gn^|^COMPLETED^|^^|^00:00:00\n" + ), + "23000233": ( + "^|^16^|^00:00:00^|^23000233^|^23000233^|^^|^1^|^1^|^4000Mc^|^" + "CANCELLED by 129319^|^6-00:00:00^|^00:00:00\n" + ), + "24221219": ( + "^|^1^|^00:09:34^|^24220929_421^|^24221219^|^^|^1^|^1^|^16000Mn^|^" + "COMPLETED^|^09:28.052\n" + "^|^1^|^00:09:34^|^24220929_421.batch^|^24221219.batch" + "^|^5664932K^|^1^|^1^|^16000Mn^|^COMPLETED^|^09:28.051\n" + "^|^1^|^00:09:34^|^24220929_421.extern^|^24221219.extern" + "^|^1404K^|^1^|^1^|^16000Mn^|^COMPLETED^|^00:00:00\n" + ), + "24221220": ( + "^|^1^|^00:09:33^|^24220929_431^|^24221220^|^^|^1^|^1^|^16000Mn^|^" + "PENDING^|^09:27.460\n" + "^|^1^|^00:09:33^|^24220929_431.batch^|^24221220.batch" + "^|^5518572K^|^1^|^1^|^16000Mn^|^PENDING^|^09:27.459\n" + "^|^1^|^00:09:33^|^24220929_431.extern^|^24221220.extern" + "^|^1400K^|^1^|^1^|^16000Mn^|^PENDING^|^00:00:00\n" + ), + "23000381": ( + "^|^8^|^00:00:12^|^23000381^|^23000381^|^^|^1^|^1^|^4000Mc^|^FAILED^|^00:00:00\n" + "^|^8^|^00:00:12^|^23000381.batch^|^23000381.batch^|^^|^1^|^1^|^4000Mc^|^" + "FAILED^|^00:00:00\n" + "^|^8^|^00:00:12^|^23000381.extern^|^23000381.extern^|^1592K^|^1^|^1^|^4000Mc^|^" + "COMPLETED^|^00:00:00\n" + ), + "23000210": ( + "^|^8^|^00:00:00^|^23000210^|^23000210^|^^|^1^|^1^|^20000Mn^|^" + "FAILED^|^00:00.007\n" + "^|^8^|^00:00:00^|^23000210.batch^|^23000210.batch^|^1988K^|^1^|^1^|^20000Mn^|^" + "FAILED^|^00:00.006\n" + "^|^8^|^00:00:00^|^23000210.extern^|^23000210.extern^|^1556K^|^1^|^1^|^20000Mn^|^" + "COMPLETED^|^00:00:00\n" + ), + } diff --git a/src/reportseff/job.py b/src/reportseff/job.py index 5cba205..43750f8 100644 --- a/src/reportseff/job.py +++ b/src/reportseff/job.py @@ -60,7 +60,6 @@ def __init__(self, job: str, jobid: str, filename: Optional[str]) -> None: self.time: Optional[str] = "---" self.time_eff: Union[str, float] = "---" self.cpu: Optional[Union[str, float]] = "---" - self.mem: Union[str, float] = "---" self.state: Optional[str] = None self.mem_eff: Optional[float] = None self.gpu: Optional[float] = None @@ -116,7 +115,8 @@ def update(self, entry: Dict) -> None: if k not in self.other_entries or not self.other_entries[k]: self.other_entries[k] = value mem = parsemem(entry["MaxRSS"]) if "MaxRSS" in entry else 0 - self.stepmem = max(self.stepmem, mem) + tasks = int(entry.get("NTasks", 1)) + self.stepmem = max(self.stepmem, mem * tasks) if "TRESUsageOutAve" in entry: self.energy = max( diff --git a/src/reportseff/output_renderer.py b/src/reportseff/output_renderer.py index 161accd..a5c2da0 100644 --- a/src/reportseff/output_renderer.py +++ b/src/reportseff/output_renderer.py @@ -45,7 +45,7 @@ def __init__( # values derived from other values, list includes all dependent values self.derived: Dict[str, List] = { "CPUEff": ["TotalCPU", "AllocCPUS", "Elapsed"], - "MemEff": ["REQMEM", "NNodes", "AllocCPUS", "MaxRSS"], + "MemEff": ["REQMEM", "NNodes", "AllocCPUS", "MaxRSS", "NTasks"], "TimeEff": ["Elapsed", "Timelimit"], "GPU": [], "GPUMem": [], diff --git a/tests/test_job.py b/tests/test_job.py index bb09c5e..45057b9 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -40,7 +40,6 @@ def test_job_init(job): assert job.totalmem is None assert job.time == "---" assert job.cpu == "---" - assert job.mem == "---" assert job.state is None @@ -250,7 +249,7 @@ def test_update_part_job(): "Elapsed": "00:10:00", "MaxRSS": "495644K", "NNodes": "1", - "NTasks": "", + "NTasks": "1", } ) assert job.state is None @@ -765,3 +764,13 @@ def test_multinode_job(multinode_job): job.update(line) assert job.cpu == 5.0 + + +def test_multinode_job_issue_41(issue_41): + """Testing issue37 which is not actually a bug efficiency is truly 5%.""" + job = job_module.Job("131042", "131042", None) + for line in issue_41: + job.update(line) + + assert job.cpu == 98.3 + assert job.get_entry("MemEff") == 95.1 diff --git a/tests/test_output_renderer.py b/tests/test_output_renderer.py index 763fe65..09896a6 100644 --- a/tests/test_output_renderer.py +++ b/tests/test_output_renderer.py @@ -175,7 +175,7 @@ def test_renderer_init(renderer): assert sorted(renderer.query_columns) == sorted( ( "JobID JobIDRaw State Elapsed TotalCPU " - "AllocCPUS REQMEM NNodes MaxRSS AdminComment" + "AllocCPUS REQMEM NNodes NTasks MaxRSS AdminComment" ).split() ) @@ -277,7 +277,7 @@ def test_renderer_correct_columns(renderer): ( "JobID TotalCPU Elapsed REQMEM" " JobIDRaw State AdminComment" - " NNodes AllocCPUS MaxRSS Timelimit" + " NNodes NTasks AllocCPUS MaxRSS Timelimit" ).split() ) diff --git a/tests/test_reportseff.py b/tests/test_reportseff.py index a3a44d8..50a1f15 100644 --- a/tests/test_reportseff.py +++ b/tests/test_reportseff.py @@ -30,20 +30,13 @@ def mock_partition_timelimits(self): ) -def test_directory_input(mocker, mock_inquirer): +def test_directory_input(mocker, mock_inquirer, console_jobs): """Able to get jobs from directory calls.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^03:00:00^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["24418435"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) def set_jobs(self, directory): @@ -68,20 +61,13 @@ def set_jobs(self, directory): ] -def test_directory_input_exception(mocker, mock_inquirer): +def test_directory_input_exception(mocker, mock_inquirer, console_jobs): """Catch exceptions in setting jobs from directory.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "24418435^|^24418435^|^COMPLETED^|^1^|^" - "01:27:29^|^01:27:42^|^03:00:00^|^1Gn^|^^|^1^|^\n" - "24418435.batch^|^24418435.batch^|^COMPLETED^|^1^|^" - "01:27:29^|^01:27:42^|^^|^1Gn^|^499092K^|^1^|^1\n" - "24418435.extern^|^24418435.extern^|^COMPLETED^|^1^|^" - "00:00:00^|^01:27:42^|^^|^1Gn^|^1376K^|^1^|^1\n" - ) + sub_result.stdout = console_jobs["24418435"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) def set_jobs(self, directory): @@ -94,16 +80,13 @@ def set_jobs(self, directory): assert "Testing EXCEPTION" in result.output -def test_debug_option(mocker, mock_inquirer): +def test_debug_option(mocker, mock_inquirer, console_jobs): """Setting debug prints subprocess result.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^16^|^00:00:00^|^23000233^|^23000233^|^^|^1^|^4000Mc^|^" - "CANCELLED by 129319^|^6-00:00:00^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000233"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -113,10 +96,7 @@ def test_debug_option(mocker, mock_inquirer): assert result.exit_code == 0 # remove header output = result.output.split("\n") - assert output[0] == ( - "^|^16^|^00:00:00^|^23000233^|^23000233^|^^|^1^|^4000Mc^|^" - "CANCELLED by 129319^|^6-00:00:00^|^00:00:00" - ) + assert output[0] == console_jobs["23000233"].strip("\n") assert output[3].split() == [ "23000233", "CANCELLED", @@ -127,16 +107,13 @@ def test_debug_option(mocker, mock_inquirer): ] -def test_process_failure(mocker, mock_inquirer): +def test_process_failure(mocker, mock_inquirer, console_jobs): """Catch exceptions in process_entry by printing the offending entry.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^16^|^00:00:00^|^23000233^|^23000233^|^^|^1^|^4000Mc^|^" - "CANCELLED by 129319^|^6-00:00:00^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000233"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) mocker.patch.object( JobCollection, "process_entry", side_effect=Exception("TESTING") @@ -153,21 +130,18 @@ def test_process_failure(mocker, mock_inquirer): "{'AdminComment': '', 'AllocCPUS': '16', " "'Elapsed': '00:00:00', 'JobID': '23000233', " "'JobIDRaw': '23000233', 'MaxRSS': '', 'NNodes': '1', " - "'REQMEM': '4000Mc', 'State': 'CANCELLED by 129319', " + "'NTasks': '1', 'REQMEM': '4000Mc', 'State': 'CANCELLED by 129319', " "'TotalCPU': '6-00:00:00'}" ) -def test_short_output(mocker, mock_inquirer): +def test_short_output(mocker, mock_inquirer, console_jobs): """Outputs with 20 or fewer entries are directly printed.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^23000233^|^23000233^|^CANCELLED by 129319^|^16^|^" - "00:00:00^|^00:00:00^|^6-00:00:00^|^4000Mc^|^^|^1^|^\n" - ) + sub_result.stdout = console_jobs["23000233"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) mocker.patch("reportseff.console.len", return_value=20) mocker.patch.object(OutputRenderer, "format_jobs", return_value="output") @@ -179,16 +153,13 @@ def test_short_output(mocker, mock_inquirer): mock_click.assert_called_once_with("output", color=None) -def test_long_output(mocker, mock_inquirer): +def test_long_output(mocker, mock_inquirer, console_jobs): """Outputs with more than 20 entries are echoed via pager.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^16^|^00:00:00^|^23000233^|^23000233" - "^|^^|^1^|^4000Mc^|^CANCELLED by 129319^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000233"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) mocker.patch("reportseff.console.len", return_value=21) mocker.patch.object(OutputRenderer, "format_jobs", return_value="output") @@ -199,20 +170,13 @@ def test_long_output(mocker, mock_inquirer): mock_click.assert_called_once_with("output", color=None) -def test_simple_job(mocker, mock_inquirer): +def test_simple_job(mocker, mock_inquirer, console_jobs): """Can get efficiency from a single job.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -225,24 +189,14 @@ def test_simple_job(mocker, mock_inquirer): assert output[0].split() == ["24418435", "COMPLETED", "01:27:42", "99.8%", "47.6%"] -def test_simple_user(mocker, mock_inquirer): +def test_simple_user(mocker, mock_inquirer, console_jobs): """Can limit outputs by user.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + console_jobs["24418435_notime"] + console_jobs["25569410_notime"] ) mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( @@ -257,24 +211,14 @@ def test_simple_user(mocker, mock_inquirer): assert output[1].split() == ["25569410", "COMPLETED", "21:14:48", "91.7%", "1.5%"] -def test_simple_partition(mocker, mock_inquirer): +def test_simple_partition(mocker, mock_inquirer, console_jobs): """Can limit outputs by partition.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0" - "^|^62328K^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + console_jobs["24418435_notime"] + console_jobs["25569410_notime"] ) mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( @@ -310,24 +254,14 @@ def test_format_add(mocker, mock_inquirer): ) -def test_since(mocker, mock_inquirer): +def test_since(mocker, mock_inquirer, console_jobs): """Can limit outputs by time since argument.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + console_jobs["24418435_notime"] + console_jobs["25569410_notime"] ) mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( @@ -345,24 +279,14 @@ def test_since(mocker, mock_inquirer): assert output[1].split() == ["25569410", "COMPLETED", "21:14:48", "91.7%", "1.5%"] -def test_since_all_users(mocker, mock_inquirer): +def test_since_all_users(mocker, mock_inquirer, console_jobs): """Can limit outputs by time since argument.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + console_jobs["24418435_notime"] + console_jobs["25569410_notime"] ) mock_sub = mocker.patch( "reportseff.db_inquirer.subprocess.run", return_value=sub_result @@ -385,7 +309,7 @@ def test_since_all_users(mocker, mock_inquirer): args=( "sacct -P -n --delimiter=^|^ " "--format=AdminComment,AllocCPUS,Elapsed,JobID,JobIDRaw," - "MaxRSS,NNodes,REQMEM,State,TotalCPU " + "MaxRSS,NNodes,NTasks,REQMEM,State,TotalCPU " "--allusers " # all users is added since no jobs/files were specified "--starttime=200406" ).split(), @@ -397,24 +321,14 @@ def test_since_all_users(mocker, mock_inquirer): ) -def test_since_all_users_partition(mocker, mock_inquirer): +def test_since_all_users_partition(mocker, mock_inquirer, console_jobs): """Can limit outputs by time since and partition argument.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^COMPLETED^|^19:28:36\n" + console_jobs["24418435_notime"] + console_jobs["25569410_notime"] ) mock_sub = mocker.patch( "reportseff.db_inquirer.subprocess.run", return_value=sub_result @@ -437,7 +351,7 @@ def test_since_all_users_partition(mocker, mock_inquirer): args=( "sacct -P -n --delimiter=^|^ " "--format=AdminComment,AllocCPUS,Elapsed,JobID,JobIDRaw," - "MaxRSS,NNodes,REQMEM,State,TotalCPU " + "MaxRSS,NNodes,NTasks,REQMEM,State,TotalCPU " "--allusers " # all users is added since no jobs/files were specified "--starttime=200406 " "--partition=partition " @@ -450,25 +364,15 @@ def test_since_all_users_partition(mocker, mock_inquirer): ) -def test_parsable(mocker, mock_inquirer): +def test_parsable(mocker, mock_inquirer, console_jobs): """Can display output as parsable format.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "RUNNING^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] + console_jobs[ + "25569410_notime" + ].replace("COMPLETED", "RUNNING") mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -483,29 +387,18 @@ def test_parsable(mocker, mock_inquirer): output = result.output.split("\n")[1:] # no color/bold codes and ^|^ delimited assert output[0].split("|") == ["24418435", "COMPLETED", "01:27:42", "99.8", "47.6"] - # other is suppressed by state filter assert output[1].split("|") == ["25569410", "RUNNING", "21:14:48", "---", "---"] -def test_simple_state(mocker, mock_inquirer): +def test_simple_state(mocker, mock_inquirer, console_jobs): """Can limit outputs by filtering state.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "RUNNING^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] + console_jobs[ + "25569410_notime" + ].replace("COMPLETED", "RUNNING") mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -523,25 +416,15 @@ def test_simple_state(mocker, mock_inquirer): assert output[1].split() == [] -def test_simple_not_state(mocker, mock_inquirer): +def test_simple_not_state(mocker, mock_inquirer, console_jobs): """Can limit outputs by removing state.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "RUNNING^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] + console_jobs[ + "25569410_notime" + ].replace("COMPLETED", "RUNNING") mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -559,25 +442,15 @@ def test_simple_not_state(mocker, mock_inquirer): assert output[1].split() == [] -def test_invalid_not_state(mocker, mock_inquirer): +def test_invalid_not_state(mocker, mock_inquirer, console_jobs): """When not state isn't found, return all jobs.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern^|^1548K^|^1^|^4000Mc^|^" - "RUNNING^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] + console_jobs[ + "25569410_notime" + ].replace("COMPLETED", "RUNNING") mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -598,25 +471,15 @@ def test_invalid_not_state(mocker, mock_inquirer): assert output[5].split() == [] -def test_no_state(mocker, mock_inquirer): +def test_no_state(mocker, mock_inquirer, console_jobs): """Unknown states produce empty output.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^01:27:42^|^24418435^|^24418435^|^^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.batch^|^24418435.batch^|^499092K^|^1^|^1Gn^|^" - "COMPLETED^|^01:27:29\n" - "^|^1^|^01:27:42^|^24418435.extern^|^24418435.extern^|^1376K^|^1^|^1Gn^|^" - "COMPLETED^|^00:00:00\n" - "^|^1^|^21:14:48^|^25569410^|^25569410^|^^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - "^|^1^|^21:14:49^|^25569410.extern^|^25569410.extern" - "^|^1548K^|^1^|^4000Mc^|^RUNNING^|^00:00:00\n" - "^|^1^|^21:14:43^|^25569410.0^|^25569410.0^|^62328K" - "^|^1^|^4000Mc^|^RUNNING^|^19:28:36\n" - ) + sub_result.stdout = console_jobs["24418435_notime"] + console_jobs[ + "25569410_notime" + ].replace("COMPLETED", "RUNNING") mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, "--no-color --state ZZ 25569410 24418435".split() @@ -638,20 +501,13 @@ def test_no_state(mocker, mock_inquirer): assert output[3] == "" -def test_array_job_raw_id(mocker, mock_inquirer): +def test_array_job_raw_id(mocker, mock_inquirer, console_jobs): """Can find job array by base id.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^00:09:34^|^24220929_421^|^24221219^|^^|^1^|^16000Mn^|^" - "COMPLETED^|^09:28.052\n" - "^|^1^|^00:09:34^|^24220929_421.batch^|^24221219.batch" - "^|^5664932K^|^1^|^16000Mn^|^COMPLETED^|^09:28.051\n" - "^|^1^|^00:09:34^|^24220929_421.extern^|^24221219.extern" - "^|^1404K^|^1^|^16000Mn^|^COMPLETED^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["24221219"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -671,26 +527,13 @@ def test_array_job_raw_id(mocker, mock_inquirer): assert len(output) == 1 -def test_array_job_single(mocker, mock_inquirer): +def test_array_job_single(mocker, mock_inquirer, console_jobs): """Can get single array job element.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^00:09:34^|^24220929_421^|^24221219^|^^|^1^|^16000Mn^|^" - "COMPLETED^|^09:28.052\n" - "^|^1^|^00:09:34^|^24220929_421.batch^|^24221219.batch" - "^|^5664932K^|^1^|^16000Mn^|^COMPLETED^|^09:28.051\n" - "^|^1^|^00:09:34^|^24220929_421.extern^|^24221219.extern" - "^|^1404K^|^1^|^16000Mn^|^COMPLETED^|^00:00:00\n" - "^|^1^|^00:09:33^|^24220929_431^|^24221220^|^^|^1^|^16000Mn^|^" - "PENDING^|^09:27.460\n" - "^|^1^|^00:09:33^|^24220929_431.batch^|^24221220.batch" - "^|^5518572K^|^1^|^16000Mn^|^PENDING^|^09:27.459\n" - "^|^1^|^00:09:33^|^24220929_431.extern^|^24221220.extern" - "^|^1400K^|^1^|^16000Mn^|^PENDING^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["24221219"] + console_jobs["24221220"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -712,26 +555,13 @@ def test_array_job_single(mocker, mock_inquirer): assert len(output) == 1 -def test_array_job_base(mocker, mock_inquirer): +def test_array_job_base(mocker, mock_inquirer, console_jobs): """Base array job id gets all elements.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^1^|^00:09:34^|^24220929_421^|^24221219^|^^|^1^|^16000Mn^|^" - "COMPLETED^|^09:28.052\n" - "^|^1^|^00:09:34^|^24220929_421.batch^|^24221219.batch^|^" - "5664932K^|^1^|^16000Mn^|^COMPLETED^|^09:28.051\n" - "^|^1^|^00:09:34^|^24220929_421.extern^|^24221219.extern^|^" - "1404K^|^1^|^16000Mn^|^COMPLETED^|^00:00:00\n" - "^|^1^|^00:09:33^|^24220929_431^|^24221220^|^^|^1^|^16000Mn^|^" - "PENDING^|^09:27.460\n" - "^|^1^|^00:09:33^|^24220929_431.batch^|^24221220.batch^|^" - "5518572K^|^1^|^16000Mn^|^PENDING^|^09:27.459\n" - "^|^1^|^00:09:33^|^24220929_431.extern^|^24221220.extern^|^" - "1400K^|^1^|^16000Mn^|^PENDING^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["24221219"] + console_jobs["24221220"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke( console.main, @@ -789,19 +619,13 @@ def test_empty_sacct(mocker, mock_inquirer): assert len(output) == 1 -def test_failed_no_mem(mocker, mock_inquirer): +def test_failed_no_mem(mocker, mock_inquirer, console_jobs): """Empty memory entries produce valid output.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^8^|^00:00:12^|^23000381^|^23000381^|^^|^1^|^4000Mc^|^FAILED^|^00:00:00\n" - "^|^8^|^00:00:12^|^23000381.batch^|^23000381.batch^|^^|^1^|^4000Mc^|^" - "FAILED^|^00:00:00\n" - "^|^8^|^00:00:12^|^23000381.extern^|^23000381.extern^|^1592K^|^1^|^4000Mc^|^" - "COMPLETED^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000381"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke(console.main, "--no-color 23000381".split()) @@ -812,16 +636,13 @@ def test_failed_no_mem(mocker, mock_inquirer): assert len(output) == 1 -def test_canceled_by_other(mocker, mock_inquirer): +def test_canceled_by_other(mocker, mock_inquirer, console_jobs): """Canceled states are correctly handled.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^16^|^00:00:00^|^23000233^|^23000233^|^^|^1^|^" - "4000Mc^|^CANCELLED by 129319^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000233"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke(console.main, "--no-color 23000233 --state CA".split()) @@ -832,27 +653,20 @@ def test_canceled_by_other(mocker, mock_inquirer): "23000233", "CANCELLED", "00:00:00", - "---", + "0.0%", "---", "0.0%", ] assert len(output) == 1 -def test_zero_runtime(mocker, mock_inquirer): +def test_zero_runtime(mocker, mock_inquirer, console_jobs): """Entries with zero runtime produce reasonable timeeff.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 - sub_result.stdout = ( - "^|^8^|^00:00:00^|^23000210^|^23000210^|^^|^1^|^20000Mn^|^" - "FAILED^|^00:00.007\n" - "^|^8^|^00:00:00^|^23000210.batch^|^23000210.batch^|^1988K^|^1^|^20000Mn^|^" - "FAILED^|^00:00.006\n" - "^|^8^|^00:00:00^|^23000210.extern^|^23000210.extern^|^1556K^|^1^|^20000Mn^|^" - "COMPLETED^|^00:00:00\n" - ) + sub_result.stdout = console_jobs["23000210"] mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke(console.main, "--no-color 23000210".split()) @@ -875,56 +689,64 @@ def test_no_systems(mocker, mock_inquirer): assert output[0] == "No supported scheduling systems found!" -def test_issue_16(mocker, mock_inquirer): +def test_issue_16(mocker, mock_inquirer, console_jobs): """Incorrect memory usage for multi-node jobs.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = """ -^|^16^|^07:36:03^|^65638294^|^65638294^|^^|^2^|^32G\ +^|^16^|^07:36:03^|^65638294^|^65638294^|^^|^1^|^2^|^32G\ ^|^COMPLETED^|^6-23:59:00^|^4-23:56:21 ^|^1^|^07:36:03^|^65638294.batch^|^65638294.batch\ -^|^1147220K^|^1^|^^|^COMPLETED^|^^|^07:30:20 +^|^1147220K^|^1^|^1^|^^|^COMPLETED^|^^|^07:30:20 ^|^16^|^07:36:03^|^65638294.extern^|^65638294.extern\ -^|^0^|^2^|^^|^COMPLETED^|^^|^00:00.001 -^|^15^|^00:00:11^|^65638294.0^|^65638294.0^|^0^|^1^|^^|^COMPLETED^|^^|^00:11.830 -^|^15^|^00:02:15^|^65638294.1^|^65638294.1^|^4455540K^|^1^|^^|^COMPLETED^|^^|^31:09.458 -^|^15^|^00:00:10^|^65638294.2^|^65638294.2^|^0^|^1^|^^|^COMPLETED^|^^|^00:00:04 -^|^15^|^00:00:08^|^65638294.3^|^65638294.3^|^0^|^1^|^^|^COMPLETED^|^^|^00:09.602 -^|^15^|^00:00:07^|^65638294.4^|^65638294.4^|^0^|^1^|^^|^COMPLETED^|^^|^00:56.827 -^|^15^|^00:00:06^|^65638294.5^|^65638294.5^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.512 -^|^15^|^00:00:08^|^65638294.6^|^65638294.6^|^0^|^1^|^^|^COMPLETED^|^^|^00:08.520 -^|^15^|^00:00:13^|^65638294.7^|^65638294.7^|^0^|^1^|^^|^COMPLETED^|^^|^01:02.013 -^|^15^|^00:00:02^|^65638294.8^|^65638294.8^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.639 -^|^15^|^00:00:06^|^65638294.9^|^65638294.9^|^0^|^1^|^^|^COMPLETED^|^^|^00:08.683 -^|^15^|^00:00:08^|^65638294.10^|^65638294.10^|^0^|^1^|^^|^COMPLETED^|^^|^00:57.438 -^|^15^|^00:00:06^|^65638294.11^|^65638294.11^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.642 -^|^15^|^00:00:09^|^65638294.12^|^65638294.12^|^0^|^1^|^^|^COMPLETED^|^^|^00:10.271 +^|^0^|^1^|^2^|^^|^COMPLETED^|^^|^00:00.001 +^|^15^|^00:00:11^|^65638294.0^|^65638294.0^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:11.830 +^|^15^|^00:02:15^|^65638294.1^|^65638294.1^|^4455540K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^31:09.458 +^|^15^|^00:00:10^|^65638294.2^|^65638294.2^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:00:04 +^|^15^|^00:00:08^|^65638294.3^|^65638294.3^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:09.602 +^|^15^|^00:00:07^|^65638294.4^|^65638294.4^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:56.827 +^|^15^|^00:00:06^|^65638294.5^|^65638294.5^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.512 +^|^15^|^00:00:08^|^65638294.6^|^65638294.6^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:08.520 +^|^15^|^00:00:13^|^65638294.7^|^65638294.7^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^01:02.013 +^|^15^|^00:00:02^|^65638294.8^|^65638294.8^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.639 +^|^15^|^00:00:06^|^65638294.9^|^65638294.9^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:08.683 +^|^15^|^00:00:08^|^65638294.10^|^65638294.10^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:57.438 +^|^15^|^00:00:06^|^65638294.11^|^65638294.11^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.642 +^|^15^|^00:00:09^|^65638294.12^|^65638294.12^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:10.271 ^|^15^|^00:01:24^|^65638294.13^|^65638294.13^|^4149700K\ -^|^1^|^^|^COMPLETED^|^^|^17:18.067 -^|^15^|^00:00:01^|^65638294.14^|^65638294.14^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.302 -^|^15^|^00:00:10^|^65638294.15^|^65638294.15^|^0^|^1^|^^|^COMPLETED^|^^|^00:14.615 -^|^15^|^00:06:45^|^65638294.16^|^65638294.16^|^4748052K^|^1^|^^|^COMPLETED^|^^|^01:36:40 -^|^15^|^00:00:10^|^65638294.17^|^65638294.17^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.864 -^|^15^|^00:00:09^|^65638294.18^|^65638294.18^|^0^|^1^|^^|^COMPLETED^|^^|^00:48.987 -^|^15^|^01:32:53^|^65638294.19^|^65638294.19^|^7734356K^|^1^|^^|^COMPLETED^|^^|^23:09:33 -^|^15^|^00:00:01^|^65638294.20^|^65638294.20^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.520 -^|^15^|^00:00:07^|^65638294.21^|^65638294.21^|^0^|^1^|^^|^COMPLETED^|^^|^00:50.015 -^|^15^|^00:55:17^|^65638294.22^|^65638294.22^|^8074500K^|^1^|^^|^COMPLETED^|^^|^13:45:29 -^|^15^|^00:00:13^|^65638294.23^|^65638294.23^|^0^|^1^|^^|^COMPLETED^|^^|^00:04.413 -^|^15^|^00:00:12^|^65638294.24^|^65638294.24^|^0^|^1^|^^|^COMPLETED^|^^|^00:49.100 -^|^15^|^00:57:41^|^65638294.25^|^65638294.25^|^7883152K^|^1^|^^|^COMPLETED^|^^|^14:20:36 -^|^15^|^00:00:01^|^65638294.26^|^65638294.26^|^0^|^1^|^^|^COMPLETED^|^^|^00:03.953 -^|^15^|^00:00:05^|^65638294.27^|^65638294.27^|^0^|^1^|^^|^COMPLETED^|^^|^00:47.223 -^|^15^|^01:00:17^|^65638294.28^|^65638294.28^|^7715752K^|^1^|^^|^COMPLETED^|^^|^14:59:40 -^|^15^|^00:00:06^|^65638294.29^|^65638294.29^|^0^|^1^|^^|^COMPLETED^|^^|^00:04.341 -^|^15^|^00:00:07^|^65638294.30^|^65638294.30^|^0^|^1^|^^|^COMPLETED^|^^|^00:50.416 -^|^15^|^01:22:31^|^65638294.31^|^65638294.31^|^7663264K^|^1^|^^|^COMPLETED^|^^|^20:33:59 -^|^15^|^00:00:05^|^65638294.32^|^65638294.32^|^0^|^1^|^^|^COMPLETED^|^^|^00:04.199 -^|^15^|^00:00:08^|^65638294.33^|^65638294.33^|^0^|^1^|^^|^COMPLETED^|^^|^00:50.009 -^|^15^|^01:32:23^|^65638294.34^|^65638294.34^|^7764884K^|^1^|^^|^COMPLETED^|^^|^23:01:52 -^|^15^|^00:00:06^|^65638294.35^|^65638294.35^|^0^|^1^|^^|^COMPLETED^|^^|^00:04.527 +^|^1^|^1^|^^|^COMPLETED^|^^|^17:18.067 +^|^15^|^00:00:01^|^65638294.14^|^65638294.14^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.302 +^|^15^|^00:00:10^|^65638294.15^|^65638294.15^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:14.615 +^|^15^|^00:06:45^|^65638294.16^|^65638294.16^|^4748052K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^01:36:40 +^|^15^|^00:00:10^|^65638294.17^|^65638294.17^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.864 +^|^15^|^00:00:09^|^65638294.18^|^65638294.18^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:48.987 +^|^15^|^01:32:53^|^65638294.19^|^65638294.19^|^7734356K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^23:09:33 +^|^15^|^00:00:01^|^65638294.20^|^65638294.20^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.520 +^|^15^|^00:00:07^|^65638294.21^|^65638294.21^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:50.015 +^|^15^|^00:55:17^|^65638294.22^|^65638294.22^|^8074500K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^13:45:29 +^|^15^|^00:00:13^|^65638294.23^|^65638294.23^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:04.413 +^|^15^|^00:00:12^|^65638294.24^|^65638294.24^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:49.100 +^|^15^|^00:57:41^|^65638294.25^|^65638294.25^|^7883152K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^14:20:36 +^|^15^|^00:00:01^|^65638294.26^|^65638294.26^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:03.953 +^|^15^|^00:00:05^|^65638294.27^|^65638294.27^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:47.223 +^|^15^|^01:00:17^|^65638294.28^|^65638294.28^|^7715752K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^14:59:40 +^|^15^|^00:00:06^|^65638294.29^|^65638294.29^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:04.341 +^|^15^|^00:00:07^|^65638294.30^|^65638294.30^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:50.416 +^|^15^|^01:22:31^|^65638294.31^|^65638294.31^|^7663264K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^20:33:59 +^|^15^|^00:00:05^|^65638294.32^|^65638294.32^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:04.199 +^|^15^|^00:00:08^|^65638294.33^|^65638294.33^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:50.009 +^|^15^|^01:32:23^|^65638294.34^|^65638294.34^|^7764884K\ +^|^1^|^1^|^^|^COMPLETED^|^^|^23:01:52 +^|^15^|^00:00:06^|^65638294.35^|^65638294.35^|^0^|^1^|^1^|^^|^COMPLETED^|^^|^00:04.527 """ mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) result = runner.invoke(console.main, "--no-color 65638294".split()) @@ -943,36 +765,36 @@ def test_issue_16(mocker, mock_inquirer): assert len(output) == 1 -def test_energy_reporting(mocker, mock_inquirer): +def test_energy_reporting(mocker, mock_inquirer, console_jobs): """Include energy reporting with the `energy` format code.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() sub_result = mocker.MagicMock() sub_result.returncode = 0 sub_result.stdout = ( - "^|^32^|^00:01:09^|^37403870_1^|^37403937^|^^|^1^|^32000M^|^" + "^|^32^|^00:01:09^|^37403870_1^|^37403937^|^^|^1^|^1^|^32000M^|^" "COMPLETED^|^^|^00:02:00^|^00:47.734\n" - "^|^32^|^00:01:09^|^37403870_1.batch^|^37403937.batch^|^6300K^|^1^|^^|^" + "^|^32^|^00:01:09^|^37403870_1.batch^|^37403937.batch^|^6300K^|^1^|^1^|^^|^" "COMPLETED^|^energy=33,fs/disk=0^|^^|^00:47.733\n" - "^|^32^|^00:01:09^|^37403870_1.extern^|^37403937.extern^|^4312K^|^1^|^^|^" + "^|^32^|^00:01:09^|^37403870_1.extern^|^37403937.extern^|^4312K^|^1^|^1^|^^|^" "COMPLETED^|^energy=33,fs/disk=0^|^^|^00:00.001\n" - "^|^32^|^00:01:21^|^37403870_2^|^37403938^|^^|^1^|^32000M^|^" + "^|^32^|^00:01:21^|^37403870_2^|^37403938^|^^|^1^|^1^|^32000M^|^" "COMPLETED^|^^|^00:02:00^|^00:41.211\n" - "^|^32^|^00:01:21^|^37403870_2.batch^|^37403938.batch^|^6316K^|^1^|^^|^" + "^|^32^|^00:01:21^|^37403870_2.batch^|^37403938.batch^|^6316K^|^1^|^1^|^^|^" "COMPLETED^|^energy=32,fs/disk=0^|^^|^00:41.210\n" - "^|^32^|^00:01:21^|^37403870_2.extern^|^37403938.extern^|^4312K^|^1^|^^|^" + "^|^32^|^00:01:21^|^37403870_2.extern^|^37403938.extern^|^4312K^|^1^|^1^|^^|^" "COMPLETED^|^energy=32,fs/disk=0^|^^|^00:00:00\n" - "^|^32^|^00:01:34^|^37403870_3^|^37403939^|^^|^1^|^32000M^|^" + "^|^32^|^00:01:34^|^37403870_3^|^37403939^|^^|^1^|^1^|^32000M^|^" "COMPLETED^|^^|^00:02:00^|^00:51.669\n" - "^|^32^|^00:01:34^|^37403870_3.batch^|^37403939.batch^|^6184K^|^1^|^^|^" + "^|^32^|^00:01:34^|^37403870_3.batch^|^37403939.batch^|^6184K^|^1^|^1^|^^|^" "COMPLETED^|^energy=30,fs/disk=0^|^^|^00:51.667\n" - "^|^32^|^00:01:35^|^37403870_3.extern^|^37403939.extern^|^4312K^|^1^|^^|^" + "^|^32^|^00:01:35^|^37403870_3.extern^|^37403939.extern^|^4312K^|^1^|^1^|^^|^" "COMPLETED^|^fs/disk=0,energy=30^|^^|^00:00.001\n" - "^|^32^|^00:01:11^|^37403870_4^|^37403870^|^^|^1^|^32000M^|^" + "^|^32^|^00:01:11^|^37403870_4^|^37403870^|^^|^1^|^1^|^32000M^|^" "COMPLETED^|^^|^00:02:00^|^01:38.184\n" - "^|^32^|^00:01:11^|^37403870_4.batch^|^37403870.batch^|^6300K^|^1^|^^|^" + "^|^32^|^00:01:11^|^37403870_4.batch^|^37403870.batch^|^6300K^|^1^|^1^|^^|^" "COMPLETED^|^fs/disk=0^|^^|^01:38.183\n" - "^|^32^|^00:01:11^|^37403870_4.extern^|^37403870.extern^|^4312K^|^1^|^^|^" + "^|^32^|^00:01:11^|^37403870_4.extern^|^37403870.extern^|^4312K^|^1^|^1^|^^|^" "COMPLETED^|^energy=27,fs/disk=0^|^^|^00:00.001\n" ) mocker.patch("reportseff.db_inquirer.subprocess.run", return_value=sub_result) @@ -1028,7 +850,7 @@ def test_energy_reporting(mocker, mock_inquirer): assert len(output) == 5 -def test_extra_args(mocker, mock_inquirer): +def test_extra_args(mocker, mock_inquirer, console_jobs): """Can add extra arguments for sacct.""" mocker.patch("reportseff.console.which", return_value=True) runner = CliRunner() From e44fde6383d7eeb4ae5331e70c01601293555aba Mon Sep 17 00:00:00 2001 From: Troy Comi Date: Thu, 14 Sep 2023 10:36:00 -0400 Subject: [PATCH 2/3] chore: version bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d536f8b..2b81226 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "reportseff" -version = "2.7.5" +version = "2.7.6" description= "Tablular seff output" authors = ["Troy Comi "] license = "MIT" From 346c806167e9c0ac4ee56d13b8b2866c94667715 Mon Sep 17 00:00:00 2001 From: Troy Comi Date: Thu, 14 Sep 2023 11:20:24 -0400 Subject: [PATCH 3/3] fix: docstring --- tests/test_job.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_job.py b/tests/test_job.py index 45057b9..7f6eb46 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -767,7 +767,10 @@ def test_multinode_job(multinode_job): def test_multinode_job_issue_41(issue_41): - """Testing issue37 which is not actually a bug efficiency is truly 5%.""" + """Testing issue 41 where multiple tasks are used. + + Previously reported incorrect memory efficiency. + """ job = job_module.Job("131042", "131042", None) for line in issue_41: job.update(line)