This release includes comprehensive architecture refactoring, WanVideoWrapper integration, critical bug fixes, and extensive testing improvements. Major changes: - Modular architecture with shared utilities (triple_ksampler/ package) - WanVideoWrapper integration nodes (TripleWVSampler variants) - Critical fix for WanVideo sample passing (issue aec0e66) - Debug logging improvements - Comprehensive documentation updates - Test infrastructure improvements (unit/integration/regression separation) - 314 total tests (204 unit + 110 integration/regression) See CHANGELOG.md for detailed release notes spanning v0.9.4 through v0.10.4.
178 lines
7.1 KiB
Python
178 lines
7.1 KiB
Python
"""
|
|
Tests for CFG parameter behavior in TripleKSampler nodes.
|
|
|
|
This test would have caught the CFG bug introduced during refactoring!
|
|
The issue was that all existing tests used lightning_cfg=1.0, so when we
|
|
accidentally hardcoded Stage 3 to always use 1.0, tests still passed.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
# Import from conftest (classes are loaded there)
|
|
from conftest import COMFYUI_AVAILABLE, TripleKSampler, TripleKSamplerAdvanced
|
|
|
|
|
|
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
|
|
class TestCFGBehavior:
|
|
"""Test CFG parameter behavior - this would have caught the refactoring bug!"""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.advanced_node = TripleKSamplerAdvanced()
|
|
self.simple_node = TripleKSampler()
|
|
|
|
# Create model mocks
|
|
self.mock_base_high = MagicMock()
|
|
self.mock_lightning_high = MagicMock()
|
|
self.mock_lightning_low = MagicMock()
|
|
|
|
# Add proper model configurations
|
|
for model in [self.mock_base_high, self.mock_lightning_high, self.mock_lightning_low]:
|
|
model.model.model_config.sampling_settings = {"shift": 1.0, "multiplier": 1000}
|
|
model.clone.return_value = model
|
|
|
|
# Add proper mock sampling for boundary calculations
|
|
mock_sampling = MagicMock()
|
|
mock_sampling.timestep.side_effect = lambda sigma: 850.0 if sigma < 2.5 else 950.0
|
|
self.mock_lightning_high.get_model_object.return_value = mock_sampling
|
|
|
|
self.mock_positive = MagicMock()
|
|
self.mock_negative = MagicMock()
|
|
|
|
# Create a proper mock tensor with real device/dtype for dry run
|
|
import torch
|
|
|
|
mock_tensor = torch.zeros((1, 4, 8, 8))
|
|
self.mock_latent = {"samples": mock_tensor}
|
|
|
|
def test_different_lightning_cfg_values_work(self):
|
|
"""
|
|
This test would have caught the CFG bug!
|
|
|
|
Tests that Advanced node can use different lightning_cfg values.
|
|
All existing tests used lightning_cfg=1.0, so when we accidentally
|
|
hardcoded Stage 3 to always use 1.0, the tests still passed.
|
|
"""
|
|
# Test multiple different lightning_cfg values to ensure they work
|
|
test_values = [0.5, 1.5, 2.0, 3.0]
|
|
|
|
for lightning_cfg_value in test_values:
|
|
params = {
|
|
"base_high": self.mock_base_high,
|
|
"lightning_high": self.mock_lightning_high,
|
|
"lightning_low": self.mock_lightning_low,
|
|
"positive": self.mock_positive,
|
|
"negative": self.mock_negative,
|
|
"latent_image": self.mock_latent,
|
|
"seed": 42,
|
|
"sigma_shift": 5.0,
|
|
"base_steps": 3,
|
|
"base_quality_threshold": 20,
|
|
"base_cfg": 3.5,
|
|
"lightning_start": 1,
|
|
"lightning_steps": 8,
|
|
"lightning_cfg": lightning_cfg_value, # This is the key test!
|
|
"base_sampler": "euler",
|
|
"base_scheduler": "simple",
|
|
"lightning_sampler": "euler",
|
|
"lightning_scheduler": "simple",
|
|
"switch_strategy": "50% of steps",
|
|
"switch_step": -1,
|
|
"switch_boundary": 0.875,
|
|
"dry_run": True,
|
|
}
|
|
|
|
# This should work without errors for any lightning_cfg value
|
|
# dry_run now raises InterruptProcessingException
|
|
import comfy.model_management
|
|
|
|
with pytest.raises(comfy.model_management.InterruptProcessingException):
|
|
self.advanced_node.sample(**params)
|
|
|
|
# If the hardcoded bug existed, this test would fail when lightning_cfg != 1.0
|
|
# because the logic would break with unexpected CFG values
|
|
|
|
def test_simple_node_parameter_exclusion(self):
|
|
"""Test that Simple node doesn't expose lightning_cfg parameter."""
|
|
# Simple node should not have lightning_cfg in its INPUT_TYPES
|
|
input_types = self.simple_node.INPUT_TYPES()
|
|
|
|
required_params = input_types.get("required", {})
|
|
optional_params = input_types.get("optional", {})
|
|
|
|
assert "lightning_cfg" not in required_params, (
|
|
"Simple node should not expose lightning_cfg as required parameter"
|
|
)
|
|
assert "lightning_cfg" not in optional_params, (
|
|
"Simple node should not expose lightning_cfg as optional parameter"
|
|
)
|
|
|
|
def test_advanced_node_parameter_inclusion(self):
|
|
"""Test that Advanced node properly exposes lightning_cfg parameter."""
|
|
# Advanced node should have lightning_cfg in its INPUT_TYPES
|
|
input_types = self.advanced_node.INPUT_TYPES()
|
|
|
|
required_params = input_types.get("required", {})
|
|
|
|
assert "lightning_cfg" in required_params, (
|
|
"Advanced node should expose lightning_cfg as required parameter"
|
|
)
|
|
|
|
# Check the parameter configuration
|
|
lightning_cfg_config = required_params["lightning_cfg"]
|
|
assert isinstance(lightning_cfg_config, tuple), (
|
|
"lightning_cfg should be properly configured"
|
|
)
|
|
assert lightning_cfg_config[0] == "FLOAT", "lightning_cfg should be a FLOAT parameter"
|
|
assert isinstance(lightning_cfg_config[1], dict), (
|
|
"lightning_cfg should have configuration dict"
|
|
)
|
|
assert lightning_cfg_config[1]["default"] == 1.0, "lightning_cfg should default to 1.0"
|
|
|
|
def test_cfg_parameter_differentiation_smoke_test(self):
|
|
"""
|
|
Smoke test to ensure base_cfg and lightning_cfg can be different.
|
|
|
|
This doesn't verify the actual CFG values used internally (that would
|
|
require complex mocking), but it ensures the parameters can be different
|
|
without causing crashes.
|
|
"""
|
|
params = {
|
|
"base_high": self.mock_base_high,
|
|
"lightning_high": self.mock_lightning_high,
|
|
"lightning_low": self.mock_lightning_low,
|
|
"positive": self.mock_positive,
|
|
"negative": self.mock_negative,
|
|
"latent_image": self.mock_latent,
|
|
"seed": 42,
|
|
"sigma_shift": 5.0,
|
|
"base_steps": 3,
|
|
"base_quality_threshold": 20,
|
|
"base_cfg": 3.5, # Different from lightning_cfg
|
|
"lightning_cfg": 7.2, # Different from base_cfg
|
|
"lightning_start": 1,
|
|
"lightning_steps": 8,
|
|
"base_sampler": "euler",
|
|
"base_scheduler": "simple",
|
|
"lightning_sampler": "euler",
|
|
"lightning_scheduler": "simple",
|
|
"switch_strategy": "50% of steps",
|
|
"switch_step": -1,
|
|
"switch_boundary": 0.875,
|
|
"dry_run": True,
|
|
}
|
|
|
|
# This should work without errors when CFG values are different
|
|
# dry_run now raises InterruptProcessingException
|
|
import comfy.model_management
|
|
|
|
with pytest.raises(comfy.model_management.InterruptProcessingException):
|
|
self.advanced_node.sample(**params)
|
|
|
|
# The key point: this test would have failed during the bug because
|
|
# the code would have been internally inconsistent about CFG handling
|