Skip to content

Commit e504b04

Browse files
authored
[tests] introduce output_shape property in the pipeline tests. (huggingface#14253)
* introduce output_shape property in the pipeline tests. * up * add missing * up
1 parent 717f903 commit e504b04

24 files changed

Lines changed: 69 additions & 26 deletions

‎.ai/testing.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Follow the style introduced in [#14113](https://github.com/huggingface/diffusers
2222
- **These are pytest-style, not `unittest`** — no `unittest.TestCase` subclassing, no `setUp`/`tearDown` (a `cleanup` fixture handles VRAM), and skips use `pytest.skip` / `@pytest.mark.skip`, never `@unittest.skip`. Fixtures like `tmp_path` and the cached `base_pipe_output` are injected into test methods as arguments.
2323
- **Define one config class**, `<Pipeline>PipelineTesterConfig`, subclassing `BasePipelineTesterConfig` (from `..testing_utils`). It holds the whole testing contract and performs no assertions:
2424
- Set `pipeline_class`, `required_input_params_in_call_signature` (params that must appear in `__call__`'s signature), and `batch_input_params` (params that get batched). Use the canonical sets in `..pipeline_params` where one fits, or an inline `frozenset([...])`.
25+
- Set `output_shape` — the per-sample output shape for `get_dummy_inputs()`, i.e. `(channels, height, width)` for an image pipeline and `(num_frames, channels, height, width)` for a video one. Assert against `self.output_shape` in pipeline-specific tests instead of repeating the literal.
2526
- Implement `get_dummy_components(...)` — build every sub-module from the **real classes** at tiny config, each preceded by `torch.manual_seed(0)`.
2627
- Implement `get_dummy_inputs()` — **no `device` / `seed` arguments** (unlike the old style). Use `self.get_generator(0)` for the generator, keep sizes tiny, and set `output_type="pt"` so tests compare torch tensors directly with `assert_tensors_close` (no numpy round-trip). Remember `"pt"` images are `(batch, channels, height, width)`.
2728
- **Compose the config with one mixin per concern**, one test class each, named `Test<Pipeline>...`. Add only the mixins that apply:

‎tests/pipelines/cogvideo/test_cogvideox.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class CogVideoXPipelineTesterConfig(BasePipelineTesterConfig):
4646
["prompt", "negative_prompt", "height", "width", "guidance_scale", "prompt_embeds", "negative_prompt_embeds"]
4747
)
4848
batch_input_params = frozenset(["prompt"])
49+
output_shape = (8, 3, 16, 16)
4950
# CogVideoX is a video pipeline: it exposes `num_videos_per_prompt`, not the base default `num_images_per_prompt`.
5051
optional_input_params = frozenset(
5152
["num_inference_steps", "num_videos_per_prompt", "generator", "latents", "output_type", "return_dict"]
@@ -134,7 +135,7 @@ def test_inference(self):
134135
inputs = self.get_dummy_inputs()
135136
video = pipe(**inputs).frames
136137
generated_video = video[0]
137-
assert generated_video.shape == (8, 3, 16, 16)
138+
assert generated_video.shape == self.output_shape
138139

139140
# fmt: off
140141
expected_slice = torch.tensor([0.4370, 0.3687, 0.3268, 0.3554, 0.3712, 0.3622, 0.3604, 0.3981, 0.5380, 0.5220, 0.5235, 0.5247, 0.5405, 0.5487, 0.5489, 0.5326])

‎tests/pipelines/cogvideo/test_cogvideox_fun_control.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class CogVideoXFunControlPipelineTesterConfig(BasePipelineTesterConfig):
3535
["prompt", "negative_prompt", "height", "width", "guidance_scale", "prompt_embeds", "negative_prompt_embeds"]
3636
)
3737
batch_input_params = frozenset(["prompt", "control_video"])
38+
output_shape = (8, 3, 16, 16)
3839
# CogVideoX is a video pipeline: it exposes `num_videos_per_prompt`, not the base default `num_images_per_prompt`.
3940
optional_input_params = frozenset(
4041
["num_inference_steps", "num_videos_per_prompt", "generator", "latents", "output_type", "return_dict"]
@@ -128,7 +129,7 @@ def test_inference(self):
128129
inputs = self.get_dummy_inputs()
129130
video = pipe(**inputs).frames
130131
generated_video = video[0]
131-
assert generated_video.shape == (8, 3, 16, 16)
132+
assert generated_video.shape == self.output_shape
132133

133134
# fmt: off
134135
expected_slice = torch.tensor([0.5921, 0.6076, 0.6015, 0.6024, 0.6140, 0.5966, 0.5870, 0.6079, 0.5049, 0.5086, 0.4891, 0.4783, 0.4770, 0.4738, 0.4537, 0.4508])

‎tests/pipelines/cogvideo/test_cogvideox_image2video.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class CogVideoXImageToVideoPipelineTesterConfig(BasePipelineTesterConfig):
5454
]
5555
)
5656
batch_input_params = frozenset(["prompt", "image"])
57+
output_shape = (8, 3, 16, 16)
5758
# CogVideoX is a video pipeline: it exposes `num_videos_per_prompt`, not the base default `num_images_per_prompt`.
5859
optional_input_params = frozenset(
5960
["num_inference_steps", "num_videos_per_prompt", "generator", "latents", "output_type", "return_dict"]
@@ -154,7 +155,7 @@ def test_inference(self):
154155
inputs = self.get_dummy_inputs()
155156
video = pipe(**inputs).frames
156157
generated_video = video[0]
157-
assert generated_video.shape == (8, 3, 16, 16)
158+
assert generated_video.shape == self.output_shape
158159

159160
# fmt: off
160161
expected_slice = torch.tensor([0.4367, 0.4802, 0.5403, 0.5509, 0.5595, 0.5698, 0.5206, 0.5207, 0.5930, 0.5178, 0.4597, 0.4430, 0.4488, 0.4766, 0.5003, 0.4865])

‎tests/pipelines/cogvideo/test_cogvideox_video2video.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class CogVideoXVideoToVideoPipelineTesterConfig(BasePipelineTesterConfig):
3535
["prompt", "negative_prompt", "height", "width", "guidance_scale", "prompt_embeds", "negative_prompt_embeds"]
3636
)
3737
batch_input_params = frozenset(["prompt", "video"])
38+
output_shape = (8, 3, 16, 16)
3839
# CogVideoX is a video pipeline: it exposes `num_videos_per_prompt`, not the base default `num_images_per_prompt`.
3940
optional_input_params = frozenset(
4041
["num_inference_steps", "num_videos_per_prompt", "generator", "latents", "output_type", "return_dict"]
@@ -129,7 +130,7 @@ def test_inference(self):
129130
inputs = self.get_dummy_inputs()
130131
video = pipe(**inputs).frames
131132
generated_video = video[0]
132-
assert generated_video.shape == (8, 3, 16, 16)
133+
assert generated_video.shape == self.output_shape
133134

134135
# fmt: off
135136
expected_slice = torch.tensor([0.5644, 0.6029, 0.6017, 0.5937, 0.5991, 0.5907, 0.6141, 0.5340, 0.3184, 0.4219, 0.4406, 0.4330, 0.4692, 0.4547, 0.4562, 0.5092])

‎tests/pipelines/flux/test_pipeline_flux.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class FluxPipelineTesterConfig(BasePipelineTesterConfig):
4646
["prompt", "height", "width", "guidance_scale", "prompt_embeds", "pooled_prompt_embeds"]
4747
)
4848
batch_input_params = frozenset(["prompt"])
49+
output_shape = (3, 8, 8)
4950

5051
def get_dummy_components(self, num_layers: int = 1, num_single_layers: int = 1):
5152
torch.manual_seed(0)

‎tests/pipelines/flux2/test_pipeline_flux2.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Flux2PipelineTesterConfig(BasePipelineTesterConfig):
2323
["prompt", "height", "width", "guidance_scale", "prompt_embeds"]
2424
)
2525
batch_input_params = frozenset(["prompt"])
26+
output_shape = (3, 8, 8)
2627

2728
def get_dummy_components(self, num_layers: int = 1, num_single_layers: int = 1):
2829
torch.manual_seed(0)

‎tests/pipelines/flux2/test_pipeline_flux2_klein.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Flux2KleinPipelineTesterConfig(BasePipelineTesterConfig):
3535
["prompt", "height", "width", "guidance_scale", "prompt_embeds"]
3636
)
3737
batch_input_params = frozenset(["prompt"])
38+
output_shape = (3, 8, 8)
3839

3940
def get_dummy_components(self, num_layers: int = 1, num_single_layers: int = 1):
4041
torch.manual_seed(0)

‎tests/pipelines/flux2/test_pipeline_flux2_klein_inpaint.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Flux2KleinInpaintPipelineTesterConfig(BasePipelineTesterConfig):
2121
["prompt", "image", "image_reference", "mask_image", "height", "width", "guidance_scale", "prompt_embeds"]
2222
)
2323
batch_input_params = frozenset(["prompt", "image", "image_reference", "mask_image"])
24+
output_shape = (3, 32, 32)
2425

2526
def get_dummy_components(self, num_layers: int = 1, num_single_layers: int = 1):
2627
torch.manual_seed(0)

‎tests/pipelines/flux2/test_pipeline_flux2_klein_kv.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Flux2KleinKVPipelineTesterConfig(BasePipelineTesterConfig):
2323
pipeline_class = Flux2KleinKVPipeline
2424
required_input_params_in_call_signature = frozenset(["prompt", "height", "width", "prompt_embeds", "image"])
2525
batch_input_params = frozenset(["prompt"])
26+
output_shape = (3, 8, 8)
2627

2728
def get_dummy_components(self, num_layers: int = 1, num_single_layers: int = 1):
2829
torch.manual_seed(0)
@@ -164,7 +165,7 @@ def test_without_image(self):
164165
inputs = self.get_dummy_inputs()
165166
del inputs["image"]
166167
image = pipe(**inputs).images
167-
assert image.shape == (1, 3, 8, 8)
168+
assert image.shape == (1, *self.output_shape)
168169

169170
@pytest.mark.skip("Needs to be revisited")
170171
def test_encode_prompt_works_in_isolation(self):

0 commit comments

Comments
 (0)