Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/Tests/unit/test_TensileBenchmarkClusterScripts.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

522 lines
20 KiB
Python

################################################################################
#
# Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# SPDX-License-Identifier: MIT
################################################################################
import pytest
import os
import stat
from pathlib import Path
from unittest.mock import Mock, patch, mock_open, MagicMock, call
from Tensile.TensileBenchmarkClusterScripts import (
ScriptHelper,
ScriptWriter,
BenchmarkNodeWriter,
BenchmarkTaskWriter,
BenchmarkJobWriter,
)
@pytest.mark.unit
class TestScriptHelper:
"""Test suite for ScriptHelper class."""
def test_genLine_simple_text(self):
"""ScriptHelper.genLine generates line with newline."""
result = ScriptHelper.genLine("test")
assert result == "test\n"
def test_genLine_empty_string(self):
"""ScriptHelper.genLine handles empty string."""
result = ScriptHelper.genLine("")
assert result == "\n"
def test_genLine_with_numbers(self):
"""ScriptHelper.genLine handles numeric input."""
result = ScriptHelper.genLine(123)
assert result == "123\n"
def test_genComment_adds_hash(self):
"""ScriptHelper.genComment adds hash prefix."""
result = ScriptHelper.genComment("test comment")
assert result == "#test comment\n"
def test_genComment_empty(self):
"""ScriptHelper.genComment handles empty comment."""
result = ScriptHelper.genComment("")
assert result == "#\n"
def test_genEcho_wraps_in_echo(self):
"""ScriptHelper.genEcho wraps text in echo command."""
result = ScriptHelper.genEcho("hello")
assert result == 'echo "hello";\n'
def test_genEcho_escapes_quotes(self):
"""ScriptHelper.genEcho preserves quotes in text."""
result = ScriptHelper.genEcho('say "hi"')
assert 'echo "say "hi""' in result or 'echo "say \\"hi\\""' in result
@patch("os.chmod")
@patch("os.stat")
def test_makeExecutable_sets_permissions(self, mock_stat, mock_chmod):
"""ScriptHelper.makeExecutable sets executable permissions."""
mock_stat_result = Mock()
mock_stat_result.st_mode = 0o644
mock_stat.return_value = mock_stat_result
ScriptHelper.makeExecutable("/path/to/script.sh")
mock_stat.assert_called_once_with("/path/to/script.sh")
mock_chmod.assert_called_once()
# Check that executable bits were added
called_mode = mock_chmod.call_args[0][1]
assert called_mode & 0o111 # At least one execute bit set
@patch("os.chmod")
@patch("os.stat")
def test_makeExecutable_preserves_read_bits(self, mock_stat, mock_chmod):
"""ScriptHelper.makeExecutable preserves read permissions."""
mock_stat_result = Mock()
mock_stat_result.st_mode = 0o444 # r--r--r--
mock_stat.return_value = mock_stat_result
ScriptHelper.makeExecutable("/path/to/script.sh")
called_mode = mock_chmod.call_args[0][1]
# Should be r-xr-xr-x (0o555)
assert called_mode == 0o555
@pytest.mark.unit
class TestScriptWriter:
"""Test suite for ScriptWriter class."""
def test_init_creates_empty_buffer(self):
"""ScriptWriter initializes with empty buffer."""
writer = ScriptWriter()
assert writer._buffer == ""
def test_writeLine_adds_line(self):
"""ScriptWriter.writeLine adds line to buffer."""
writer = ScriptWriter()
writer.writeLine("test")
assert writer._buffer == "test\n"
def test_writeLine_multiple_lines(self):
"""ScriptWriter.writeLine accumulates multiple lines."""
writer = ScriptWriter()
writer.writeLine("line1")
writer.writeLine("line2")
assert writer._buffer == "line1\nline2\n"
def test_writeComment_adds_comment(self):
"""ScriptWriter.writeComment adds comment to buffer."""
writer = ScriptWriter()
writer.writeComment("comment")
assert writer._buffer == "#comment\n"
def test_writeEcho_adds_echo(self):
"""ScriptWriter.writeEcho adds echo command to buffer."""
writer = ScriptWriter()
writer.writeEcho("message")
assert 'echo "message";' in writer._buffer
def test_str_returns_buffer(self):
"""ScriptWriter.__str__ returns buffer content."""
writer = ScriptWriter()
writer.writeLine("test")
assert str(writer) == "test\n"
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_dumpToFile_writes_to_file(self, mock_exec, mock_file):
"""ScriptWriter.dumpToFile writes buffer to file."""
writer = ScriptWriter()
writer.writeLine("#!/bin/bash")
writer.writeLine("echo 'test'")
writer.dumpToFile("script.sh", "/output")
mock_file.assert_called_once_with("/output/script.sh", "w")
mock_file().write.assert_called_once_with("#!/bin/bash\necho 'test'\n")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_dumpToFile_makes_executable(self, mock_exec, mock_file):
"""ScriptWriter.dumpToFile makes file executable."""
writer = ScriptWriter()
writer.writeLine("test")
writer.dumpToFile("script.sh", "/output")
mock_exec.assert_called_once_with("/output/script.sh")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_dumpToFile_default_output_dir(self, mock_exec, mock_file):
"""ScriptWriter.dumpToFile uses current dir by default."""
writer = ScriptWriter()
writer.writeLine("test")
writer.dumpToFile("script.sh")
mock_file.assert_called_once_with("./script.sh", "w")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeScript_factory_method(self, mock_exec, mock_file):
"""ScriptWriter.writeScript factory method creates script."""
mock_writer_class = Mock()
# Call writeScript - it will use ScriptWriter as the buffer class (cls)
ScriptWriter.writeScript(mock_writer_class, "test.sh", "/out")
# Verify the writer methods were called
mock_writer_class.writeHeader.assert_called_once()
mock_writer_class.writeBody.assert_called_once()
mock_writer_class.writeEpilogue.assert_called_once()
# Verify file was written
mock_file.assert_called_once_with("/out/test.sh", "w")
mock_exec.assert_called_once_with("/out/test.sh")
@patch("builtins.print")
def test_previewScript_prints_buffer(self, mock_print):
"""ScriptWriter.previewScript prints script content."""
mock_writer_class = Mock()
# Call previewScript - it will use ScriptWriter as the buffer class (cls)
ScriptWriter.previewScript(mock_writer_class)
# Verify the writer methods were called
mock_writer_class.writeHeader.assert_called_once()
mock_writer_class.writeBody.assert_called_once()
mock_writer_class.writeEpilogue.assert_called_once()
# Verify print was called
mock_print.assert_called_once()
@pytest.mark.unit
class TestBenchmarkNodeWriter:
"""Test suite for BenchmarkNodeWriter class."""
def test_writeHeader_adds_header(self):
"""BenchmarkNodeWriter.writeHeader writes header comments."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeHeader(buffer)
content = str(buffer)
assert "#!/bin/bash" in content
assert "#Benchmark node script" in content
def test_writeBody_includes_functions(self):
"""BenchmarkNodeWriter.writeBody includes function definitions."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeBody(buffer)
content = str(buffer)
assert "usage()" in content
assert "failAndExit()" in content
def test_writeBody_includes_arg_parsing(self):
"""BenchmarkNodeWriter.writeBody includes argument parsing."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeBody(buffer)
content = str(buffer)
assert "getopts" in content
assert "dockerImagePath" in content
assert "logDir" in content
assert "resultDir" in content
assert "taskDir" in content
def test_writeBody_includes_docker_load(self):
"""BenchmarkNodeWriter.writeBody includes docker load commands."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeBody(buffer)
content = str(buffer)
assert "docker load" in content
assert "dockerImageId" in content
def test_writeBody_includes_docker_run(self):
"""BenchmarkNodeWriter.writeBody includes docker run command."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeBody(buffer)
content = str(buffer)
assert "docker run" in content
assert "--device=/dev/kfd" in content
assert "--device=/dev/dri" in content
def test_writeEpilogue_adds_exit(self):
"""BenchmarkNodeWriter.writeEpilogue adds exit statement."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeEpilogue(buffer)
content = str(buffer)
assert "exit $returnCode" in content
assert "Done!" in content
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_full_script_generation(self, mock_exec, mock_file):
"""BenchmarkNodeWriter generates complete script."""
buffer = ScriptWriter()
BenchmarkNodeWriter.writeHeader(buffer)
BenchmarkNodeWriter.writeBody(buffer)
BenchmarkNodeWriter.writeEpilogue(buffer)
content = str(buffer)
# Check structure
assert content.startswith("#")
assert "#!/bin/bash" in content or "#!/bin/bash" in content
assert "docker" in content
assert "exit" in content
@pytest.mark.unit
class TestBenchmarkTaskWriter:
"""Test suite for BenchmarkTaskWriter class."""
def test_writeHeader_adds_header(self):
"""BenchmarkTaskWriter.writeHeader writes header comments."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeHeader(buffer)
content = str(buffer)
assert "#!/bin/bash" in content
assert "#Benchmark task enqueue script" in content
def test_writeBody_includes_functions(self):
"""BenchmarkTaskWriter.writeBody includes function definitions."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeBody(buffer)
content = str(buffer)
assert "usage()" in content
assert "failAndExit()" in content
def test_writeBody_includes_arg_parsing(self):
"""BenchmarkTaskWriter.writeBody includes argument parsing."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeBody(buffer)
content = str(buffer)
assert "getopts" in content
assert "imageDir" in content
assert "logsDir" in content
assert "resultsDir" in content
assert "tasksDir" in content
def test_writeBody_includes_task_config(self):
"""BenchmarkTaskWriter.writeBody includes task configuration."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeBody(buffer)
content = str(buffer)
assert "SLURM_ARRAY_TASK_ID" in content
assert "taskName" in content
assert "mkdir -p" in content
def test_writeBody_includes_srun(self):
"""BenchmarkTaskWriter.writeBody includes srun command."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeBody(buffer)
content = str(buffer)
assert "srun" in content
assert "-N 1" in content
def test_writeEpilogue_adds_exit(self):
"""BenchmarkTaskWriter.writeEpilogue adds exit statement."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeEpilogue(buffer)
content = str(buffer)
assert "exit $returnCode" in content
assert "Done!" in content
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_full_script_generation(self, mock_exec, mock_file):
"""BenchmarkTaskWriter generates complete script."""
buffer = ScriptWriter()
BenchmarkTaskWriter.writeHeader(buffer)
BenchmarkTaskWriter.writeBody(buffer)
BenchmarkTaskWriter.writeEpilogue(buffer)
content = str(buffer)
# Check structure
assert content.startswith("#")
assert "srun" in content
assert "SLURM" in content
assert "exit" in content
@pytest.mark.unit
class TestBenchmarkJobWriter:
"""Test suite for BenchmarkJobWriter class."""
def test_writeHeader_adds_header(self):
"""BenchmarkJobWriter.writeHeader writes header comments."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeHeader(buffer)
content = str(buffer)
assert "#!/bin/bash" in content
assert "#Benchmark batch job script" in content
def test_writeBody_includes_functions(self):
"""BenchmarkJobWriter.writeBody includes function definitions."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeBody(buffer)
content = str(buffer)
assert "usage()" in content
assert "failAndExit()" in content
def test_writeBody_includes_arg_parsing(self):
"""BenchmarkJobWriter.writeBody includes argument parsing."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeBody(buffer)
content = str(buffer)
assert "getopts" in content
assert "imageDir" in content
assert "logsDir" in content
assert "resultsDir" in content
assert "tasksDir" in content
assert "taskScript" in content
def test_writeBody_includes_job_config(self):
"""BenchmarkJobWriter.writeBody includes job configuration."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeBody(buffer)
content = str(buffer)
assert "arraySize" in content
assert "arrayStart" in content
assert "arrayEnd" in content
assert "slurmLogsDir" in content
def test_writeBody_includes_sbatch(self):
"""BenchmarkJobWriter.writeBody includes sbatch command."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeBody(buffer)
content = str(buffer)
assert "sbatch" in content
assert "--nodes=1" in content
assert "--array=" in content
assert "--wait" in content
def test_writeEpilogue_adds_exit(self):
"""BenchmarkJobWriter.writeEpilogue adds exit statement."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeEpilogue(buffer)
content = str(buffer)
assert "exit $returnCode" in content
assert "Done!" in content
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_full_script_generation(self, mock_exec, mock_file):
"""BenchmarkJobWriter generates complete script."""
buffer = ScriptWriter()
BenchmarkJobWriter.writeHeader(buffer)
BenchmarkJobWriter.writeBody(buffer)
BenchmarkJobWriter.writeEpilogue(buffer)
content = str(buffer)
# Check structure
assert content.startswith("#")
assert "sbatch" in content
assert "SLURM" in content
assert "exit" in content
@pytest.mark.unit
class TestScriptWriterFactoryMethods:
"""Test suite for ScriptWriter factory methods."""
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkNodeScript_creates_file(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkNodeScript creates node script file."""
ScriptWriter.writeBenchmarkNodeScript("node.sh", "/output")
mock_file.assert_called_once_with("/output/node.sh", "w")
mock_exec.assert_called_once_with("/output/node.sh")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkTaskScript_creates_file(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkTaskScript creates task script file."""
ScriptWriter.writeBenchmarkTaskScript("task.sh", "/output")
mock_file.assert_called_once_with("/output/task.sh", "w")
mock_exec.assert_called_once_with("/output/task.sh")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkJobScript_creates_file(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkJobScript creates job script file."""
ScriptWriter.writeBenchmarkJobScript("job.sh", "/output")
mock_file.assert_called_once_with("/output/job.sh", "w")
mock_exec.assert_called_once_with("/output/job.sh")
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkNodeScript_content(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkNodeScript writes docker-related content."""
ScriptWriter.writeBenchmarkNodeScript("node.sh", "/output")
written_content = mock_file().write.call_args[0][0]
assert "docker" in written_content
assert "usage" in written_content
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkTaskScript_content(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkTaskScript writes SLURM srun content."""
ScriptWriter.writeBenchmarkTaskScript("task.sh", "/output")
written_content = mock_file().write.call_args[0][0]
assert "srun" in written_content
assert "SLURM" in written_content
@patch("builtins.open", new_callable=mock_open)
@patch("Tensile.TensileBenchmarkClusterScripts.ScriptHelper.makeExecutable")
def test_writeBenchmarkJobScript_content(self, mock_exec, mock_file):
"""ScriptWriter.writeBenchmarkJobScript writes SLURM sbatch content."""
ScriptWriter.writeBenchmarkJobScript("job.sh", "/output")
written_content = mock_file().write.call_args[0][0]
assert "sbatch" in written_content
assert "array" in written_content