import contextlib
from contextvars import ContextVar
import os
from unittest import mock
import unittest
from unittest.mock import MagicMock, patch
import torch
from torch import nn
from mindiesd.utils import ParametersInvalid, ConfigError
from mindiesd.quantization.utils import (
extract_constructor_args,
replace_rank_suffix,
get_quant_weight,
TimestepManager,
build_online_fa_rot_weights,
create_online_hadamard_rot,
get_mxfp4_quant_kwargs,
match_fa_layer,
match_layer_config,
match_layer_pattern,
match_layer_patterns,
)
from mindiesd.quantization.config import QuantConfig
class MockSafeTensorHandler:
def __init__(self, data):
self.data = data
def get_tensor(self, key):
return self.data.get(key, None)
def keys(self):
return self.data.keys()
def create_mock_handler(mock_data):
return MockSafeTensorHandler(mock_data)
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestGetInitParams(unittest.TestCase):
def test_extract_constructor_args(self):
class SampleClass:
def __init__(self, a, b, c=1):
self.a = a
self.b = b
self.c = c
obj = SampleClass(1, 2, 3)
expected_params = {'a': 1, 'b': 2, 'c': 3}
self.assertEqual(extract_constructor_args(obj), expected_params)
expected_params_with_class = {'a': 1, 'b': 2, 'c': 3}
self.assertEqual(extract_constructor_args(obj, SampleClass), expected_params_with_class)
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestReplaceRankSuffix(unittest.TestCase):
def test_no_rank_suffix(self):
file_path = "/path/to/config.json"
file_name = "config.json"
new_path, new_file_name, rank = replace_rank_suffix(file_path)
self.assertEqual(new_path, file_path)
self.assertEqual(new_file_name, file_name)
self.assertEqual(rank, -1)
def test_with_underscore_not_rank(self):
file_path = "/path/to/config_name.json"
file_name = "config_name.json"
new_path, new_file_name, rank = replace_rank_suffix(file_path)
self.assertEqual(new_path, file_path)
self.assertEqual(new_file_name, file_name)
self.assertEqual(rank, -1)
@patch('torch.distributed.is_initialized')
@patch('torch.distributed.get_rank')
def test_with_rank_suffix_dist_initialized(self, mock_get_rank, mock_is_initialized):
mock_is_initialized.return_value = True
mock_get_rank.return_value = 3
file_path = "/path/to/config_0.json"
expected_path = "/path/to/config_3.json"
file_name = "config_3.json"
new_path, new_file_name, rank = replace_rank_suffix(file_path)
self.assertEqual(new_path, expected_path)
self.assertEqual(new_file_name, file_name)
self.assertEqual(rank, 3)
mock_is_initialized.assert_called_once()
mock_get_rank.assert_called_once()
@patch('torch.distributed.is_initialized')
def test_with_rank_suffix_dist_not_initialized(self, mock_is_initialized):
mock_is_initialized.return_value = False
file_path = "/path/to/config_0.json"
with self.assertRaises(ConfigError):
replace_rank_suffix(file_path)
mock_is_initialized.assert_called_once()
@patch('torch.distributed.is_initialized')
@patch('torch.distributed.get_rank')
def test_complex_path_with_rank_suffix(self, mock_get_rank, mock_is_initialized):
mock_is_initialized.return_value = True
mock_get_rank.return_value = 2
file_path = "/complex/path/with_underscores/config_file_1.json"
expected_path = "/complex/path/with_underscores/config_file_2.json"
file_name = "config_file_2.json"
new_path, new_file_name, rank = replace_rank_suffix(file_path)
self.assertEqual(new_path, expected_path)
self.assertEqual(new_file_name, file_name)
self.assertEqual(rank, 2)
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestGetQuantWeight(unittest.TestCase):
"""测试get_quant_weight函数所有分支"""
def test_normal_case(self):
"""测试正常获取量化权重"""
mock_tensor = MagicMock()
weights = {'valid_key': mock_tensor}
result = get_quant_weight(create_mock_handler(weights), 'valid_key')
self.assertEqual(result, mock_tensor)
def test_key_not_exist(self):
"""测试键不存在的情况"""
invalid_weights = {'other_key': 'value'}
with self.assertRaises(ParametersInvalid) as cm:
get_quant_weight(create_mock_handler(invalid_weights), 'missing_key')
self.assertIn("Critical parameter missing: missing_key.", str(cm.exception))
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestTimestepManager(unittest.TestCase):
def test_set_and_get(self):
"""测试正常设置和获取时间步索引"""
TimestepManager.set_timestep_idx_max(3)
TimestepManager.set_timestep_idx(3)
self.assertEqual(TimestepManager.get_timestep_idx(), 3)
def test_multiple_sets(self):
"""测试多次设置不同的值,确保最后的值正确"""
TimestepManager.set_timestep_idx_max(20)
TimestepManager.set_timestep_idx(10)
TimestepManager.set_timestep_idx(20)
self.assertEqual(TimestepManager.get_timestep_idx(), 20)
self.assertEqual(TimestepManager.get_timestep_idx_max(), 20)
def test_set_timestep_idx_exceeds_max(self):
"""测试多次设置不同的值,确保最后的值正确"""
TimestepManager.set_timestep_idx_max(5)
with self.assertRaises(ParametersInvalid):
TimestepManager.set_timestep_idx(10)
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestLayerPatternMatcher(unittest.TestCase):
def test_match_exact_wildcard_and_brace_patterns(self):
self.assertTrue(match_layer_pattern("blocks.0.attn.proj", "blocks.0.attn.proj"))
self.assertTrue(match_layer_pattern("blocks.0.attn.proj", "blocks.*.attn.*"))
self.assertTrue(match_layer_pattern("blocks.2.attn.proj", "blocks.{1,2}.attn.proj"))
self.assertFalse(match_layer_pattern("blocks.3.attn.proj", "blocks.{1,2}.attn.proj"))
def test_match_layer_config_prefers_exact_match(self):
cfg = {"blocks.*.proj": "wildcard", "blocks.0.proj": "exact"}
self.assertEqual(match_layer_config("blocks.0.proj", cfg), "exact")
self.assertEqual(match_layer_config("blocks.1.proj", cfg), "wildcard")
self.assertIsNone(match_layer_config("blocks.1.fc", cfg))
def test_match_layer_patterns(self):
self.assertTrue(match_layer_patterns("blocks.1.attn", ("blocks.{0,1}.attn",)))
self.assertFalse(match_layer_patterns("blocks.2.attn", ("blocks.{0,1}.attn",)))
def test_match_fa_layer_matches_name_and_class(self):
class FakeAttention(nn.Module):
pass
layer = FakeAttention()
self.assertTrue(match_fa_layer("blocks.0.self_attn", layer, ("blocks.*.self_attn",)))
self.assertTrue(match_fa_layer("blocks.0.self_attn", layer, ("Fake{Attention,Block}",)))
self.assertFalse(match_fa_layer("blocks.0.self_attn", layer, ("OtherAttention",)))
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestOnlineFARotUtils(unittest.TestCase):
def test_create_online_hadamard_rot_is_reproducible(self):
rot1 = create_online_hadamard_rot(8, dtype=torch.float32, seed=1234)
rot2 = create_online_hadamard_rot(8, dtype=torch.float32, seed=1234)
self.assertTrue(torch.equal(rot1, rot2))
self.assertEqual(rot1.shape, (8, 8))
def test_build_online_fa_rot_weights_generates_matching_qk_rot(self):
class FakeAttention(nn.Module):
head_dim = 8
weights = build_online_fa_rot_weights("blocks.0.attn", FakeAttention(), dtype=torch.float32)
q_rot = weights.get_tensor("blocks.0.attn.q_rot")
k_rot = weights.get_tensor("blocks.0.attn.k_rot")
self.assertTrue(torch.equal(q_rot, k_rot))
self.assertEqual(q_rot.dtype, torch.float32)
self.assertEqual(q_rot.shape, (8, 8))
@unittest.skipIf(
os.environ.get("MINDIE_TEST_MODE", "ALL") == "NPU", "Skip CPU-compatible tests when MINDIE_TEST_MODE is NPU."
)
class TestMXFP4QuantKwargs(unittest.TestCase):
def test_get_mxfp4_quant_kwargs_uses_offline_style_config(self):
cfg = QuantConfig(mxfp4_scale_alg=2, mxfp4_dst_type_max=7.25)
self.assertEqual(get_mxfp4_quant_kwargs(cfg), {"scale_alg": 2, "dst_type_max": 7.25})
def test_get_mxfp4_quant_kwargs_empty_without_scale_alg(self):
cfg = QuantConfig(mxfp4_dst_type_max=7.25)
self.assertEqual(get_mxfp4_quant_kwargs(cfg), {})
if __name__ == '__main__':
unittest.main()