Repository: huggingface/transformers
Pull request: Add processing tests for phi4 multimodal

Cc @zucchini-nlp 

Changed files:
filename: src/transformers/models/phi4_multimodal/processing_phi4_multimodal.py
patch: @@ -58,7 +58,7 @@ def __call__(
         text: TextInput | list[TextInput],
         images: ImageInput | None = None,
         audio: AudioInput | None = None,
-        **kwargs: Unpack[ProcessingKwargs],
+        **kwargs: Unpack[Phi4MultimodalProcessorKwargs],
     ) -> BatchFeature:
         r"""
         Returns:

filename: tests/models/phi4_multimodal/test_processing_phi4_multimodal.py
patch: @@ -0,0 +1,104 @@
+# Copyright 2026 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import inspect
+import unittest
+
+import numpy as np
+
+from transformers.testing_utils import require_vision
+from transformers.utils import is_vision_available
+
+from ...test_processing_common import ProcessorTesterMixin
+
+
+if is_vision_available():
+    from PIL import Image
+
+    from transformers import Phi4MultimodalProcessor
+
+
+@require_vision
+class Phi4MultimodalProcessorTest(ProcessorTesterMixin, unittest.TestCase):
+    processor_class = Phi4MultimodalProcessor
+    checkpoint_path = "microsoft/Phi-4-multimodal-instruct"
+    revision = "refs/pr/70"
+    text_input_name = "input_ids"
+    images_input_name = "image_pixel_values"
+    audio_input_name = "audio_input_features"
+
+    # Max-length values used in image-text kwargs tests. Override as phi4 needs lots of tokens for images.
+    image_text_kwargs_max_length = 400
+    image_text_kwargs_override_max_length = 396
+    image_unstructured_max_length = 407
+
+    # Max-length values used in audio-text kwargs tests. Override as phi4 needs lots of tokens for audio.
+    audio_text_kwargs_max_length = 300
+    audio_processor_tester_max_length = 117
+    audio_unstructured_max_length = 76
+
+    # Max-length values used in video-text kwargs tests. Override in subclasses if needed.
+    video_text_kwargs_max_length = 167
+    video_text_kwargs_override_max_length = 162
+    video_unstructured_max_length = 176
+
+    @classmethod
+    def _setup_tokenizer(cls):
+        tokenizer_class = cls._get_component_class_from_processor("tokenizer")
+        return tokenizer_class.from_pretrained(cls.checkpoint_path, revision=cls.revision)
+
+    @classmethod
+    def _setup_test_attributes(cls, processor):
+        cls.image_token = processor.image_token
+        cls.image_token_id = processor.image_token_id
+        cls.audio_token = processor.audio_token
+        cls.audio_token_id = processor.audio_token_id
+
+    # override: audio_attention_mask is returned conditionally, and not expected in the input names in this case
+    def test_model_input_names(self):
+        processor = self.get_processor()
+
+        text = self.prepare_text_inputs(modalities=["image", "video", "audio"])
+        image_input = self.prepare_image_inputs()
+        video_inputs = self.prepare_video_inputs()
+        audio_inputs = self.prepare_audio_inputs()
+        inputs_dict = {"text": text, "images": image_input, "videos": video_inputs, "audio": audio_inputs}
+
+        call_signature = inspect.signature(processor.__call__)
+        input_args = [param.name for param in call_signature.parameters.values()]
+        inputs_dict = {k: v for k, v in inputs_dict.items() if k in input_args}
+
+        inputs = processor(**inputs_dict, return_tensors="pt")
+
+        # audio_attention_mask is returned conditionally, and not expected in the input names in this case
+        input_names_expected = set(processor.model_input_names) - {"audio_attention_mask"}
+        self.assertSetEqual(set(inputs.keys()), input_names_expected)
+
+    def test_dynamic_hd_kwarg_passed_to_image_processor(self):
+        processor = self.get_processor()
+        # 1000x1000 image: with size=448, w_crop_num=3, h_crop_num=3 -> 9 HD crops (1 global + 9 = 10 total)
+        # With dynamic_hd=4: limits to 2x2 grid -> 4 HD crops (1 global + 4 = 5 total)
+        arr = np.random.randint(255, size=(3, 1000, 1000), dtype=np.uint8)
+        image_input = Image.fromarray(np.moveaxis(arr, 0, -1))
+        input_str = self.prepare_text_inputs(modalities="image")
+
+        inputs_default = processor(text=input_str, images=image_input, return_tensors="pt")
+        inputs_limited = processor(
+            text=input_str,
+            images=image_input,
+            dynamic_hd=4,
+            return_tensors="pt",
+        )
+
+        self.assertEqual(inputs_limited[self.images_input_name].shape[1], 5)
+        self.assertEqual(inputs_default[self.images_input_name].shape[1], 10)

filename: tests/test_processing_common.py
patch: @@ -106,6 +106,24 @@ class ProcessorTesterMixin:
     videos_input_name = "pixel_values_videos"
     audio_input_name = "input_features"
 
+    # Max-length values used in image-text kwargs tests. Override in subclasses if needed.
+    image_text_kwargs_max_length = 117
+    image_text_kwargs_override_max_length = 112
+    image_unstructured_max_length = 76
+
+    # Max-length values used in audio-text kwargs tests. Override in subclasses if needed.
+    audio_text_kwargs_max_length = 300
+    audio_processor_tester_max_length = 117
+    audio_unstructured_max_length = 76
+
+    # Max-length values used in video-text kwargs tests. Override in subclasses if needed.
+    video_text_kwargs_max_length = 167
+    video_text_kwargs_override_max_length = 162
+    video_unstructured_max_length = 176
+
+    # Max-length value used in chat template tests. Override in subclasses if needed.
+    chat_template_max_length = 100  # max_length in test_apply_chat_template_*
+
     @classmethod
     def setUpClass(cls):
         """
@@ -288,7 +306,7 @@ def _get_component_class_from_processor(cls, attribute, use_fast: bool = True):
             from transformers.models.auto.image_processing_auto import IMAGE_PROCESSOR_MAPPING
 
             component_class = IMAGE_PROCESSOR_MAPPING.get(config_class, None)
-        elif mapping_name == "feature_extractor":
+        elif mapping_name == "feature_extractor" or mapping_name == "audio_processor":
             from transformers.models.auto.feature_extraction_auto import FEATURE_EXTRACTOR_MAPPING
 
             component_class = FEATURE_EXTRACTOR_MAPPING.get(config_class, None)
@@ -475,7 +493,8 @@ def test_save_load_pretrained_additional_features(self):
         attributes = self.processor_class.get_attributes()
 
         if not any(
-            attr in ["tokenizer", "image_processor", "feature_extractor", "video_processor"] for attr in attributes
+            attr in ["tokenizer", "image_processor", "feature_extractor", "audio_processor", "video_processor"]
+            for attr in attributes
         ):
             self.skipTest("Processor has no tokenizer or image_processor to test additional features")
         additional_kwargs = {}
@@ -712,6 +731,7 @@ def test_processor_with_multiple_inputs(self):
             "image_processor": ("images", "prepare_image_inputs", "images_input_name"),
             "video_processor": ("videos", "prepare_video_inputs", "videos_input_name"),
             "feature_extractor": ("audio", "prepare_audio_inputs", "audio_input_name"),
+            "audio_processor": ("audio", "prepare_audio_inputs", "audio_input_name"),
         }
 
         # Prepare inputs dynamically based on processor attributes
@@ -856,15 +876,17 @@ def test_tokenizer_defaults_preserved_by_kwargs(self):
         if "tokenizer" not in self.processor_class.get_attributes():
             self.skipTest(f"tokenizer attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=117, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.image_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
         self.skip_processor_without_typed_kwargs(processor)
         input_str = self.prepare_text_inputs(modalities="image")
         image_input = self.prepare_image_inputs()
         inputs = processor(text=input_str, images=image_input, return_tensors="pt")
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 117)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.image_text_kwargs_max_length)
 
     def test_image_processor_defaults_preserved_by_image_kwargs(self):
         """
@@ -880,7 +902,9 @@ def test_image_processor_defaults_preserved_by_image_kwargs(self):
         processor_components["image_processor"] = self.get_component(
             "image_processor", do_rescale=True, rescale_factor=-1.0
         )
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=117, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.image_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -906,9 +930,13 @@ def test_kwargs_overrides_default_tokenizer_kwargs(self):
         input_str = self.prepare_text_inputs(modalities="image")
         image_input = self.prepare_image_inputs()
         inputs = processor(
-            text=input_str, images=image_input, return_tensors="pt", max_length=112, padding="max_length"
+            text=input_str,
+            images=image_input,
+            return_tensors="pt",
+            max_length=self.image_text_kwargs_override_max_length,
+            padding="max_length",
         )
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 112)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.image_text_kwargs_override_max_length)
 
     def test_kwargs_overrides_default_image_processor_kwargs(self):
         if "image_processor" not in self.processor_class.get_attributes():
@@ -919,7 +947,9 @@ def test_kwargs_overrides_default_image_processor_kwargs(self):
         processor_components["image_processor"] = self.get_component(
             "image_processor", do_rescale=True, rescale_factor=1
         )
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=117, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.image_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -950,11 +980,11 @@ def test_unstructured_kwargs(self):
             do_rescale=True,
             rescale_factor=-1.0,
             padding="max_length",
-            max_length=76,
+            max_length=self.image_unstructured_max_length,
         )
 
         self.assertLessEqual(inputs[self.images_input_name][0][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 76)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.image_unstructured_max_length)
 
     def test_unstructured_kwargs_batched(self):
         if "image_processor" not in self.processor_class.get_attributes():
@@ -973,13 +1003,13 @@ def test_unstructured_kwargs_batched(self):
             do_rescale=True,
             rescale_factor=-1.0,
             padding="longest",
-            max_length=76,
+            max_length=self.image_unstructured_max_length,
         )
 
         self.assertLessEqual(inputs[self.images_input_name][0][0].mean(), 0)
         self.assertTrue(
             len(inputs[self.text_input_name][0]) == len(inputs[self.text_input_name][1])
-            and len(inputs[self.text_input_name][1]) < 76
+            and len(inputs[self.text_input_name][1]) < self.image_unstructured_max_length
         )
 
     def test_doubly_passed_kwargs(self):
@@ -1028,14 +1058,14 @@ def test_structured_kwargs_nested(self):
         all_kwargs = {
             "common_kwargs": {"return_tensors": "pt"},
             "images_kwargs": {"do_rescale": True, "rescale_factor": -1.0},
-            "text_kwargs": {"padding": "max_length", "max_length": 76},
+            "text_kwargs": {"padding": "max_length", "max_length": self.image_unstructured_max_length},
         }
 
         inputs = processor(text=input_str, images=image_input, **all_kwargs)
         self.skip_processor_without_typed_kwargs(processor)
 
         self.assertLessEqual(inputs[self.images_input_name][0][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 76)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.image_unstructured_max_length)
 
     def test_structured_kwargs_nested_from_dict(self):
         if "image_processor" not in self.processor_class.get_attributes():
@@ -1051,22 +1081,27 @@ def test_structured_kwargs_nested_from_dict(self):
         all_kwargs = {
             "common_kwargs": {"return_tensors": "pt"},
             "images_kwargs": {"do_rescale": True, "rescale_factor": -1.0},
-            "text_kwargs": {"padding": "max_length", "max_length": 76},
+            "text_kwargs": {"padding": "max_length", "max_length": self.image_unstructured_max_length},
         }
 
         inputs = processor(text=input_str, images=image_input, **all_kwargs)
         self.assertLessEqual(inputs[self.images_input_name][0][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 76)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.image_unstructured_max_length)
 
     # text + audio kwargs testing
     @require_torch
     def test_tokenizer_defaults_preserved_by_kwargs_audio(self):
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
         if "tokenizer" not in self.processor_class.get_attributes():
             self.skipTest(f"tokenizer attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=300, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.audio_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -1075,30 +1110,44 @@ def test_tokenizer_defaults_preserved_by_kwargs_audio(self):
         input_str = self.prepare_text_inputs(batch_size=3, modalities="audio")
         raw_speech = self.prepare_audio_inputs(batch_size=3)
         inputs = processor(text=input_str, audio=raw_speech, return_tensors="pt")
-        self.assertEqual(len(inputs[self.text_input_name][0]), 300)
+        self.assertEqual(len(inputs[self.text_input_name][0]), self.audio_text_kwargs_max_length)
 
     @require_torch
     def test_kwargs_overrides_default_tokenizer_kwargs_audio(self):
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
         if "tokenizer" not in self.processor_class.get_attributes():
             self.skipTest(f"tokenizer attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=117)
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.audio_processor_tester_max_length
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
         self.skip_processor_without_typed_kwargs(processor)
 
         input_str = self.prepare_text_inputs(batch_size=3, modalities="audio")
         raw_speech = self.prepare_audio_inputs(batch_size=3)
-        inputs = processor(text=input_str, audio=raw_speech, return_tensors="pt", max_length=300, padding="max_length")
+        inputs = processor(
+            text=input_str,
+            audio=raw_speech,
+            return_tensors="pt",
+            max_length=self.audio_text_kwargs_max_length,
+            padding="max_length",
+        )
 
-        self.assertEqual(len(inputs[self.text_input_name][0]), 300)
+        self.assertEqual(len(inputs[self.text_input_name][0]), self.audio_text_kwargs_max_length)
 
     @require_torch
     def test_unstructured_kwargs_audio(self):
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
         processor_kwargs = self.prepare_processor_dict()
@@ -1108,13 +1157,22 @@ def test_unstructured_kwargs_audio(self):
 
         input_str = self.prepare_text_inputs(batch_size=3, modalities="audio")
         raw_speech = self.prepare_audio_inputs(batch_size=3)
-        inputs = processor(text=input_str, audio=raw_speech, return_tensors="pt", max_length=300, padding="max_length")
+        inputs = processor(
+            text=input_str,
+            audio=raw_speech,
+            return_tensors="pt",
+            max_length=self.audio_text_kwargs_max_length,
+            padding="max_length",
+        )
 
-        self.assertEqual(len(inputs[self.text_input_name][0]), 300)
+        self.assertEqual(len(inputs[self.text_input_name][0]), self.audio_text_kwargs_max_length)
 
     @require_torch
     def test_doubly_passed_kwargs_audio(self):
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
         processor_kwargs = self.prepare_processor_dict()
@@ -1135,12 +1193,17 @@ def test_doubly_passed_kwargs_audio(self):
     @require_torch
     @require_vision
     def test_structured_kwargs_audio_nested(self):
-        if "feature_extractor" not in self.processor_class.get_attributes():
-            self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
+            self.skipTest(f"feature_extractor or audio_processor attribute not present in {self.processor_class}")
         if "tokenizer" not in self.processor_class.get_attributes():
             self.skipTest(f"tokenizer attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=117)
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.audio_processor_tester_max_length
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -1152,28 +1215,30 @@ def test_structured_kwargs_audio_nested(self):
         # Define the kwargs for each modality
         all_kwargs = {
             "common_kwargs": {"return_tensors": "pt"},
-            "text_kwargs": {"padding": "max_length", "max_length": 76},
-            "audio_kwargs": {"padding": "max_length", "max_length": 300},
+            "text_kwargs": {"padding": "max_length", "max_length": self.audio_unstructured_max_length},
+            "audio_kwargs": {"padding": "max_length", "max_length": self.audio_text_kwargs_max_length},
         }
 
         inputs = processor(text=input_str, audio=raw_speech, **all_kwargs)
-        self.assertEqual(len(inputs[self.text_input_name][0]), 76)
+        self.assertEqual(len(inputs[self.text_input_name][0]), self.audio_unstructured_max_length)
 
     def test_tokenizer_defaults_preserved_by_kwargs_video(self):
         if "video_processor" not in self.processor_class.get_attributes():
             self.skipTest(f"video_processor attribute not present in {self.processor_class}")
         if "tokenizer" not in self.processor_class.get_attributes():
             self.skipTest(f"tokenizer attribute not present in {self.processor_class}")
         processor_components = self.prepare_components()
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=167, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.video_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
         self.skip_processor_without_typed_kwargs(processor)
         input_str = self.prepare_text_inputs(modalities="video")
         video_input = self.prepare_video_inputs()
         inputs = processor(text=input_str, videos=video_input, do_sample_frames=False, return_tensors="pt")
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 167)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.video_text_kwargs_max_length)
 
     def test_video_processor_defaults_preserved_by_video_kwargs(self):
         """
@@ -1189,7 +1254,9 @@ def test_video_processor_defaults_preserved_by_video_kwargs(self):
         processor_components["video_processor"] = self.get_component(
             "video_processor", do_rescale=True, rescale_factor=-1.0
         )
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=167, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.video_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -1219,10 +1286,10 @@ def test_kwargs_overrides_default_tokenizer_kwargs_video(self):
             videos=video_input,
             do_sample_frames=False,
             return_tensors="pt",
-            max_length=162,
+            max_length=self.video_text_kwargs_override_max_length,
             padding="max_length",
         )
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 162)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.video_text_kwargs_override_max_length)
 
     def test_kwargs_overrides_default_video_processor_kwargs(self):
         if "video_processor" not in self.processor_class.get_attributes():
@@ -1233,7 +1300,9 @@ def test_kwargs_overrides_default_video_processor_kwargs(self):
         processor_components["video_processor"] = self.get_component(
             "video_processor", do_rescale=True, rescale_factor=1
         )
-        processor_components["tokenizer"] = self.get_component("tokenizer", max_length=167, padding="max_length")
+        processor_components["tokenizer"] = self.get_component(
+            "tokenizer", max_length=self.video_text_kwargs_max_length, padding="max_length"
+        )
         processor_kwargs = self.prepare_processor_dict()
 
         processor = self.processor_class(**processor_components, **processor_kwargs)
@@ -1270,11 +1339,11 @@ def test_unstructured_kwargs_video(self):
             do_rescale=True,
             rescale_factor=-1.0,
             padding="max_length",
-            max_length=176,
+            max_length=self.video_unstructured_max_length,
         )
 
         self.assertLessEqual(inputs[self.videos_input_name][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 176)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.video_unstructured_max_length)
 
     def test_unstructured_kwargs_batched_video(self):
         if "video_processor" not in self.processor_class.get_attributes():
@@ -1294,13 +1363,13 @@ def test_unstructured_kwargs_batched_video(self):
             do_rescale=True,
             rescale_factor=-1.0,
             padding="longest",
-            max_length=176,
+            max_length=self.video_unstructured_max_length,
         )
 
         self.assertLessEqual(inputs[self.videos_input_name][0].mean(), 0)
         self.assertTrue(
             len(inputs[self.text_input_name][0]) == len(inputs[self.text_input_name][1])
-            and len(inputs[self.text_input_name][1]) < 176
+            and len(inputs[self.text_input_name][1]) < self.video_unstructured_max_length
         )
 
     def test_doubly_passed_kwargs_video(self):
@@ -1338,14 +1407,14 @@ def test_structured_kwargs_nested_video(self):
         all_kwargs = {
             "common_kwargs": {"return_tensors": "pt"},
             "videos_kwargs": {"do_rescale": True, "rescale_factor": -1.0, "do_sample_frames": False},
-            "text_kwargs": {"padding": "max_length", "max_length": 176},
+            "text_kwargs": {"padding": "max_length", "max_length": self.video_unstructured_max_length},
         }
 
         inputs = processor(text=input_str, videos=video_input, **all_kwargs)
         self.skip_processor_without_typed_kwargs(processor)
 
         self.assertLessEqual(inputs[self.videos_input_name][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 176)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.video_unstructured_max_length)
 
     def test_structured_kwargs_nested_from_dict_video(self):
         if "video_processor" not in self.processor_class.get_attributes():
@@ -1361,12 +1430,12 @@ def test_structured_kwargs_nested_from_dict_video(self):
         all_kwargs = {
             "common_kwargs": {"return_tensors": "pt"},
             "videos_kwargs": {"do_rescale": True, "rescale_factor": -1.0, "do_sample_frames": False},
-            "text_kwargs": {"padding": "max_length", "max_length": 176},
+            "text_kwargs": {"padding": "max_length", "max_length": self.video_unstructured_max_length},
         }
 
         inputs = processor(text=input_str, videos=video_input, **all_kwargs)
         self.assertLessEqual(inputs[self.videos_input_name][0].mean(), 0)
-        self.assertEqual(inputs[self.text_input_name].shape[-1], 176)
+        self.assertEqual(inputs[self.text_input_name].shape[-1], self.video_unstructured_max_length)
 
     # TODO: the same test, but for audio + text processors that have strong overlap in kwargs
     # TODO (molbap) use the same structure of attribute kwargs for other tests to avoid duplication
@@ -1396,7 +1465,10 @@ def test_overlapping_text_audio_kwargs_handling(self):
         Checks that `padding`, or any other overlap arg between audio extractor and tokenizer
         is be passed to only text and ignored for audio for BC purposes
         """
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
 
         processor_components = self.prepare_components()
@@ -1503,9 +1575,9 @@ def _test_apply_chat_template(
             padding="max_length",
             truncation=True,
             return_tensors=return_tensors,
-            max_length=100,
+            max_length=self.chat_template_max_length,
         )
-        self.assertEqual(len(tokenized_prompt_100[0]), 100)
+        self.assertEqual(len(tokenized_prompt_100[0]), self.chat_template_max_length)
 
         # Test that `return_dict=True` returns text related inputs in the dict
         out_dict_text = processor.apply_chat_template(
@@ -1555,9 +1627,24 @@ def _test_apply_chat_template(
     @require_librosa
     @parameterized.expand([(1, "np"), (1, "pt"), (2, "np"), (2, "pt")])
     def test_apply_chat_template_audio(self, batch_size: int, return_tensors: str):
-        self._test_apply_chat_template(
-            "audio", batch_size, return_tensors, "audio_input_name", "feature_extractor", MODALITY_INPUT_DATA["audio"]
-        )
+        if "feature_extractor" in self.processor_class.get_attributes():
+            self._test_apply_chat_template(
+                "audio",
+                batch_size,
+                return_tensors,
+                "audio_input_name",
+                "feature_extractor",
+                MODALITY_INPUT_DATA["audio"],
+            )
+        else:
+            self._test_apply_chat_template(
+                "audio",
+                batch_size,
+                return_tensors,
+                "audio_input_name",
+                "audio_processor",
+                MODALITY_INPUT_DATA["audio"],
+            )
 
     @require_av
     @parameterized.expand([(1, "pt")])
@@ -1725,7 +1812,10 @@ def test_chat_template_audio_from_video(self):
         ):
             self.skipTest(f"{self.processor_class} does not support video inputs")
 
-        if "feature_extractor" not in self.processor_class.get_attributes():
+        if (
+            "feature_extractor" not in self.processor_class.get_attributes()
+            or "audio_processor" not in self.processor_class.get_attributes()
+        ):
             self.skipTest(f"feature_extractor attribute not present in {self.processor_class}")
 
         video_file_path = hf_hub_download(