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
1916import torch
2017from PIL import Image
2118from transformers import (
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