@@ -683,10 +683,12 @@ def test_baseline(binary_handler_analyze_rocprof_compute):
|
||||
)
|
||||
assert code == 1
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Test cases for Parser.py
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_dependency_MI100(binary_handler_analyze_rocprof_compute):
|
||||
for dir in indirs:
|
||||
@@ -697,229 +699,256 @@ def test_dependency_MI100(binary_handler_analyze_rocprof_compute):
|
||||
assert code == 0
|
||||
test_utils.clean_output_dir(config["cleanup"], workload_dir)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_parser_utility_functions():
|
||||
"""Test parser utility functions edge cases"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import to_min, to_max, to_avg, to_median, to_std, to_int, to_quantile, to_round, to_mod, to_concat
|
||||
import pandas as pd
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from utils.parser import (
|
||||
to_avg,
|
||||
to_concat,
|
||||
to_int,
|
||||
to_max,
|
||||
to_median,
|
||||
to_min,
|
||||
to_mod,
|
||||
to_quantile,
|
||||
to_round,
|
||||
to_std,
|
||||
)
|
||||
|
||||
try:
|
||||
result = to_min(None, None)
|
||||
result = to_min(None, None)
|
||||
assert np.isnan(result), "to_min with all None should return nan"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
result = to_min(None, 5)
|
||||
assert False, "Should have crashed"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
result = to_min(7, 3, 9, 1)
|
||||
assert result == 1, "to_min should return minimum value"
|
||||
|
||||
|
||||
try:
|
||||
result = to_max(None, None)
|
||||
assert np.isnan(result), "to_max with all None should return nan"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
result = to_max(None, 5)
|
||||
assert False, "Should have crashed"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
result = to_max(7, 3, 9, 1)
|
||||
assert result == 9, "to_max should return maximum value"
|
||||
|
||||
|
||||
result = to_median(None)
|
||||
assert result is None, "to_median should return None for None input"
|
||||
|
||||
|
||||
try:
|
||||
to_median("invalid_string")
|
||||
assert False, "to_median should raise exception for invalid type"
|
||||
except Exception as e:
|
||||
assert "unsupported type" in str(e)
|
||||
|
||||
|
||||
try:
|
||||
to_std("invalid_string")
|
||||
assert False, "to_std should raise exception for invalid type"
|
||||
except Exception as e:
|
||||
assert "unsupported type" in str(e)
|
||||
|
||||
|
||||
result = to_int(None)
|
||||
assert result is None, "to_int should return None for None input"
|
||||
|
||||
|
||||
try:
|
||||
to_int(["list", "not", "supported"])
|
||||
assert False, "to_int should raise exception for invalid type"
|
||||
except Exception as e:
|
||||
assert "unsupported type" in str(e)
|
||||
|
||||
|
||||
result = to_quantile(None, 0.5)
|
||||
assert result is None, "to_quantile should return None for None input"
|
||||
|
||||
|
||||
try:
|
||||
to_quantile("invalid_string", 0.5)
|
||||
assert False, "to_quantile should raise exception for invalid type"
|
||||
except Exception as e:
|
||||
assert "unsupported type" in str(e)
|
||||
|
||||
|
||||
result = to_concat("hello", "world")
|
||||
assert result == "helloworld", "to_concat should concatenate strings"
|
||||
|
||||
|
||||
result = to_concat(123, 456)
|
||||
assert result == "123456", "to_concat should convert to strings and concatenate"
|
||||
|
||||
|
||||
series = pd.Series([1.234, 2.567, 3.890])
|
||||
result = to_round(series, 2)
|
||||
expected = pd.Series([1.23, 2.57, 3.89])
|
||||
pd.testing.assert_series_equal(result, expected)
|
||||
|
||||
|
||||
result = to_round(3.14159, 2)
|
||||
assert result == 3.14, "to_round should round scalar values"
|
||||
|
||||
|
||||
series = pd.Series([10, 15, 20])
|
||||
result = to_mod(series, 3)
|
||||
expected = pd.Series([1, 0, 2])
|
||||
pd.testing.assert_series_equal(result, expected)
|
||||
|
||||
|
||||
result = to_mod(10, 3)
|
||||
assert result == 1, "to_mod should return modulo for scalars"
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_parser_error_handling():
|
||||
"""Test parser error handling paths"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import build_eval_string, update_denom_string, calc_builtin_var
|
||||
|
||||
|
||||
from utils.parser import build_eval_string, calc_builtin_var, update_denom_string
|
||||
|
||||
try:
|
||||
build_eval_string("AVG(SQ_WAVES)", None)
|
||||
assert False, "Should have raised exception for None coll_level"
|
||||
except Exception as e:
|
||||
assert "coll_level can not be None" in str(e)
|
||||
|
||||
|
||||
assert build_eval_string("", "pmc_perf") == ""
|
||||
assert update_denom_string("", "per_wave") == ""
|
||||
|
||||
|
||||
class MockSysInfo:
|
||||
total_l2_chan = 32
|
||||
|
||||
|
||||
sys_info = MockSysInfo()
|
||||
try:
|
||||
calc_builtin_var("$unsupported_var", sys_info)
|
||||
assert False, "Should have raised exception for unsupported var"
|
||||
except SystemExit:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_parser_error_handling():
|
||||
"""Test parser error handling paths"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import build_eval_string, update_denom_string, calc_builtin_var
|
||||
|
||||
|
||||
from utils.parser import build_eval_string, calc_builtin_var, update_denom_string
|
||||
|
||||
try:
|
||||
build_eval_string("AVG(SQ_WAVES)", None)
|
||||
assert False, "Should have raised exception for None coll_level"
|
||||
except Exception as e:
|
||||
assert "coll_level can not be None" in str(e)
|
||||
|
||||
|
||||
assert build_eval_string("", "pmc_perf") == ""
|
||||
assert update_denom_string("", "per_wave") == ""
|
||||
|
||||
|
||||
class MockSysInfo:
|
||||
total_l2_chan = 32
|
||||
|
||||
|
||||
sys_info = MockSysInfo()
|
||||
try:
|
||||
calc_builtin_var("$unsupported_var", sys_info)
|
||||
assert False, "Should have raised exception for unsupported var"
|
||||
except SystemExit:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_missing_file_handling(binary_handler_analyze_rocprof_compute):
|
||||
"""Test handling of missing files"""
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
import tempfile
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
code = binary_handler_analyze_rocprof_compute(
|
||||
["analyze", "--path", temp_dir]
|
||||
)
|
||||
code = binary_handler_analyze_rocprof_compute(["analyze", "--path", temp_dir])
|
||||
assert code != 0
|
||||
|
||||
@pytest.mark.misc
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_ast_transformer_edge_cases():
|
||||
"""Simplified test focusing on the actual code paths"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import CodeTransformer
|
||||
|
||||
import ast
|
||||
|
||||
|
||||
from utils.parser import CodeTransformer
|
||||
|
||||
transformer = CodeTransformer()
|
||||
|
||||
|
||||
unknown_call = ast.Call(
|
||||
func=ast.Name(id='UNKNOWN_FUNCTION', ctx=ast.Load()),
|
||||
args=[ast.Constant(value=5) if hasattr(ast, 'Constant') else ast.Num(n=5)],
|
||||
keywords=[]
|
||||
func=ast.Name(id="UNKNOWN_FUNCTION", ctx=ast.Load()),
|
||||
args=[ast.Constant(value=5) if hasattr(ast, "Constant") else ast.Num(n=5)],
|
||||
keywords=[],
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
result = transformer.visit_Call(unknown_call)
|
||||
if hasattr(result.func, 'id') and result.func.id == 'UNKNOWN_FUNCTION':
|
||||
if hasattr(result.func, "id") and result.func.id == "UNKNOWN_FUNCTION":
|
||||
assert False, "Function name should have been changed or exception raised"
|
||||
except Exception as e:
|
||||
assert "Unknown call" in str(e), f"Expected 'Unknown call' in error, got: {str(e)}"
|
||||
|
||||
assert "Unknown call" in str(
|
||||
e
|
||||
), f"Expected 'Unknown call' in error, got: {str(e)}"
|
||||
|
||||
supported_call = ast.Call(
|
||||
func=ast.Name(id='MIN', ctx=ast.Load()),
|
||||
args=[ast.Constant(value=5) if hasattr(ast, 'Constant') else ast.Num(n=5)],
|
||||
keywords=[]
|
||||
func=ast.Name(id="MIN", ctx=ast.Load()),
|
||||
args=[ast.Constant(value=5) if hasattr(ast, "Constant") else ast.Num(n=5)],
|
||||
keywords=[],
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
result = transformer.visit_Call(supported_call)
|
||||
assert result.func.id == 'to_min', f"Expected 'to_min', got: {result.func.id}"
|
||||
assert result.func.id == "to_min", f"Expected 'to_min', got: {result.func.id}"
|
||||
except Exception as e:
|
||||
assert False, f"Supported function call should not raise exception: {e}"
|
||||
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_analyze_with_debug_mode(binary_handler_analyze_rocprof_compute):
|
||||
"""Test analyze to cover debug paths in eval_metric - using direct function call"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import eval_metric
|
||||
import pandas as pd
|
||||
|
||||
import numpy as np
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from utils.parser import eval_metric
|
||||
|
||||
mock_dfs = {
|
||||
1: pd.DataFrame({
|
||||
'Metric_ID': ['1.1.0'],
|
||||
'Metric': ['Test Metric'],
|
||||
'Expr': ['AVG(SQ_WAVES)'],
|
||||
'coll_level': ['pmc_perf']
|
||||
}).set_index('Metric_ID')
|
||||
1: pd.DataFrame(
|
||||
{
|
||||
"Metric_ID": ["1.1.0"],
|
||||
"Metric": ["Test Metric"],
|
||||
"Expr": ["AVG(SQ_WAVES)"],
|
||||
"coll_level": ["pmc_perf"],
|
||||
}
|
||||
).set_index("Metric_ID")
|
||||
}
|
||||
|
||||
mock_dfs_type = {1: 'metric_table'}
|
||||
|
||||
|
||||
mock_dfs_type = {1: "metric_table"}
|
||||
|
||||
class MockSysInfo:
|
||||
ip_blocks = "standard"
|
||||
se_per_gpu = 4
|
||||
@@ -937,41 +966,43 @@ def test_analyze_with_debug_mode(binary_handler_analyze_rocprof_compute):
|
||||
total_l2_chan = 32
|
||||
num_xcd = 1
|
||||
wave_size = 64
|
||||
|
||||
|
||||
sys_info = MockSysInfo()
|
||||
|
||||
|
||||
raw_pmc_df = {
|
||||
'pmc_perf': pd.DataFrame({
|
||||
'SQ_WAVES': [100, 200, 150],
|
||||
'GRBM_GUI_ACTIVE': [1000, 2000, 1500],
|
||||
'End_Timestamp': [1000000, 2000000, 1500000],
|
||||
'Start_Timestamp': [0, 1000000, 500000]
|
||||
})
|
||||
"pmc_perf": pd.DataFrame(
|
||||
{
|
||||
"SQ_WAVES": [100, 200, 150],
|
||||
"GRBM_GUI_ACTIVE": [1000, 2000, 1500],
|
||||
"End_Timestamp": [1000000, 2000000, 1500000],
|
||||
"Start_Timestamp": [0, 1000000, 500000],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
eval_metric(mock_dfs, mock_dfs_type, sys_info, raw_pmc_df, debug=True)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
@pytest.mark.misc
|
||||
def test_filter_combinations_coverage(binary_handler_analyze_rocprof_compute):
|
||||
"""Test basic filters that should work"""
|
||||
for dir in ["tests/workloads/vcopy/MI100", "tests/workloads/vcopy/MI200"]:
|
||||
if os.path.exists(dir):
|
||||
workload_dir = test_utils.setup_workload_dir(dir)
|
||||
|
||||
|
||||
code = binary_handler_analyze_rocprof_compute(
|
||||
["analyze", "--path", workload_dir]
|
||||
)
|
||||
assert code == 0
|
||||
|
||||
|
||||
code = binary_handler_analyze_rocprof_compute(
|
||||
["analyze", "--path", workload_dir, "--block", "SQ"]
|
||||
)
|
||||
assert code == 0
|
||||
|
||||
|
||||
test_utils.clean_output_dir(config["cleanup"], workload_dir)
|
||||
break
|
||||
|
||||
@@ -981,61 +1012,70 @@ def test_apply_filters_direct():
|
||||
"""Test apply_filters function directly to cover filter branches"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import apply_filters
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
from utils.parser import apply_filters
|
||||
|
||||
class MockWorkload:
|
||||
def __init__(self):
|
||||
self.raw_pmc = pd.DataFrame({
|
||||
('pmc_perf', 'GPU_ID'): [0, 0, 1, 1],
|
||||
('pmc_perf', 'Kernel_Name'): ['vecCopy', 'vecAdd', 'vecCopy', 'vecMul'],
|
||||
('pmc_perf', 'Dispatch_ID'): [0, 1, 2, 3],
|
||||
('pmc_perf', 'Node'): ['node0', 'node0', 'node1', 'node1']
|
||||
})
|
||||
self.raw_pmc = pd.DataFrame(
|
||||
{
|
||||
("pmc_perf", "GPU_ID"): [0, 0, 1, 1],
|
||||
("pmc_perf", "Kernel_Name"): [
|
||||
"vecCopy",
|
||||
"vecAdd",
|
||||
"vecCopy",
|
||||
"vecMul",
|
||||
],
|
||||
("pmc_perf", "Dispatch_ID"): [0, 1, 2, 3],
|
||||
("pmc_perf", "Node"): ["node0", "node0", "node1", "node1"],
|
||||
}
|
||||
)
|
||||
self.raw_pmc.columns = pd.MultiIndex.from_tuples(self.raw_pmc.columns)
|
||||
|
||||
|
||||
filter_nodes = None
|
||||
filter_gpu_ids = None
|
||||
filter_gpu_ids = None
|
||||
filter_kernel_ids = None
|
||||
filter_dispatch_ids = None
|
||||
|
||||
|
||||
workload = MockWorkload()
|
||||
|
||||
|
||||
workload.filter_gpu_ids = "0"
|
||||
result = apply_filters(workload, "/tmp", False, False)
|
||||
assert len(result) == 2
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
workload.filter_gpu_ids = None
|
||||
workload.filter_kernel_ids = ["vecCopy"]
|
||||
result = apply_filters(workload, "/tmp", False, False)
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
workload.filter_kernel_ids = None
|
||||
workload.filter_dispatch_ids = ["0", "1"]
|
||||
result = apply_filters(workload, "/tmp", False, False)
|
||||
assert len(result) == 2
|
||||
assert len(result) == 2
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_missing_files_scenarios(binary_handler_analyze_rocprof_compute):
|
||||
"""Test scenarios with missing files to cover error paths"""
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
import tempfile
|
||||
|
||||
for dir in ["tests/workloads/vcopy/MI100", "tests/workloads/vcopy/MI200"]:
|
||||
if os.path.exists(dir):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
workload_dir = os.path.join(temp_dir, "incomplete_workload")
|
||||
shutil.copytree(dir, workload_dir)
|
||||
|
||||
|
||||
csv_files = ["pmc_perf_1.csv", "pmc_perf_2.csv", "timestamps.csv"]
|
||||
for csv_file in csv_files:
|
||||
csv_path = os.path.join(workload_dir, csv_file)
|
||||
csv_path = os.path.join(workload_dir, csv_file)
|
||||
if os.path.exists(csv_path):
|
||||
os.remove(csv_path)
|
||||
|
||||
|
||||
code = binary_handler_analyze_rocprof_compute(
|
||||
["analyze", "--path", workload_dir]
|
||||
)
|
||||
@@ -1047,27 +1087,29 @@ def test_pc_sampling_basic_coverage():
|
||||
"""Test PC sampling functions with minimal data"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from utils.parser import load_pc_sampling_data, search_pc_sampling_record
|
||||
|
||||
import tempfile
|
||||
|
||||
|
||||
from utils.parser import load_pc_sampling_data, search_pc_sampling_record
|
||||
|
||||
class MockWorkload:
|
||||
filter_kernel_ids = []
|
||||
|
||||
|
||||
workload = MockWorkload()
|
||||
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
result = load_pc_sampling_data(workload, temp_dir, "none", "count")
|
||||
assert result.empty
|
||||
|
||||
|
||||
result = load_pc_sampling_data(workload, temp_dir, "missing", "count")
|
||||
assert result.empty
|
||||
|
||||
|
||||
workload.filter_kernel_ids = [0, 1, 2] # Multiple kernels
|
||||
result = load_pc_sampling_data(workload, temp_dir, "test", "count")
|
||||
assert result.empty
|
||||
|
||||
|
||||
result = search_pc_sampling_record([])
|
||||
assert result is None
|
||||
|
||||
@@ -1077,26 +1119,27 @@ def test_build_dfs_edge_cases():
|
||||
"""Test build_dfs and gen_counter_list with various configurations"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
|
||||
from utils.parser import gen_counter_list
|
||||
|
||||
|
||||
visited, counters = gen_counter_list(None)
|
||||
assert not visited
|
||||
assert counters == []
|
||||
|
||||
|
||||
visited, counters = gen_counter_list(123)
|
||||
assert not visited
|
||||
assert counters == []
|
||||
|
||||
|
||||
visited, counters = gen_counter_list("AVG(SQ_WAVES + TCC_HIT)")
|
||||
assert visited
|
||||
assert "SQ_WAVES" in counters
|
||||
assert "TCC_HIT" in counters
|
||||
|
||||
|
||||
visited, counters = gen_counter_list("Start_Timestamp + End_Timestamp")
|
||||
assert visited
|
||||
|
||||
assert visited
|
||||
|
||||
visited, counters = gen_counter_list("INVALID SYNTAX !!!")
|
||||
assert not visited
|
||||
|
||||
@@ -1106,23 +1149,24 @@ def test_update_functions_coverage():
|
||||
"""Test update_denom_string and update_normUnit_string branches"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
|
||||
from utils.parser import update_denom_string, update_normUnit_string
|
||||
|
||||
|
||||
result = update_denom_string("AVG(SQ_WAVES / $denom)", "per_wave")
|
||||
assert "$denom" not in result
|
||||
assert "SQ_WAVES" in result
|
||||
|
||||
result = update_denom_string("AVG(DATA / $denom)", "per_cycle")
|
||||
|
||||
result = update_denom_string("AVG(DATA / $denom)", "per_cycle")
|
||||
assert "$GRBM_GUI_ACTIVE_PER_XCD" in result
|
||||
|
||||
|
||||
result = update_denom_string("AVG(DATA / $denom)", "per_second")
|
||||
assert "End_Timestamp - Start_Timestamp" in result
|
||||
|
||||
|
||||
result = update_denom_string("AVG(DATA / $denom)", "unsupported_unit")
|
||||
assert "$denom" in result
|
||||
|
||||
|
||||
result = update_normUnit_string("(Prefix + $normUnit)", "per_wave")
|
||||
assert "per wave" in result.lower()
|
||||
assert result[0].isupper()
|
||||
assert result[0].isupper()
|
||||
|
||||
@@ -23,31 +23,37 @@
|
||||
##############################################################################el
|
||||
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import logging
|
||||
import shutil
|
||||
import sys
|
||||
import logging
|
||||
from unittest.mock import Mock, patch, MagicMock, call
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, Mock, call, patch
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
logging.TRACE = logging.DEBUG - 5
|
||||
logging.addLevelName(logging.TRACE, "TRACE")
|
||||
|
||||
|
||||
def trace_logger(message, *args, **kwargs):
|
||||
logging.log(logging.TRACE, message, *args, **kwargs)
|
||||
|
||||
|
||||
setattr(logging, "trace", trace_logger)
|
||||
|
||||
from db_connector import DatabaseConnector
|
||||
|
||||
"""
|
||||
Tests for the DatabaseConnector class that tests almost methods with initialization,
|
||||
CSV import, database removal, and error handling.
|
||||
Tests for the DatabaseConnector class that tests almost methods with initialization,
|
||||
CSV import, database removal, and error handling.
|
||||
The tests use mocks instead of a real MongoDB server for speed and reliability.
|
||||
"""
|
||||
|
||||
|
||||
class TestDatabaseConnector:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_args_import(self):
|
||||
"""Mock arguments for import operation"""
|
||||
@@ -62,7 +68,7 @@ class TestDatabaseConnector:
|
||||
args.remove = False
|
||||
args.kernel_verbose = False
|
||||
return args
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_args_remove(self):
|
||||
"""Mock arguments for remove operation"""
|
||||
@@ -77,15 +83,15 @@ class TestDatabaseConnector:
|
||||
args.remove = True
|
||||
args.kernel_verbose = False
|
||||
return args
|
||||
|
||||
|
||||
def test_init(self, mock_args_import):
|
||||
"""Test DatabaseConnector initialization"""
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
|
||||
assert connector.args == mock_args_import
|
||||
assert isinstance(connector.cache, dict)
|
||||
assert len(connector.cache) == 0
|
||||
|
||||
|
||||
expected_connection_info = {
|
||||
"username": "test_user",
|
||||
"password": "test_pass",
|
||||
@@ -99,130 +105,140 @@ class TestDatabaseConnector:
|
||||
assert connector.interaction_type is None
|
||||
assert connector.client is None
|
||||
|
||||
@patch('db_connector.pd.read_csv')
|
||||
@patch('db_connector.Path')
|
||||
@patch("db_connector.pd.read_csv")
|
||||
@patch("db_connector.Path")
|
||||
def test_prep_import_success(self, mock_path, mock_read_csv, mock_args_import):
|
||||
"""Test successful prep_import"""
|
||||
# Setup mocks
|
||||
mock_path.return_value.joinpath.return_value = "/fake/path/sysinfo.csv"
|
||||
mock_path.return_value.is_file.return_value = True
|
||||
|
||||
mock_sysinfo = pd.DataFrame({
|
||||
'gpu_model': ['MI100 '],
|
||||
'workload_name': [' test_workload']
|
||||
})
|
||||
|
||||
mock_sysinfo = pd.DataFrame(
|
||||
{"gpu_model": ["MI100 "], "workload_name": [" test_workload"]}
|
||||
)
|
||||
mock_read_csv.return_value = mock_sysinfo
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
connector.prep_import()
|
||||
|
||||
|
||||
expected_db = "rocprofiler-compute_test_team_test_workload_MI100"
|
||||
assert connector.connection_info["db"] == expected_db
|
||||
|
||||
@patch('db_connector.pd.read_csv')
|
||||
@patch('db_connector.Path')
|
||||
@patch("db_connector.pd.read_csv")
|
||||
@patch("db_connector.Path")
|
||||
def test_prep_import_missing_file(self, mock_path, mock_read_csv, mock_args_import):
|
||||
"""Test prep_import when sysinfo.csv is missing"""
|
||||
mock_path.return_value.joinpath.return_value = "/fake/path/sysinfo.csv"
|
||||
mock_path.return_value.is_file.return_value = False
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)) as mock_console_error:
|
||||
|
||||
with patch(
|
||||
"db_connector.console_error", side_effect=SystemExit(1)
|
||||
) as mock_console_error:
|
||||
with pytest.raises(SystemExit):
|
||||
connector.prep_import()
|
||||
|
||||
|
||||
mock_console_error.assert_called_with(
|
||||
"database", "Unable to parse SoC and/or workload name from sysinfo.csv"
|
||||
)
|
||||
|
||||
@patch('db_connector.pd.read_csv')
|
||||
@patch('db_connector.Path')
|
||||
@patch("db_connector.pd.read_csv")
|
||||
@patch("db_connector.Path")
|
||||
def test_prep_import_key_error(self, mock_path, mock_read_csv, mock_args_import):
|
||||
"""Test prep_import when required fields are missing"""
|
||||
mock_path.return_value.joinpath.return_value = "/fake/path/sysinfo.csv"
|
||||
mock_path.return_value.is_file.return_value = True
|
||||
|
||||
mock_sysinfo = pd.DataFrame({'other_column': ['value']})
|
||||
|
||||
mock_sysinfo = pd.DataFrame({"other_column": ["value"]})
|
||||
mock_read_csv.return_value = mock_sysinfo
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)) as mock_console_error:
|
||||
|
||||
with patch(
|
||||
"db_connector.console_error", side_effect=SystemExit(1)
|
||||
) as mock_console_error:
|
||||
with pytest.raises(SystemExit):
|
||||
connector.prep_import()
|
||||
|
||||
|
||||
assert mock_console_error.called
|
||||
error_call = mock_console_error.call_args[0][0]
|
||||
assert "Outdated workload" in error_call
|
||||
|
||||
@patch('db_connector.tqdm')
|
||||
@patch('db_connector.os.listdir')
|
||||
@patch('db_connector.console_log')
|
||||
@patch('db_connector.console_warning')
|
||||
@patch('db_connector.kernel_name_shortener')
|
||||
@patch('db_connector.MongoClient')
|
||||
@patch('db_connector.pd.read_csv')
|
||||
def test_db_import_success(self, mock_read_csv, mock_mongo_client, mock_kernel_shortener,
|
||||
mock_console_warning, mock_console_log, mock_listdir,
|
||||
mock_tqdm, mock_args_import):
|
||||
@patch("db_connector.tqdm")
|
||||
@patch("db_connector.os.listdir")
|
||||
@patch("db_connector.console_log")
|
||||
@patch("db_connector.console_warning")
|
||||
@patch("db_connector.kernel_name_shortener")
|
||||
@patch("db_connector.MongoClient")
|
||||
@patch("db_connector.pd.read_csv")
|
||||
def test_db_import_success(
|
||||
self,
|
||||
mock_read_csv,
|
||||
mock_mongo_client,
|
||||
mock_kernel_shortener,
|
||||
mock_console_warning,
|
||||
mock_console_log,
|
||||
mock_listdir,
|
||||
mock_tqdm,
|
||||
mock_args_import,
|
||||
):
|
||||
"""Test successful database import"""
|
||||
mock_listdir.return_value = ['test_data.csv', 'empty_file.csv', 'non_csv.txt']
|
||||
mock_listdir.return_value = ["test_data.csv", "empty_file.csv", "non_csv.txt"]
|
||||
mock_tqdm.return_value = mock_listdir.return_value
|
||||
|
||||
test_df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
|
||||
mock_read_csv.side_effect = [
|
||||
test_df,
|
||||
pd.errors.EmptyDataError()
|
||||
]
|
||||
|
||||
|
||||
test_df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
|
||||
mock_read_csv.side_effect = [test_df, pd.errors.EmptyDataError()]
|
||||
|
||||
mock_client_instance = MagicMock()
|
||||
mock_db = MagicMock()
|
||||
mock_collection = MagicMock()
|
||||
mock_workload_db = MagicMock()
|
||||
mock_workload_col = MagicMock()
|
||||
|
||||
|
||||
mock_mongo_client.return_value = mock_client_instance
|
||||
mock_client_instance.__getitem__.side_effect = lambda x: {
|
||||
'rocprofiler-compute_test_team_test_workload_MI100': mock_db,
|
||||
'workload_names': mock_workload_db
|
||||
"rocprofiler-compute_test_team_test_workload_MI100": mock_db,
|
||||
"workload_names": mock_workload_db,
|
||||
}.get(x, mock_db)
|
||||
mock_db.__getitem__.return_value = mock_collection
|
||||
mock_workload_db.__getitem__.return_value = mock_workload_col
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
connector.connection_info["workload"] = "/fake/workload/path"
|
||||
connector.client = mock_client_instance
|
||||
|
||||
with patch.object(connector, 'prep_import') as mock_prep:
|
||||
|
||||
with patch.object(connector, "prep_import") as mock_prep:
|
||||
mock_prep.return_value = None
|
||||
connector.connection_info["db"] = "rocprofiler-compute_test_team_test_workload_MI100"
|
||||
|
||||
connector.connection_info["db"] = (
|
||||
"rocprofiler-compute_test_team_test_workload_MI100"
|
||||
)
|
||||
|
||||
connector.db_import()
|
||||
|
||||
|
||||
mock_collection.insert_many.assert_called_once()
|
||||
mock_workload_col.replace_one.assert_called_once()
|
||||
|
||||
@patch('db_connector.console_log')
|
||||
@patch("db_connector.console_log")
|
||||
def test_db_remove_success(self, mock_console_log, mock_args_remove):
|
||||
"""Test successful database removal"""
|
||||
mock_client = MagicMock()
|
||||
mock_db_to_remove = MagicMock()
|
||||
mock_workload_names_db = MagicMock()
|
||||
mock_names_col = MagicMock()
|
||||
|
||||
|
||||
mock_client.__getitem__.side_effect = lambda x: {
|
||||
'rocprofiler-compute_test_team_workload_mi100': mock_db_to_remove,
|
||||
'workload_names': mock_workload_names_db
|
||||
"rocprofiler-compute_test_team_workload_mi100": mock_db_to_remove,
|
||||
"workload_names": mock_workload_names_db,
|
||||
}[x]
|
||||
mock_workload_names_db.__getitem__.return_value = mock_names_col
|
||||
mock_db_to_remove.list_collection_names.return_value = ['col1', 'col2']
|
||||
|
||||
mock_db_to_remove.list_collection_names.return_value = ["col1", "col2"]
|
||||
|
||||
connector = DatabaseConnector(mock_args_remove)
|
||||
connector.client = mock_client
|
||||
|
||||
|
||||
connector.db_remove()
|
||||
|
||||
|
||||
mock_client.drop_database.assert_called_once_with(mock_db_to_remove)
|
||||
mock_names_col.delete_many.assert_called_once_with(
|
||||
{"name": "rocprofiler-compute_test_team_workload_mi100"}
|
||||
@@ -232,20 +248,20 @@ class TestDatabaseConnector:
|
||||
"""Test pre_processing when neither upload nor remove is specified"""
|
||||
mock_args_import.upload = False
|
||||
mock_args_import.remove = False
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
def test_pre_processing_remove_invalid_workload_name(self, mock_args_remove):
|
||||
"""Test pre_processing remove with invalid workload name"""
|
||||
mock_args_remove.workload = "invalid_name"
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_remove)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
@@ -253,106 +269,121 @@ class TestDatabaseConnector:
|
||||
"""Test pre_processing remove with missing host/username"""
|
||||
mock_args_remove.host = None
|
||||
mock_args_remove.username = None
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_remove)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
def test_pre_processing_remove_protected_database(self, mock_args_remove):
|
||||
"""Test pre_processing remove with protected database names"""
|
||||
mock_args_remove.workload = "admin"
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_remove)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
@patch('db_connector.Path')
|
||||
@patch('db_connector.is_workload_empty')
|
||||
@patch('db_connector.getpass.getpass')
|
||||
@patch('db_connector.console_log')
|
||||
@patch('db_connector.MongoClient')
|
||||
def test_pre_processing_import_password_prompt_success(self, mock_mongo_client, mock_console_log,
|
||||
mock_getpass, mock_is_workload_empty,
|
||||
mock_path, mock_args_import):
|
||||
@patch("db_connector.Path")
|
||||
@patch("db_connector.is_workload_empty")
|
||||
@patch("db_connector.getpass.getpass")
|
||||
@patch("db_connector.console_log")
|
||||
@patch("db_connector.MongoClient")
|
||||
def test_pre_processing_import_password_prompt_success(
|
||||
self,
|
||||
mock_mongo_client,
|
||||
mock_console_log,
|
||||
mock_getpass,
|
||||
mock_is_workload_empty,
|
||||
mock_path,
|
||||
mock_args_import,
|
||||
):
|
||||
"""Test pre_processing import with password prompt success"""
|
||||
mock_args_import.password = ""
|
||||
mock_getpass.return_value = "prompted_password"
|
||||
|
||||
|
||||
mock_path.return_value.absolute.return_value.is_dir.return_value = True
|
||||
mock_path.return_value.absolute.return_value.resolve.return_value = "/resolved/path"
|
||||
|
||||
mock_path.return_value.absolute.return_value.resolve.return_value = (
|
||||
"/resolved/path"
|
||||
)
|
||||
|
||||
mock_client_instance = MagicMock()
|
||||
mock_mongo_client.return_value = mock_client_instance
|
||||
mock_client_instance.server_info.return_value = {}
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
connector.pre_processing()
|
||||
|
||||
|
||||
mock_getpass.assert_called_once()
|
||||
mock_console_log.assert_called_with("database", "Password received")
|
||||
|
||||
@patch('db_connector.Path')
|
||||
@patch('db_connector.is_workload_empty')
|
||||
@patch('db_connector.MongoClient')
|
||||
def test_pre_processing_import_connection_failure(self, mock_mongo_client, mock_is_workload_empty,
|
||||
mock_path, mock_args_import):
|
||||
@patch("db_connector.Path")
|
||||
@patch("db_connector.is_workload_empty")
|
||||
@patch("db_connector.MongoClient")
|
||||
def test_pre_processing_import_connection_failure(
|
||||
self, mock_mongo_client, mock_is_workload_empty, mock_path, mock_args_import
|
||||
):
|
||||
"""Test pre_processing import with MongoDB connection failure"""
|
||||
mock_path.return_value.absolute.return_value.is_dir.return_value = True
|
||||
mock_path.return_value.absolute.return_value.resolve.return_value = "/resolved/path"
|
||||
|
||||
mock_path.return_value.absolute.return_value.resolve.return_value = (
|
||||
"/resolved/path"
|
||||
)
|
||||
|
||||
mock_client_instance = MagicMock()
|
||||
mock_mongo_client.return_value = mock_client_instance
|
||||
mock_client_instance.server_info.side_effect = Exception("Connection failed")
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
@patch('db_connector.Path')
|
||||
@patch('db_connector.is_workload_empty')
|
||||
def test_pre_processing_import_missing_required_fields(self, mock_is_workload_empty, mock_path, mock_args_import):
|
||||
@patch("db_connector.Path")
|
||||
@patch("db_connector.is_workload_empty")
|
||||
def test_pre_processing_import_missing_required_fields(
|
||||
self, mock_is_workload_empty, mock_path, mock_args_import
|
||||
):
|
||||
"""Test pre_processing import with missing required fields"""
|
||||
mock_args_import.host = None
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
@patch('db_connector.Path')
|
||||
def test_pre_processing_import_invalid_workload_path(self, mock_path, mock_args_import):
|
||||
@patch("db_connector.Path")
|
||||
def test_pre_processing_import_invalid_workload_path(
|
||||
self, mock_path, mock_args_import
|
||||
):
|
||||
"""Test pre_processing import with invalid workload path"""
|
||||
mock_path.return_value.absolute.return_value.is_dir.return_value = False
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
def test_pre_processing_import_team_name_too_long(self, mock_args_import):
|
||||
"""Test pre_processing import with team name exceeding limit"""
|
||||
mock_args_import.team = "this_team_name_is_way_too_long"
|
||||
|
||||
|
||||
connector = DatabaseConnector(mock_args_import)
|
||||
|
||||
with patch('db_connector.console_error', side_effect=SystemExit(1)):
|
||||
|
||||
with patch("db_connector.console_error", side_effect=SystemExit(1)):
|
||||
with pytest.raises(SystemExit):
|
||||
connector.pre_processing()
|
||||
|
||||
|
||||
class TestDatabaseConnectorIntegration:
|
||||
"""Simple integration test"""
|
||||
|
||||
@patch('db_connector.Path')
|
||||
@patch('db_connector.pd.read_csv')
|
||||
|
||||
@patch("db_connector.Path")
|
||||
@patch("db_connector.pd.read_csv")
|
||||
def test_prep_import_with_real_workload_path(self, mock_read_csv, mock_path):
|
||||
"""Test prep_import with actual workload path structure"""
|
||||
args = Mock()
|
||||
@@ -365,22 +396,23 @@ class TestDatabaseConnectorIntegration:
|
||||
args.upload = True
|
||||
args.remove = False
|
||||
args.kernel_verbose = False
|
||||
|
||||
mock_path.return_value.joinpath.return_value = "/app/tests/workloads/device_filter/MI100/sysinfo.csv"
|
||||
|
||||
mock_path.return_value.joinpath.return_value = (
|
||||
"/app/tests/workloads/device_filter/MI100/sysinfo.csv"
|
||||
)
|
||||
mock_path.return_value.is_file.return_value = True
|
||||
|
||||
mock_sysinfo = pd.DataFrame({
|
||||
'gpu_model': ['MI100'],
|
||||
'workload_name': ['device_filter']
|
||||
})
|
||||
|
||||
mock_sysinfo = pd.DataFrame(
|
||||
{"gpu_model": ["MI100"], "workload_name": ["device_filter"]}
|
||||
)
|
||||
mock_read_csv.return_value = mock_sysinfo
|
||||
|
||||
|
||||
connector = DatabaseConnector(args)
|
||||
connector.prep_import()
|
||||
|
||||
|
||||
expected_db = "rocprofiler-compute_test_team_device_filter_MI100"
|
||||
assert connector.connection_info["db"] == expected_db
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
pytest.main([__file__, "-v"])
|
||||
|
||||
@@ -22,19 +22,18 @@
|
||||
# SOFTWARE.
|
||||
##############################################################################el
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import pytest
|
||||
import yaml
|
||||
import tempfile
|
||||
import os
|
||||
from importlib.machinery import SourceFileLoader
|
||||
from unittest.mock import patch, mock_open, MagicMock
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, mock_open, patch
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from src.utils.specs import generate_machine_specs
|
||||
|
||||
@@ -201,185 +200,214 @@ def test_num_xcds_cli_output():
|
||||
assert compute_partition_actual is not None
|
||||
assert int(num_xcd_actual) == num_xcds.get(compute_partition_actual.lower(), -1)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_load_yaml_file_not_found():
|
||||
"""Test _load_yaml with non-existent file - covers lines 104-105"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
non_existent_path = "/path/that/does/not/exist/file.yaml"
|
||||
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs._load_yaml(non_existent_path)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_load_yaml_invalid_yaml():
|
||||
"""Test _load_yaml with corrupted YAML - covers lines 106-107"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
||||
f.write('invalid: yaml: content: [\nunclosed bracket')
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||||
f.write("invalid: yaml: content: [\nunclosed bracket")
|
||||
temp_path = f.name
|
||||
|
||||
|
||||
try:
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs._load_yaml(temp_path)
|
||||
finally:
|
||||
os.unlink(temp_path)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_load_yaml_generic_exception():
|
||||
"""Test _load_yaml generic exception handling - covers lines 108-111"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch('builtins.open', side_effect=PermissionError("Access denied")):
|
||||
|
||||
with patch("builtins.open", side_effect=PermissionError("Access denied")):
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs._load_yaml("some_file.yaml")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_series_dict_uninitialized():
|
||||
"""Test get_gpu_series_dict when dict not populated - covers lines 182-185"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_gpu_series_dict', {}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_gpu_series_dict", {}):
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs.get_gpu_series_dict()
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_series_uninitialized():
|
||||
"""Test get_gpu_series when dict not populated - covers lines 191-194"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_gpu_series_dict', {}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_gpu_series_dict", {}):
|
||||
with pytest.raises(SystemExit):
|
||||
result = MIGPUSpecs.get_gpu_series("gfx942")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_perfmon_config_uninitialized():
|
||||
"""Test get_perfmon_config when dict not populated - covers lines 210-213"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_perfmon_config', {}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_perfmon_config", {}):
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs.get_perfmon_config("gfx942")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_model_uninitialized():
|
||||
"""Test get_gpu_model when dict not populated - covers lines 223-226"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_gpu_model_dict', {}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_gpu_model_dict", {}):
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs.get_gpu_model("gfx942", "29857")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_model_invalid_chip_id():
|
||||
"""Test get_gpu_model with invalid chip_id - covers lines 235-236"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_gpu_model("gfx942", "99999")
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_model_invalid_arch():
|
||||
"""Test get_gpu_model with invalid architecture - covers lines 243-244"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_gpu_model("gfx999", "12345")
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_gpu_model_none_result():
|
||||
"""Test get_gpu_model when result is None - covers lines 246-248"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_chip_id_dict', {999: None}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_chip_id_dict", {999: None}):
|
||||
result = MIGPUSpecs.get_gpu_model("gfx942", "999")
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.misc
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_no_compute_partition_data():
|
||||
"""Test get_num_xcds when no compute partition data found - covers lines 307-309"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
mock_dict = {"gfx942": None}
|
||||
with patch.object(MIGPUSpecs, '_gpu_arch_to_compute_partition_dict', mock_dict):
|
||||
with patch.object(MIGPUSpecs, "_gpu_arch_to_compute_partition_dict", mock_dict):
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx942")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_uninitialized_dict():
|
||||
"""Test get_num_xcds when XCD dict not populated - covers lines 315-317"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_num_xcds_dict', {}):
|
||||
|
||||
with patch.object(MIGPUSpecs, "_num_xcds_dict", {}):
|
||||
with pytest.raises(SystemExit):
|
||||
MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="MI350")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_unknown_gpu_model():
|
||||
"""Test get_num_xcds with unknown gpu model - covers lines 319-321"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="UNKNOWN_MODEL")
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_no_compute_partition():
|
||||
"""Test get_num_xcds with no compute partition - covers lines 325-327"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="MI350", compute_partition="")
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(
|
||||
gpu_arch="gfx950", gpu_model="MI350", compute_partition=""
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_unknown_compute_partition():
|
||||
"""Test get_num_xcds with unknown compute partition - covers lines 329-332"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="MI350", compute_partition="UNKNOWN")
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(
|
||||
gpu_arch="gfx950", gpu_model="MI350", compute_partition="UNKNOWN"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_none_partition_value():
|
||||
"""Test get_num_xcds when partition value is None - covers lines 338-340"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
mock_dict = {"mi350": {"spx": None}}
|
||||
with patch.object(MIGPUSpecs, '_num_xcds_dict', mock_dict):
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="MI350", compute_partition="spx")
|
||||
with patch.object(MIGPUSpecs, "_num_xcds_dict", mock_dict):
|
||||
result = MIGPUSpecs.get_num_xcds(
|
||||
gpu_arch="gfx950", gpu_model="MI350", compute_partition="spx"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_no_gpu_model():
|
||||
"""Test get_num_xcds with no gpu model - covers line 342"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx950", gpu_model="", compute_partition="spx")
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(
|
||||
gpu_arch="gfx950", gpu_model="", compute_partition="spx"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_chip_id_dict_empty():
|
||||
"""Test get_chip_id_dict when dict is empty - covers line 352"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_chip_id_dict', {}):
|
||||
with patch('src.utils.mi_gpu_spec.console_error') as mock_error:
|
||||
|
||||
with patch.object(MIGPUSpecs, "_chip_id_dict", {}):
|
||||
with patch("src.utils.mi_gpu_spec.console_error") as mock_error:
|
||||
result = MIGPUSpecs.get_chip_id_dict()
|
||||
mock_error.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_get_num_xcds_dict_empty():
|
||||
"""Test get_num_xcds_dict when dict is empty - covers line 359"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
with patch.object(MIGPUSpecs, '_num_xcds_dict', {}):
|
||||
with patch('src.utils.mi_gpu_spec.console_error') as mock_error:
|
||||
|
||||
with patch.object(MIGPUSpecs, "_num_xcds_dict", {}):
|
||||
with patch("src.utils.mi_gpu_spec.console_error") as mock_error:
|
||||
result = MIGPUSpecs.get_num_xcds_dict()
|
||||
mock_error.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.misc
|
||||
def test_normal_functionality_still_works():
|
||||
"""Ensure that normal paths still work after adding error handling tests"""
|
||||
from src.utils.mi_gpu_spec import MIGPUSpecs
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_gpu_model("gfx906", None)
|
||||
assert result is not None
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_gpu_series("gfx906")
|
||||
assert result is not None
|
||||
|
||||
|
||||
result = MIGPUSpecs.get_num_xcds(gpu_arch="gfx906")
|
||||
assert result == 1
|
||||
assert result == 1
|
||||
|
||||
تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
Diff را بارگزاری کن
مرجع در شماره جدید
Block a user