Skip to content

Commit 5cfbf05

Browse files
refactor wan animate pipeline tests to the new mixin structure (#14239)
* refactor wan animate pipeline tests to the new mixin structure * assert against real value slice in wan animate test_inference --------- Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>
1 parent ad1982e commit 5cfbf05

1 file changed

Lines changed: 38 additions & 89 deletions

File tree

tests/pipelines/wan/test_wan_animate.py

Lines changed: 38 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import gc
16-
import unittest
17-
18-
import numpy as np
15+
import pytest
1916
import torch
2017
from PIL import Image
2118
from transformers import (
@@ -34,37 +31,19 @@
3431
WanAnimateTransformer3DModel,
3532
)
3633

37-
from ...testing_utils import (
38-
backend_empty_cache,
39-
enable_full_determinism,
40-
require_torch_accelerator,
41-
slow,
42-
torch_device,
43-
)
44-
from ..pipeline_params import TEXT_TO_IMAGE_BATCH_PARAMS, TEXT_TO_IMAGE_IMAGE_PARAMS, TEXT_TO_IMAGE_PARAMS
45-
from ..test_pipelines_common import PipelineTesterMixin
46-
47-
48-
enable_full_determinism()
34+
from ..testing_utils import BasePipelineTesterConfig, MemoryTesterMixin, PipelineTesterMixin
4935

5036

51-
class WanAnimatePipelineFastTests(PipelineTesterMixin, unittest.TestCase):
37+
class WanAnimatePipelineTesterConfig(BasePipelineTesterConfig):
5238
pipeline_class = WanAnimatePipeline
53-
params = TEXT_TO_IMAGE_PARAMS - {"cross_attention_kwargs"}
54-
batch_params = TEXT_TO_IMAGE_BATCH_PARAMS
55-
image_params = TEXT_TO_IMAGE_IMAGE_PARAMS
56-
image_latents_params = TEXT_TO_IMAGE_IMAGE_PARAMS
57-
required_optional_params = frozenset(
58-
[
59-
"num_inference_steps",
60-
"generator",
61-
"latents",
62-
"return_dict",
63-
"callback_on_step_end",
64-
"callback_on_step_end_tensor_inputs",
65-
]
39+
required_input_params_in_call_signature = frozenset(
40+
["prompt", "negative_prompt", "height", "width", "guidance_scale", "prompt_embeds", "negative_prompt_embeds"]
41+
)
42+
batch_input_params = frozenset(["prompt"])
43+
# Wan is a video pipeline: it exposes `num_videos_per_prompt`, not the base default `num_images_per_prompt`.
44+
optional_input_params = frozenset(
45+
["num_inference_steps", "num_videos_per_prompt", "generator", "latents", "output_type", "return_dict"]
6646
)
67-
test_xformers_attention = False
6847

6948
def get_dummy_components(self):
7049
torch.manual_seed(0)
@@ -124,7 +103,7 @@ def get_dummy_components(self):
124103
torch.manual_seed(0)
125104
image_processor = CLIPImageProcessor(crop_size=4, size=4)
126105

127-
components = {
106+
return {
128107
"transformer": transformer,
129108
"vae": vae,
130109
"scheduler": scheduler,
@@ -133,14 +112,8 @@ def get_dummy_components(self):
133112
"image_encoder": image_encoder,
134113
"image_processor": image_processor,
135114
}
136-
return components
137-
138-
def get_dummy_inputs(self, device, seed=0):
139-
if str(device).startswith("mps"):
140-
generator = torch.manual_seed(seed)
141-
else:
142-
generator = torch.Generator(device=device).manual_seed(seed)
143115

116+
def get_dummy_inputs(self):
144117
num_frames = 17
145118
height = 16
146119
width = 16
@@ -151,7 +124,7 @@ def get_dummy_inputs(self, device, seed=0):
151124
pose_video = [Image.new("RGB", (height, width))] * num_frames
152125
face_video = [Image.new("RGB", (face_height, face_width))] * num_frames
153126

154-
inputs = {
127+
return {
155128
"image": image,
156129
"pose_video": pose_video,
157130
"face_video": face_video,
@@ -163,40 +136,36 @@ def get_dummy_inputs(self, device, seed=0):
163136
"num_inference_steps": 2,
164137
"mode": "animate",
165138
"prev_segment_conditioning_frames": 1,
166-
"generator": generator,
139+
"generator": self.get_generator(0),
167140
"guidance_scale": 1.0,
141+
# Request torch outputs so tests compare torch tensors directly (see `BasePipelineTesterConfig`).
168142
"output_type": "pt",
169143
"max_sequence_length": 16,
170144
}
171-
return inputs
172145

173-
def test_inference(self):
174-
"""Test basic inference in animation mode."""
175-
device = "cpu"
176146

177-
components = self.get_dummy_components()
178-
pipe = self.pipeline_class(**components)
179-
pipe.to(device)
180-
pipe.set_progress_bar_config(disable=None)
147+
class TestWanAnimatePipeline(WanAnimatePipelineTesterConfig, PipelineTesterMixin):
148+
def test_inference(self):
149+
# Basic inference in animation mode. Run on CPU.
150+
pipe = self.get_pipeline()
181151

182-
inputs = self.get_dummy_inputs(device)
152+
inputs = self.get_dummy_inputs()
183153
video = pipe(**inputs).frames[0]
184-
self.assertEqual(video.shape, (17, 3, 16, 16))
154+
assert video.shape == (17, 3, 16, 16)
185155

186-
expected_video = torch.randn(17, 3, 16, 16)
187-
max_diff = np.abs(video - expected_video).max()
188-
self.assertLessEqual(max_diff, 1e10)
156+
# fmt: off
157+
expected_slice = torch.tensor([0.4525, 0.4521, 0.4486, 0.4534, 0.4523, 0.4529, 0.454, 0.4533, 0.5055, 0.5203, 0.5363, 0.4827, 0.5057, 0.5176, 0.5117, 0.5139])
158+
# fmt: on
189159

190-
def test_inference_replacement(self):
191-
"""Test the pipeline in replacement mode with background and mask videos."""
192-
device = "cpu"
160+
generated_slice = video.flatten()
161+
generated_slice = torch.cat([generated_slice[:8], generated_slice[-8:]])
162+
assert torch.allclose(generated_slice, expected_slice, atol=1e-3)
193163

194-
components = self.get_dummy_components()
195-
pipe = self.pipeline_class(**components)
196-
pipe.to(device)
197-
pipe.set_progress_bar_config(disable=None)
164+
def test_inference_replacement(self):
165+
# Replacement mode with background and mask videos. Run on CPU.
166+
pipe = self.get_pipeline()
198167

199-
inputs = self.get_dummy_inputs(device)
168+
inputs = self.get_dummy_inputs()
200169
inputs["mode"] = "replace"
201170
num_frames = 17
202171
height = 16
@@ -205,36 +174,16 @@ def test_inference_replacement(self):
205174
inputs["mask_video"] = [Image.new("L", (height, width))] * num_frames
206175

207176
video = pipe(**inputs).frames[0]
208-
self.assertEqual(video.shape, (17, 3, 16, 16))
209-
210-
@unittest.skip("Test not supported")
211-
def test_attention_slicing_forward_pass(self):
212-
pass
177+
assert video.shape == (17, 3, 16, 16)
213178

214-
@unittest.skip(
215-
"Setting the Wan Animate latents to zero at the last denoising step does not guarantee that the output will be"
216-
" zero. I believe this is because the latents are further processed in the outer loop where we loop over"
217-
" inference segments."
179+
@pytest.mark.skip(
180+
reason="Setting the Wan Animate latents to zero at the last denoising step does not guarantee that the output"
181+
" will be zero. I believe this is because the latents are further processed in the outer loop where we loop"
182+
" over inference segments."
218183
)
219184
def test_callback_inputs(self):
220185
pass
221186

222187

223-
@slow
224-
@require_torch_accelerator
225-
class WanAnimatePipelineIntegrationTests(unittest.TestCase):
226-
prompt = "A painting of a squirrel eating a burger."
227-
228-
def setUp(self):
229-
super().setUp()
230-
gc.collect()
231-
backend_empty_cache(torch_device)
232-
233-
def tearDown(self):
234-
super().tearDown()
235-
gc.collect()
236-
backend_empty_cache(torch_device)
237-
238-
@unittest.skip("TODO: test needs to be implemented")
239-
def test_wan_animate(self):
240-
pass
188+
class TestWanAnimatePipelineMemory(WanAnimatePipelineTesterConfig, MemoryTesterMixin):
189+
pass

0 commit comments

Comments
 (0)