264 lines
7.7 KiB
Python
Executable file
264 lines
7.7 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Test script to validate ProfileManager without requiring NVIDIA hardware.
|
|
|
|
This script tests profile loading and validation using the example profile files.
|
|
It can run without pynvml/NVIDIA drivers installed since it only tests the
|
|
profile management layer.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
# Add parent directory to path to allow imports
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
# Mock pynvml before importing nvidia_oc modules
|
|
import unittest.mock as mock
|
|
sys.modules['pynvml'] = mock.MagicMock()
|
|
|
|
from nvidia_oc.core.profile import ProfileManager, ProfileConfig, ProfileValidationError
|
|
|
|
|
|
def test_load_profiles():
|
|
"""Test loading all example profiles."""
|
|
manager = ProfileManager()
|
|
profiles_dir = Path(__file__).parent / "profiles"
|
|
|
|
print("Testing profile loading...")
|
|
print(f"Profiles directory: {profiles_dir}\n")
|
|
|
|
# Test individual profile loading
|
|
test_files = ["balanced.yaml", "performance.yaml", "quiet.yaml", "auto.yaml"]
|
|
|
|
for filename in test_files:
|
|
filepath = profiles_dir / filename
|
|
try:
|
|
profile = manager.load(filepath)
|
|
print(f"✓ {filename}:")
|
|
print(f" Name: {profile.name}")
|
|
print(f" Description: {profile.description}")
|
|
print(f" Core offset: {profile.core_offset:+d} MHz")
|
|
print(f" Memory offset: {profile.memory_offset:+d} MHz")
|
|
print(f" Power limit: {profile.power_limit}%")
|
|
if profile.fan_curve:
|
|
print(f" Fan curve: {len(profile.fan_curve)} points")
|
|
for i, (temp, speed) in enumerate(profile.fan_curve):
|
|
print(f" {temp}°C → {speed}%")
|
|
else:
|
|
print(f" Fan curve: Automatic")
|
|
print()
|
|
except Exception as e:
|
|
print(f"✗ {filename}: FAILED")
|
|
print(f" Error: {e}\n")
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def test_list_profiles():
|
|
"""Test listing all profiles in directory."""
|
|
manager = ProfileManager()
|
|
profiles_dir = Path(__file__).parent / "profiles"
|
|
|
|
print("Testing profile directory listing...")
|
|
|
|
try:
|
|
profiles = manager.list_profiles(profiles_dir)
|
|
print(f"✓ Found {len(profiles)} profiles:")
|
|
for profile in profiles:
|
|
print(f" - {profile.name}: {profile.description}")
|
|
print()
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ Directory listing FAILED: {e}\n")
|
|
return False
|
|
|
|
|
|
def test_profile_validation():
|
|
"""Test profile validation with invalid data."""
|
|
print("Testing profile validation...")
|
|
|
|
# Test invalid core offset
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=300, # Exceeds ±200 MHz limit
|
|
memory_offset=0,
|
|
power_limit=100,
|
|
fan_curve=None
|
|
)
|
|
print("✗ Should have rejected core_offset=300")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected core_offset=300 (exceeds ±200 MHz)")
|
|
|
|
# Test invalid memory offset
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=1500, # Exceeds ±1000 MHz limit
|
|
power_limit=100,
|
|
fan_curve=None
|
|
)
|
|
print("✗ Should have rejected memory_offset=1500")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected memory_offset=1500 (exceeds ±1000 MHz)")
|
|
|
|
# Test invalid power limit
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=0,
|
|
power_limit=200, # Exceeds 150% limit
|
|
fan_curve=None
|
|
)
|
|
print("✗ Should have rejected power_limit=200")
|
|
return False
|
|
except Exception:
|
|
print("✓ Correctly rejected power_limit=200 (exceeds 150%)")
|
|
|
|
# Test invalid fan curve (unsorted)
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=0,
|
|
power_limit=100,
|
|
fan_curve=[(70, 70), (60, 50)] # Unsorted
|
|
)
|
|
print("✗ Should have rejected unsorted fan curve")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected unsorted fan curve")
|
|
|
|
# Test invalid fan curve (single point)
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=0,
|
|
power_limit=100,
|
|
fan_curve=[(60, 50)] # Need at least 2 points
|
|
)
|
|
print("✗ Should have rejected single-point fan curve")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected single-point fan curve")
|
|
|
|
# Test invalid fan curve (invalid temperature)
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=0,
|
|
power_limit=100,
|
|
fan_curve=[(20, 50), (80, 80)] # 20°C is below 30°C minimum
|
|
)
|
|
print("✗ Should have rejected temperature 20°C")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected temperature 20°C (below 30°C minimum)")
|
|
|
|
# Test invalid fan curve (invalid speed)
|
|
try:
|
|
ProfileConfig(
|
|
name="Invalid",
|
|
core_offset=0,
|
|
memory_offset=0,
|
|
power_limit=100,
|
|
fan_curve=[(60, 50), (80, 120)] # 120% exceeds 100% maximum
|
|
)
|
|
print("✗ Should have rejected fan speed 120%")
|
|
return False
|
|
except ProfileValidationError:
|
|
print("✓ Correctly rejected fan speed 120% (exceeds 100%)")
|
|
|
|
print()
|
|
return True
|
|
|
|
|
|
def test_profile_save_load():
|
|
"""Test profile save/load roundtrip."""
|
|
import tempfile
|
|
|
|
print("Testing profile save/load roundtrip...")
|
|
|
|
manager = ProfileManager()
|
|
|
|
# Create a test profile
|
|
original = ProfileConfig(
|
|
name="Test Profile",
|
|
description="This is a test",
|
|
core_offset=125,
|
|
memory_offset=625,
|
|
power_limit=110,
|
|
fan_curve=[(60, 45), (75, 75), (85, 95)]
|
|
)
|
|
|
|
# Save to temporary file
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
temp_path = Path(f.name)
|
|
|
|
try:
|
|
manager.save(original, temp_path)
|
|
print(f"✓ Saved profile to {temp_path}")
|
|
|
|
# Load it back
|
|
loaded = manager.load(temp_path)
|
|
print(f"✓ Loaded profile from {temp_path}")
|
|
|
|
# Verify all fields match
|
|
assert loaded.name == original.name
|
|
assert loaded.description == original.description
|
|
assert loaded.core_offset == original.core_offset
|
|
assert loaded.memory_offset == original.memory_offset
|
|
assert loaded.power_limit == original.power_limit
|
|
assert loaded.fan_curve == original.fan_curve
|
|
|
|
print(f"✓ All fields match after roundtrip")
|
|
print()
|
|
return True
|
|
|
|
finally:
|
|
# Clean up temp file
|
|
if temp_path.exists():
|
|
temp_path.unlink()
|
|
|
|
|
|
def main():
|
|
"""Run all validation tests."""
|
|
print("=" * 70)
|
|
print("ProfileManager Validation Tests")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
tests = [
|
|
test_load_profiles,
|
|
test_list_profiles,
|
|
test_profile_validation,
|
|
test_profile_save_load,
|
|
]
|
|
|
|
results = []
|
|
for test in tests:
|
|
try:
|
|
result = test()
|
|
results.append(result)
|
|
except Exception as e:
|
|
print(f"✗ Test {test.__name__} crashed: {e}\n")
|
|
results.append(False)
|
|
|
|
print("=" * 70)
|
|
passed = sum(results)
|
|
total = len(results)
|
|
print(f"Results: {passed}/{total} tests passed")
|
|
print("=" * 70)
|
|
|
|
return 0 if all(results) else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|