Skip to content

Commit

Permalink
add unit-test for workload
Browse files Browse the repository at this point in the history
  • Loading branch information
mo-dkrz committed Apr 8, 2024
1 parent 3d7f48d commit 9408cfa
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 1 deletion.
112 changes: 112 additions & 0 deletions src/evaluation_system/tests/workload_manager/test_core.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,121 @@
import re
import subprocess
from unittest.mock import MagicMock, patch

import pytest

from evaluation_system.api.workload_manager.core import Job


def test_get_cancel_command():
from evaluation_system.api.workload_manager import cancel_command

with pytest.raises(NotImplementedError):
cancel_command("bla", 1000)
assert "scancel" in cancel_command("slurm", 1000)


def test_parse_bytes():

from evaluation_system.api.workload_manager.core import parse_bytes

assert parse_bytes(123) == 123, "Integer input should return the input as is"

assert (
parse_bytes(123.0) == 123
), "Float input should return the input converted to int"

assert (
parse_bytes("100") == 100
), "String input with digits should be correctly parsed"

assert (
parse_bytes(" 100 MB ") == 100000000
), "String input with spaces should be correctly parsed"

assert parse_bytes("5kB") == 5000, "String with kB should be correctly parsed"
assert parse_bytes("1kiB") == 1024, "String with kiB should be correctly parsed"

assert (
parse_bytes("MB") == 1000000
), "String without digits should prepend '1' and parse correctly"

try:
parse_bytes("5 blah")
assert False, "Expected ValueError for input with invalid units"
except ValueError as e:
assert (
str(e) == "Could not interpret 'blah' as a byte unit"
), "Expected specific ValueError message for invalid units"

try:
parse_bytes("1e6 blah")
assert False, "Expected ValueError for malformed number input"
except ValueError as e:
assert "Could not interpret" in str(
e
), "Expected ValueError for malformed number input"


def test_string_to_bytes():
from evaluation_system.api.workload_manager.core import string_to_bytes

assert (
string_to_bytes("5g") == 5 * 1000**3
) # Specific case when byte_form length is 1


def test_get_format_bytes():
from evaluation_system.api.workload_manager.core import format_bytes

assert format_bytes(1) == "1 B", "Failed to format bytes correctly."
assert format_bytes(1023) == "1.00 kiB", "Failed to format Kibibytes correctly."
assert format_bytes(1024) == "1.00 kiB", "Failed to format Kibibytes correctly."
assert format_bytes(2**20) == "1.00 MiB", "Failed to format Mebibytes correctly."
assert format_bytes(2**30) == "1.00 GiB", "Failed to format Gibibytes correctly."
assert format_bytes(2**40) == "1.00 TiB", "Failed to format Tebibytes correctly."
assert format_bytes(2**50) == "1.00 PiB", "Failed to format Pebibytes correctly."
assert format_bytes(1024 * 1024 - 1) == "1.00 MiB", "Failed on edge of MiB."


class MockJob(Job):
submit_command = "mock_submit"
cancel_command = "mock_cancel"

def __init__(self, *args, **kwargs):
# For simplicity, bypass Job's __init__ with a direct call to super().__init__()
# This avoids needing to deal with Job's abstract __init__ parameters.
super(Job, self).__init__(*args, **kwargs)
self.job_id = "" # Ensuring this attribute is set for testing.
self.name = "mock_job"


def test_job_methods():

job_instance = MockJob()

with patch("subprocess.Popen") as mock_popen:
mock_process = MagicMock()
mock_process.communicate.return_value = (b"output", b"")
mock_process.returncode = 0
mock_popen.return_value = mock_process

output = job_instance._call(["echo", "test"])
assert output == "output", "Expected mocked output from _call method."

job_id_output = "Submitted batch job 12345"
assert (
job_instance._job_id_from_submit_output(job_id_output) == "12345"
), "Failed to extract job ID."

try:
job_instance._job_id_from_submit_output("No job id here")
except ValueError as e:
assert "Could not parse job id" in str(
e
), "Expected failure when job ID is missing."

with patch.object(MockJob, "_call", return_value="") as mock_call:
job_instance = MockJob()
job_instance._close_job("12345", "scancel")
mock_call.assert_called_with(["scancel", "12345"])
19 changes: 18 additions & 1 deletion src/evaluation_system/tests/workload_manager/test_slurm.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from evaluation_system.api.workload_manager.slurm import SLURMJob


def test_header():
def test_header(tmpdir):
log_directory = tmpdir.strpath
with SLURMJob(walltime="00:02:00", processes=4, cores=8, memory="28GB") as cluster:
assert "#SBATCH" in cluster.job_header
assert "#SBATCH -J worker" in cluster.job_header
Expand All @@ -20,6 +21,7 @@ def test_header():
memory="28GB",
job_cpu=16,
job_mem="100G",
log_directory=log_directory,
) as cluster:
assert "#SBATCH --cpus-per-task=16" in cluster.job_header
assert "#SBATCH --cpus-per-task=8" not in cluster.job_header
Expand Down Expand Up @@ -75,3 +77,18 @@ def test_job_script():
assert 'export LANG="en_US.utf8"' in job_script
assert 'export LANGUAGE="en_US.utf8"' in job_script
assert 'export LC_ALL="en_US.utf8"' in job_script


def test_slurm_format_bytes_ceil():
from evaluation_system.api.workload_manager.slurm import slurm_format_bytes_ceil

assert (
slurm_format_bytes_ceil(1024**3 - 1) == "1024M"
), "Failed to round up just below 1G bytes"
assert slurm_format_bytes_ceil(1024**2) == "1M", "Failed for exact 1M bytes"
assert (
slurm_format_bytes_ceil(1025) == "2K"
), "Failed to round up slightly over 1K bytes"
assert (
slurm_format_bytes_ceil(1022) == "1K"
), "Failed to round up slightly over 1K bytes"

0 comments on commit 9408cfa

Please sign in to comment.