nvidia-oc/backend/test_profile_validation.py
2026-01-14 12:30:45 -08:00

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())