gradio

Форк
0
/
test_components.py 
3009 строк · 102.9 Кб
1
"""
2
Tests for all of the components defined in components.py. Tests are divided into two types:
3
1. test_component_functions() are unit tests that check essential functions of a component, the functions that are checked are documented in the docstring.
4
2. test_in_interface() are functional tests that check a component's functionalities inside an Interface. Please do not use Interface.launch() in this file, as it slow downs the tests.
5
"""
6

7
import filecmp
8
import inspect
9
import json
10
import os
11
import shutil
12
import tempfile
13
from copy import deepcopy
14
from difflib import SequenceMatcher
15
from pathlib import Path
16
from unittest.mock import MagicMock, patch
17

18
import numpy as np
19
import pandas as pd
20
import PIL
21
import pytest
22
import vega_datasets
23
from gradio_client import media_data
24
from gradio_client import utils as client_utils
25
from gradio_pdf import PDF
26
from scipy.io import wavfile
27

28
try:
29
    from typing import cast
30
except ImportError:
31
    from typing import cast
32

33
import gradio as gr
34
from gradio import processing_utils, utils
35
from gradio.components.base import Component
36
from gradio.components.dataframe import DataframeData
37
from gradio.components.file_explorer import FileExplorerData
38
from gradio.components.image_editor import EditorData
39
from gradio.components.video import VideoData
40
from gradio.data_classes import FileData, GradioModel, GradioRootModel, ListFiles
41

42
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
43

44

45
class TestGettingComponents:
46
    def test_component_function(self):
47
        assert isinstance(
48
            gr.components.component("textarea", render=False), gr.templates.TextArea
49
        )
50

51
    @pytest.mark.parametrize(
52
        "component, render, unrender, should_be_rendered",
53
        [
54
            (gr.Textbox(render=True), False, True, False),
55
            (gr.Textbox(render=False), False, False, False),
56
            (gr.Textbox(render=False), True, False, True),
57
            ("textbox", False, False, False),
58
            ("textbox", True, False, True),
59
        ],
60
    )
61
    def test_get_component_instance_rendering(
62
        self, component, render, unrender, should_be_rendered
63
    ):
64
        with gr.Blocks():
65
            textbox = gr.components.get_component_instance(
66
                component, render=render, unrender=unrender
67
            )
68
            assert textbox.is_rendered == should_be_rendered
69

70

71
class TestTextbox:
72
    def test_component_functions(self):
73
        """
74
        Preprocess, postprocess, serialize, tokenize, get_config
75
        """
76
        text_input = gr.Textbox()
77
        assert text_input.preprocess("Hello World!") == "Hello World!"
78
        assert text_input.postprocess("Hello World!") == "Hello World!"
79
        assert text_input.postprocess(None) is None
80
        assert text_input.postprocess("Ali") == "Ali"
81
        assert text_input.postprocess(2) == "2"
82
        assert text_input.postprocess(2.14) == "2.14"
83
        assert text_input.get_config() == {
84
            "lines": 1,
85
            "max_lines": 20,
86
            "placeholder": None,
87
            "value": "",
88
            "name": "textbox",
89
            "show_copy_button": False,
90
            "show_label": True,
91
            "type": "text",
92
            "label": None,
93
            "container": True,
94
            "min_width": 160,
95
            "scale": None,
96
            "elem_id": None,
97
            "elem_classes": [],
98
            "visible": True,
99
            "interactive": None,
100
            "proxy_url": None,
101
            "rtl": False,
102
            "text_align": None,
103
            "autofocus": False,
104
            "_selectable": False,
105
            "info": None,
106
            "autoscroll": True,
107
        }
108

109
    @pytest.mark.asyncio
110
    async def test_in_interface_as_input(self):
111
        """
112
        Interface, process
113
        """
114
        iface = gr.Interface(lambda x: x[::-1], "textbox", "textbox")
115
        assert iface("Hello") == "olleH"
116

117
    def test_in_interface_as_output(self):
118
        """
119
        Interface, process
120

121
        """
122
        iface = gr.Interface(lambda x: x[-1], "textbox", gr.Textbox())
123
        assert iface("Hello") == "o"
124
        iface = gr.Interface(lambda x: x / 2, "number", gr.Textbox())
125
        assert iface(10) == "5.0"
126

127
    def test_static(self):
128
        """
129
        postprocess
130
        """
131
        component = gr.Textbox("abc")
132
        assert component.get_config().get("value") == "abc"
133

134
    def test_override_template(self):
135
        """
136
        override template
137
        """
138
        component = gr.TextArea(value="abc")
139
        assert component.get_config().get("value") == "abc"
140
        assert component.get_config().get("lines") == 7
141
        component = gr.TextArea(value="abc", lines=4)
142
        assert component.get_config().get("value") == "abc"
143
        assert component.get_config().get("lines") == 4
144

145
    def test_faulty_type(self):
146
        with pytest.raises(
147
            ValueError, match='`type` must be one of "text", "password", or "email".'
148
        ):
149
            gr.Textbox(type="boo")
150

151
    def test_max_lines(self):
152
        assert gr.Textbox(type="password").get_config().get("max_lines") == 1
153
        assert gr.Textbox(type="email").get_config().get("max_lines") == 1
154
        assert gr.Textbox(type="text").get_config().get("max_lines") == 20
155
        assert gr.Textbox().get_config().get("max_lines") == 20
156

157

158
class TestNumber:
159
    def test_component_functions(self):
160
        """
161
        Preprocess, postprocess, serialize, get_config
162

163
        """
164
        numeric_input = gr.Number(elem_id="num", elem_classes="first")
165
        assert numeric_input.preprocess(3) == 3.0
166
        assert numeric_input.preprocess(None) is None
167
        assert numeric_input.postprocess(3) == 3
168
        assert numeric_input.postprocess(3) == 3.0
169
        assert numeric_input.postprocess(2.14) == 2.14
170
        assert numeric_input.postprocess(None) is None
171
        assert numeric_input.get_config() == {
172
            "value": None,
173
            "name": "number",
174
            "show_label": True,
175
            "step": 1,
176
            "label": None,
177
            "minimum": None,
178
            "maximum": None,
179
            "container": True,
180
            "min_width": 160,
181
            "scale": None,
182
            "elem_id": "num",
183
            "elem_classes": ["first"],
184
            "visible": True,
185
            "interactive": None,
186
            "proxy_url": None,
187
            "info": None,
188
            "precision": None,
189
            "_selectable": False,
190
        }
191

192
    def test_component_functions_integer(self):
193
        """
194
        Preprocess, postprocess, serialize, get_template_context
195

196
        """
197
        numeric_input = gr.Number(precision=0, value=42)
198
        assert numeric_input.preprocess(3) == 3
199
        assert numeric_input.preprocess(None) is None
200
        assert numeric_input.postprocess(3) == 3
201
        assert numeric_input.postprocess(3) == 3
202
        assert numeric_input.postprocess(2.85) == 3
203
        assert numeric_input.postprocess(None) is None
204
        assert numeric_input.get_config() == {
205
            "value": 42,
206
            "name": "number",
207
            "show_label": True,
208
            "step": 1,
209
            "label": None,
210
            "minimum": None,
211
            "maximum": None,
212
            "container": True,
213
            "min_width": 160,
214
            "scale": None,
215
            "elem_id": None,
216
            "elem_classes": [],
217
            "visible": True,
218
            "interactive": None,
219
            "proxy_url": None,
220
            "info": None,
221
            "precision": 0,
222
            "_selectable": False,
223
        }
224

225
    def test_component_functions_precision(self):
226
        """
227
        Preprocess, postprocess, serialize, get_template_context
228

229
        """
230
        numeric_input = gr.Number(precision=2, value=42.3428)
231
        assert numeric_input.preprocess(3.231241) == 3.23
232
        assert numeric_input.preprocess(None) is None
233
        assert numeric_input.postprocess(-42.1241) == -42.12
234
        assert numeric_input.postprocess(5.6784) == 5.68
235
        assert numeric_input.postprocess(2.1421) == 2.14
236
        assert numeric_input.postprocess(None) is None
237

238
    def test_precision_none_with_integer(self):
239
        """
240
        Preprocess, postprocess
241
        """
242
        numeric_input = gr.Number(precision=None)
243
        assert numeric_input.preprocess(5) == 5
244
        assert isinstance(numeric_input.preprocess(5), int)
245
        assert numeric_input.postprocess(5) == 5
246
        assert isinstance(numeric_input.postprocess(5), int)
247

248
    def test_precision_none_with_float(self):
249
        """
250
        Preprocess, postprocess
251
        """
252
        numeric_input = gr.Number(value=5.5, precision=None)
253
        assert numeric_input.preprocess(5.5) == 5.5
254
        assert isinstance(numeric_input.preprocess(5.5), float)
255
        assert numeric_input.postprocess(5.5) == 5.5
256
        assert isinstance(numeric_input.postprocess(5.5), float)
257

258
    def test_in_interface_as_input(self):
259
        """
260
        Interface, process
261
        """
262
        iface = gr.Interface(lambda x: x**2, "number", "textbox")
263
        assert iface(2) == "4"
264

265
    def test_precision_0_in_interface(self):
266
        """
267
        Interface, process
268
        """
269
        iface = gr.Interface(lambda x: x**2, gr.Number(precision=0), "textbox")
270
        assert iface(2) == "4"
271

272
    def test_in_interface_as_output(self):
273
        """
274
        Interface, process
275
        """
276
        iface = gr.Interface(lambda x: int(x) ** 2, "textbox", "number")
277
        assert iface(2) == 4.0
278

279
    def test_static(self):
280
        """
281
        postprocess
282
        """
283
        component = gr.Number()
284
        assert component.get_config().get("value") is None
285
        component = gr.Number(3)
286
        assert component.get_config().get("value") == 3.0
287

288

289
class TestSlider:
290
    def test_component_functions(self):
291
        """
292
        Preprocess, postprocess, serialize, get_config
293
        """
294
        slider_input = gr.Slider()
295
        assert slider_input.preprocess(3.0) == 3.0
296
        assert slider_input.postprocess(3) == 3
297
        assert slider_input.postprocess(3) == 3
298
        assert slider_input.postprocess(None) == 0
299

300
        slider_input = gr.Slider(10, 20, value=15, step=1, label="Slide Your Input")
301
        assert slider_input.get_config() == {
302
            "minimum": 10,
303
            "maximum": 20,
304
            "step": 1,
305
            "value": 15,
306
            "name": "slider",
307
            "show_label": True,
308
            "label": "Slide Your Input",
309
            "container": True,
310
            "min_width": 160,
311
            "scale": None,
312
            "elem_id": None,
313
            "elem_classes": [],
314
            "visible": True,
315
            "interactive": None,
316
            "proxy_url": None,
317
            "info": None,
318
            "_selectable": False,
319
        }
320

321
    def test_in_interface(self):
322
        """ "
323
        Interface, process
324
        """
325
        iface = gr.Interface(lambda x: x**2, "slider", "textbox")
326
        assert iface(2) == "4"
327

328
    def test_static(self):
329
        """
330
        postprocess
331
        """
332
        component = gr.Slider(0, 100, 5)
333
        assert component.get_config().get("value") == 5
334
        component = gr.Slider(0, 100, None)
335
        assert component.get_config().get("value") == 0
336

337
    @patch("gradio.Slider.get_random_value", return_value=7)
338
    def test_slider_get_random_value_on_load(self, mock_get_random_value):
339
        slider = gr.Slider(minimum=-5, maximum=10, randomize=True)
340
        assert slider.value == 7
341
        assert slider.load_event_to_attach[0]() == 7
342
        assert slider.load_event_to_attach[1] is None
343

344
    @patch("random.randint", return_value=3)
345
    def test_slider_rounds_when_using_default_randomizer(self, mock_randint):
346
        slider = gr.Slider(minimum=0, maximum=1, randomize=True, step=0.1)
347
        # If get_random_value didn't round, this test would fail
348
        # because 0.30000000000000004 != 0.3
349
        assert slider.get_random_value() == 0.3
350
        mock_randint.assert_called()
351

352

353
class TestCheckbox:
354
    def test_component_functions(self):
355
        """
356
        Preprocess, postprocess, serialize, get_config
357
        """
358
        bool_input = gr.Checkbox()
359
        assert bool_input.preprocess(True)
360
        assert bool_input.postprocess(True)
361
        assert bool_input.postprocess(True)
362
        bool_input = gr.Checkbox(value=True, label="Check Your Input")
363
        assert bool_input.get_config() == {
364
            "value": True,
365
            "name": "checkbox",
366
            "show_label": True,
367
            "label": "Check Your Input",
368
            "container": True,
369
            "min_width": 160,
370
            "scale": None,
371
            "elem_id": None,
372
            "elem_classes": [],
373
            "visible": True,
374
            "interactive": None,
375
            "proxy_url": None,
376
            "_selectable": False,
377
            "info": None,
378
        }
379

380
    def test_in_interface(self):
381
        """
382
        Interface, process
383
        """
384
        iface = gr.Interface(lambda x: 1 if x else 0, "checkbox", "number")
385
        assert iface(True) == 1
386

387

388
class TestCheckboxGroup:
389
    def test_component_functions(self):
390
        """
391
        Preprocess, postprocess, serialize, get_config
392
        """
393
        checkboxes_input = gr.CheckboxGroup(["a", "b", "c"])
394
        assert checkboxes_input.preprocess(["a", "c"]) == ["a", "c"]
395
        assert checkboxes_input.postprocess(["a", "c"]) == ["a", "c"]
396

397
        checkboxes_input = gr.CheckboxGroup(["a", "b"], type="index")
398
        assert checkboxes_input.preprocess(["a"]) == [0]
399
        assert checkboxes_input.preprocess(["a", "b"]) == [0, 1]
400
        assert checkboxes_input.preprocess(["a", "b", "c"]) == [0, 1, None]
401

402
        # When a Gradio app is loaded with gr.load, the tuples are converted to lists,
403
        # so we need to test that case as well
404
        checkboxgroup = gr.CheckboxGroup(["a", "b", ["c", "c full"]])  # type: ignore
405
        assert checkboxgroup.choices == [("a", "a"), ("b", "b"), ("c", "c full")]
406

407
        checkboxes_input = gr.CheckboxGroup(
408
            value=["a", "c"],
409
            choices=["a", "b", "c"],
410
            label="Check Your Inputs",
411
        )
412
        assert checkboxes_input.get_config() == {
413
            "choices": [("a", "a"), ("b", "b"), ("c", "c")],
414
            "value": ["a", "c"],
415
            "name": "checkboxgroup",
416
            "show_label": True,
417
            "label": "Check Your Inputs",
418
            "container": True,
419
            "min_width": 160,
420
            "scale": None,
421
            "elem_id": None,
422
            "elem_classes": [],
423
            "visible": True,
424
            "interactive": None,
425
            "proxy_url": None,
426
            "_selectable": False,
427
            "type": "value",
428
            "info": None,
429
        }
430
        with pytest.raises(ValueError):
431
            gr.CheckboxGroup(["a"], type="unknown")
432

433
        cbox = gr.CheckboxGroup(choices=["a", "b"], value="c")
434
        assert cbox.get_config()["value"] == ["c"]
435
        assert cbox.postprocess("a") == ["a"]
436
        assert cbox.process_example("a") == ["a"]
437

438
    def test_in_interface(self):
439
        """
440
        Interface, process
441
        """
442
        checkboxes_input = gr.CheckboxGroup(["a", "b", "c"])
443
        iface = gr.Interface(lambda x: "|".join(x), checkboxes_input, "textbox")
444
        assert iface(["a", "c"]) == "a|c"
445
        assert iface([]) == ""
446
        _ = gr.CheckboxGroup(["a", "b", "c"], type="index")
447

448

449
class TestRadio:
450
    def test_component_functions(self):
451
        """
452
        Preprocess, postprocess, serialize, get_config
453

454
        """
455
        radio_input = gr.Radio(["a", "b", "c"])
456
        assert radio_input.preprocess("c") == "c"
457
        assert radio_input.postprocess("a") == "a"
458
        radio_input = gr.Radio(
459
            choices=["a", "b", "c"], value="a", label="Pick Your One Input"
460
        )
461
        assert radio_input.get_config() == {
462
            "choices": [("a", "a"), ("b", "b"), ("c", "c")],
463
            "value": "a",
464
            "name": "radio",
465
            "show_label": True,
466
            "label": "Pick Your One Input",
467
            "container": True,
468
            "min_width": 160,
469
            "scale": None,
470
            "elem_id": None,
471
            "elem_classes": [],
472
            "visible": True,
473
            "interactive": None,
474
            "proxy_url": None,
475
            "_selectable": False,
476
            "type": "value",
477
            "info": None,
478
        }
479

480
        radio = gr.Radio(choices=["a", "b"], type="index")
481
        assert radio.preprocess("a") == 0
482
        assert radio.preprocess("b") == 1
483
        assert radio.preprocess("c") is None
484

485
        # When a Gradio app is loaded with gr.load, the tuples are converted to lists,
486
        # so we need to test that case as well
487
        radio = gr.Radio(["a", "b", ["c", "c full"]])  # type: ignore
488
        assert radio.choices == [("a", "a"), ("b", "b"), ("c", "c full")]
489

490
        with pytest.raises(ValueError):
491
            gr.Radio(["a", "b"], type="unknown")
492

493
    def test_in_interface(self):
494
        """
495
        Interface, process
496
        """
497
        radio_input = gr.Radio(["a", "b", "c"])
498
        iface = gr.Interface(lambda x: 2 * x, radio_input, "textbox")
499
        assert iface("c") == "cc"
500

501

502
class TestDropdown:
503
    def test_component_functions(self):
504
        """
505
        Preprocess, postprocess, serialize, get_config
506
        """
507
        dropdown_input = gr.Dropdown(["a", "b", ("c", "c full")], multiselect=True)
508
        assert dropdown_input.preprocess("a") == "a"
509
        assert dropdown_input.postprocess("a") == ["a"]
510
        assert dropdown_input.preprocess("c full") == "c full"
511
        assert dropdown_input.postprocess("c full") == ["c full"]
512

513
        # When a Gradio app is loaded with gr.load, the tuples are converted to lists,
514
        # so we need to test that case as well
515
        dropdown_input = gr.Dropdown(["a", "b", ["c", "c full"]])  # type: ignore
516
        assert dropdown_input.choices == [("a", "a"), ("b", "b"), ("c", "c full")]
517

518
        dropdown = gr.Dropdown(choices=["a", "b"], type="index")
519
        assert dropdown.preprocess("a") == 0
520
        assert dropdown.preprocess("b") == 1
521
        assert dropdown.preprocess("c") is None
522

523
        dropdown = gr.Dropdown(choices=["a", "b"], type="index", multiselect=True)
524
        assert dropdown.preprocess(["a"]) == [0]
525
        assert dropdown.preprocess(["a", "b"]) == [0, 1]
526
        assert dropdown.preprocess(["a", "b", "c"]) == [0, 1, None]
527

528
        dropdown_input_multiselect = gr.Dropdown(["a", "b", ("c", "c full")])
529
        assert dropdown_input_multiselect.preprocess(["a", "c full"]) == ["a", "c full"]
530
        assert dropdown_input_multiselect.postprocess(["a", "c full"]) == [
531
            "a",
532
            "c full",
533
        ]
534
        dropdown_input_multiselect = gr.Dropdown(
535
            value=["a", "c"],
536
            choices=["a", "b", ("c", "c full")],
537
            label="Select Your Inputs",
538
            multiselect=True,
539
            max_choices=2,
540
        )
541
        assert dropdown_input_multiselect.get_config() == {
542
            "allow_custom_value": False,
543
            "choices": [("a", "a"), ("b", "b"), ("c", "c full")],
544
            "value": ["a", "c"],
545
            "name": "dropdown",
546
            "show_label": True,
547
            "label": "Select Your Inputs",
548
            "container": True,
549
            "min_width": 160,
550
            "scale": None,
551
            "elem_id": None,
552
            "elem_classes": [],
553
            "visible": True,
554
            "interactive": None,
555
            "proxy_url": None,
556
            "multiselect": True,
557
            "filterable": True,
558
            "max_choices": 2,
559
            "_selectable": False,
560
            "type": "value",
561
            "info": None,
562
        }
563
        with pytest.raises(ValueError):
564
            gr.Dropdown(["a"], type="unknown")
565

566
        dropdown = gr.Dropdown(choices=["a", "b"], value="c")
567
        assert dropdown.get_config()["value"] == "c"
568
        assert dropdown.postprocess("a") == "a"
569

570
    def test_in_interface(self):
571
        """
572
        Interface, process
573
        """
574
        dropdown_input = gr.Dropdown(["a", "b", "c"])
575
        iface = gr.Interface(lambda x: "|".join(x), dropdown_input, "textbox")
576
        assert iface(["a", "c"]) == "a|c"
577
        assert iface([]) == ""
578

579

580
class TestImageEditor:
581
    def test_component_functions(self):
582
        test_image_path = "test/test_files/bus.png"
583
        image_data = FileData(path=test_image_path)
584
        image_editor_data = EditorData(
585
            background=image_data, layers=[image_data, image_data], composite=image_data
586
        )
587
        payload = {
588
            "background": test_image_path,
589
            "layers": [test_image_path, test_image_path],
590
            "composite": test_image_path,
591
        }
592

593
        image_editor_component = gr.ImageEditor()
594

595
        assert isinstance(image_editor_component.preprocess(image_editor_data), dict)
596
        assert image_editor_component.postprocess(payload) == image_editor_data
597

598
        # Test that ImageEditor can accept just a filepath as well
599
        simpler_data = EditorData(
600
            background=image_data, layers=[], composite=image_data
601
        )
602
        assert image_editor_component.postprocess(test_image_path) == simpler_data
603

604
        assert image_editor_component.get_config() == {
605
            "value": None,
606
            "height": None,
607
            "width": None,
608
            "image_mode": "RGBA",
609
            "sources": ("upload", "webcam", "clipboard"),
610
            "type": "numpy",
611
            "label": None,
612
            "show_label": True,
613
            "show_download_button": True,
614
            "container": True,
615
            "scale": None,
616
            "min_width": 160,
617
            "interactive": None,
618
            "visible": True,
619
            "elem_id": None,
620
            "elem_classes": [],
621
            "mirror_webcam": True,
622
            "show_share_button": False,
623
            "_selectable": False,
624
            "crop_size": None,
625
            "transforms": ("crop",),
626
            "eraser": {"default_size": "auto"},
627
            "brush": {
628
                "default_size": "auto",
629
                "colors": [
630
                    "rgb(204, 50, 50)",
631
                    "rgb(173, 204, 50)",
632
                    "rgb(50, 204, 112)",
633
                    "rgb(50, 112, 204)",
634
                    "rgb(173, 50, 204)",
635
                ],
636
                "default_color": "auto",
637
                "color_mode": "defaults",
638
            },
639
            "proxy_url": None,
640
            "name": "imageeditor",
641
        }
642

643
    def test_process_example(self):
644
        test_image_path = "test/test_files/bus.png"
645
        image_editor = gr.ImageEditor()
646
        example_value = image_editor.process_example(test_image_path)
647
        assert isinstance(example_value, EditorData)
648
        assert example_value.background and example_value.background.path
649

650

651
class TestImage:
652
    def test_component_functions(self, gradio_temp_dir):
653
        """
654
        Preprocess, postprocess, serialize, get_config, _segment_by_slic
655
        type: pil, file, filepath, numpy
656
        """
657

658
        img = FileData(path="test/test_files/bus.png")
659
        image_input = gr.Image()
660

661
        image_input = gr.Image(type="filepath")
662
        image_temp_filepath = image_input.preprocess(img)
663
        assert image_temp_filepath in [
664
            str(f) for f in gradio_temp_dir.glob("**/*") if f.is_file()
665
        ]
666

667
        image_input = gr.Image(type="pil", label="Upload Your Image")
668
        assert image_input.get_config() == {
669
            "image_mode": "RGB",
670
            "sources": ["upload", "webcam", "clipboard"],
671
            "name": "image",
672
            "show_share_button": False,
673
            "show_download_button": True,
674
            "streaming": False,
675
            "show_label": True,
676
            "label": "Upload Your Image",
677
            "container": True,
678
            "min_width": 160,
679
            "scale": None,
680
            "height": None,
681
            "width": None,
682
            "elem_id": None,
683
            "elem_classes": [],
684
            "visible": True,
685
            "value": None,
686
            "interactive": None,
687
            "proxy_url": None,
688
            "mirror_webcam": True,
689
            "_selectable": False,
690
            "streamable": False,
691
            "type": "pil",
692
        }
693
        assert image_input.preprocess(None) is None
694
        image_input = gr.Image()
695
        assert image_input.preprocess(img) is not None
696
        image_input.preprocess(img)
697
        file_image = gr.Image(type="filepath")
698
        assert isinstance(file_image.preprocess(img), str)
699
        with pytest.raises(ValueError):
700
            gr.Image(type="unknown")
701

702
        string_source = gr.Image(sources="upload")
703
        assert string_source.sources == ["upload"]
704
        # Output functionalities
705
        image_output = gr.Image(type="pil")
706
        processed_image = image_output.postprocess(
707
            PIL.Image.open(img.path)
708
        ).model_dump()
709
        assert processed_image is not None
710
        if processed_image is not None:
711
            processed = PIL.Image.open(cast(dict, processed_image).get("path", ""))
712
            source = PIL.Image.open(img.path)
713
            assert processed.size == source.size
714

715
    def test_in_interface_as_output(self):
716
        """
717
        Interface, process
718
        """
719

720
        def generate_noise(height, width):
721
            return np.random.randint(0, 256, (height, width, 3))
722

723
        iface = gr.Interface(generate_noise, ["slider", "slider"], "image")
724
        assert iface(10, 20).endswith(".png")
725

726
    def test_static(self):
727
        """
728
        postprocess
729
        """
730
        component = gr.Image("test/test_files/bus.png")
731
        value = component.get_config().get("value")
732
        base64 = client_utils.encode_file_to_base64(value["path"])
733
        assert base64 == media_data.BASE64_IMAGE
734
        component = gr.Image(None)
735
        assert component.get_config().get("value") is None
736

737
    def test_images_upright_after_preprocess(self):
738
        component = gr.Image(type="pil")
739
        file_path = "test/test_files/rotated_image.jpeg"
740
        im = PIL.Image.open(file_path)
741
        assert im.getexif().get(274) != 1
742
        image = component.preprocess(FileData(path=file_path))
743
        assert image == PIL.ImageOps.exif_transpose(im)
744

745

746
class TestPlot:
747
    @pytest.mark.asyncio
748
    async def test_in_interface_as_output(self):
749
        """
750
        Interface, process
751
        """
752

753
        def plot(num):
754
            import matplotlib.pyplot as plt
755

756
            fig = plt.figure()
757
            plt.plot(range(num), range(num))
758
            return fig
759

760
        iface = gr.Interface(plot, "slider", "plot")
761
        with utils.MatplotlibBackendMananger():
762
            output = await iface.process_api(fn_index=0, inputs=[10], state={})
763
        assert output["data"][0]["type"] == "matplotlib"
764
        assert output["data"][0]["plot"].startswith("data:image/png;base64")
765

766
    def test_static(self):
767
        """
768
        postprocess
769
        """
770
        with utils.MatplotlibBackendMananger():
771
            import matplotlib.pyplot as plt
772

773
            fig = plt.figure()
774
            plt.plot([1, 2, 3], [1, 2, 3])
775

776
        component = gr.Plot(fig)
777
        assert component.get_config().get("value") is not None
778
        component = gr.Plot(None)
779
        assert component.get_config().get("value") is None
780

781
    def test_postprocess_altair(self):
782
        import altair as alt
783
        from vega_datasets import data
784

785
        cars = data.cars()
786
        chart = (
787
            alt.Chart(cars)
788
            .mark_point()
789
            .encode(
790
                x="Horsepower",
791
                y="Miles_per_Gallon",
792
                color="Origin",
793
            )
794
        )
795
        out = gr.Plot().postprocess(chart).model_dump()
796
        assert isinstance(out["plot"], str)
797
        assert out["plot"] == chart.to_json()
798

799

800
class TestAudio:
801
    def test_component_functions(self, gradio_temp_dir):
802
        """
803
        Preprocess, postprocess serialize, get_config, deserialize
804
        type: filepath, numpy, file
805
        """
806
        x_wav = FileData(path=media_data.BASE64_AUDIO["path"])
807
        audio_input = gr.Audio()
808
        output1 = audio_input.preprocess(x_wav)
809
        assert output1[0] == 8000
810
        assert output1[1].shape == (8046,)
811

812
        x_wav = processing_utils.move_files_to_cache([x_wav], audio_input)[0]
813
        audio_input = gr.Audio(type="filepath")
814
        output1 = audio_input.preprocess(x_wav)
815
        assert Path(output1).name.endswith("audio_sample.wav")
816

817
        audio_input = gr.Audio(label="Upload Your Audio")
818
        assert audio_input.get_config() == {
819
            "autoplay": False,
820
            "sources": ["upload", "microphone"],
821
            "name": "audio",
822
            "show_download_button": None,
823
            "show_share_button": False,
824
            "streaming": False,
825
            "show_label": True,
826
            "label": "Upload Your Audio",
827
            "container": True,
828
            "editable": True,
829
            "min_width": 160,
830
            "scale": None,
831
            "elem_id": None,
832
            "elem_classes": [],
833
            "visible": True,
834
            "value": None,
835
            "interactive": None,
836
            "proxy_url": None,
837
            "type": "numpy",
838
            "format": "wav",
839
            "streamable": False,
840
            "max_length": None,
841
            "min_length": None,
842
            "waveform_options": {
843
                "sample_rate": 44100,
844
                "show_controls": False,
845
                "show_recording_waveform": True,
846
                "skip_length": 5,
847
                "waveform_color": None,
848
                "waveform_progress_color": None,
849
                "trim_region_color": None,
850
            },
851
            "_selectable": False,
852
        }
853
        assert audio_input.preprocess(None) is None
854

855
        audio_input = gr.Audio(type="filepath")
856
        assert isinstance(audio_input.preprocess(x_wav), str)
857
        with pytest.raises(ValueError):
858
            gr.Audio(type="unknown")
859

860
        rng = np.random.default_rng()
861
        # Confirm Audio can be instantiated with a numpy array
862
        gr.Audio((100, rng.random(size=(1000, 2))), label="Play your audio")
863

864
        # Output functionalities
865
        y_audio = client_utils.decode_base64_to_file(
866
            deepcopy(media_data.BASE64_AUDIO)["data"]
867
        )
868
        audio_output = gr.Audio(type="filepath")
869
        assert filecmp.cmp(
870
            y_audio.name, audio_output.postprocess(y_audio.name).model_dump()["path"]
871
        )
872
        assert audio_output.get_config() == {
873
            "autoplay": False,
874
            "name": "audio",
875
            "show_download_button": None,
876
            "show_share_button": False,
877
            "streaming": False,
878
            "show_label": True,
879
            "label": None,
880
            "max_length": None,
881
            "min_length": None,
882
            "container": True,
883
            "editable": True,
884
            "min_width": 160,
885
            "scale": None,
886
            "elem_id": None,
887
            "elem_classes": [],
888
            "visible": True,
889
            "value": None,
890
            "interactive": None,
891
            "proxy_url": None,
892
            "type": "filepath",
893
            "format": "wav",
894
            "streamable": False,
895
            "sources": ["upload", "microphone"],
896
            "waveform_options": {
897
                "sample_rate": 44100,
898
                "show_controls": False,
899
                "show_recording_waveform": True,
900
                "skip_length": 5,
901
                "waveform_color": None,
902
                "waveform_progress_color": None,
903
                "trim_region_color": None,
904
            },
905
            "_selectable": False,
906
        }
907

908
        output1 = audio_output.postprocess(y_audio.name).model_dump()
909
        output2 = audio_output.postprocess(Path(y_audio.name)).model_dump()
910
        assert output1 == output2
911

912
    def test_default_value_postprocess(self):
913
        x_wav = deepcopy(media_data.BASE64_AUDIO)
914
        audio = gr.Audio(value=x_wav["path"])
915
        assert utils.is_in_or_equal(audio.value["path"], audio.GRADIO_CACHE)
916

917
    def test_in_interface(self):
918
        def reverse_audio(audio):
919
            sr, data = audio
920
            return (sr, np.flipud(data))
921

922
        iface = gr.Interface(reverse_audio, "audio", "audio")
923
        reversed_file = iface("test/test_files/audio_sample.wav")
924
        reversed_reversed_file = iface(reversed_file)
925
        reversed_reversed_data = client_utils.encode_url_or_file_to_base64(
926
            reversed_reversed_file
927
        )
928
        similarity = SequenceMatcher(
929
            a=reversed_reversed_data, b=media_data.BASE64_AUDIO["data"]
930
        ).ratio()
931
        assert similarity > 0.99
932

933
    def test_in_interface_as_output(self):
934
        """
935
        Interface, process
936
        """
937

938
        def generate_noise(duration):
939
            return 48000, np.random.randint(-256, 256, (duration, 3)).astype(np.int16)
940

941
        iface = gr.Interface(generate_noise, "slider", "audio")
942
        assert iface(100).endswith(".wav")
943

944
    def test_audio_preprocess_can_be_read_by_scipy(self, gradio_temp_dir):
945
        x_wav = FileData(
946
            path=processing_utils.save_base64_to_cache(
947
                media_data.BASE64_MICROPHONE["data"], cache_dir=gradio_temp_dir
948
            )
949
        )
950
        audio_input = gr.Audio(type="filepath")
951
        output = audio_input.preprocess(x_wav)
952
        wavfile.read(output)
953

954
    def test_prepost_process_to_mp3(self, gradio_temp_dir):
955
        x_wav = FileData(
956
            path=processing_utils.save_base64_to_cache(
957
                media_data.BASE64_MICROPHONE["data"], cache_dir=gradio_temp_dir
958
            )
959
        )
960
        audio_input = gr.Audio(type="filepath", format="mp3")
961
        output = audio_input.preprocess(x_wav)
962
        assert output.endswith("mp3")
963
        output = audio_input.postprocess(
964
            (48000, np.random.randint(-256, 256, (5, 3)).astype(np.int16))
965
        ).model_dump()
966
        assert output["path"].endswith("mp3")
967

968

969
class TestFile:
970
    def test_component_functions(self):
971
        """
972
        Preprocess, serialize, get_config, value
973
        """
974
        x_file = FileData(path=media_data.BASE64_FILE["path"])
975
        file_input = gr.File()
976
        output = file_input.preprocess(x_file)
977
        assert isinstance(output, str)
978

979
        input1 = file_input.preprocess(x_file)
980
        input2 = file_input.preprocess(x_file)
981
        assert input1 == input1.name  # Testing backwards compatibility
982
        assert input1 == input2
983
        assert Path(input1).name == "sample_file.pdf"
984

985
        file_input = gr.File(label="Upload Your File")
986
        assert file_input.get_config() == {
987
            "file_count": "single",
988
            "file_types": None,
989
            "name": "file",
990
            "show_label": True,
991
            "label": "Upload Your File",
992
            "container": True,
993
            "min_width": 160,
994
            "scale": None,
995
            "elem_id": None,
996
            "elem_classes": [],
997
            "visible": True,
998
            "value": None,
999
            "interactive": None,
1000
            "proxy_url": None,
1001
            "_selectable": False,
1002
            "height": None,
1003
            "type": "filepath",
1004
        }
1005
        assert file_input.preprocess(None) is None
1006
        assert file_input.preprocess(x_file) is not None
1007

1008
        zero_size_file = FileData(path="document.txt", size=0)
1009
        temp_file = file_input.preprocess(zero_size_file)
1010
        assert not Path(temp_file.name).exists()
1011

1012
        file_input = gr.File(type="binary")
1013
        output = file_input.preprocess(x_file)
1014
        assert isinstance(output, bytes)
1015

1016
        output1 = file_input.postprocess("test/test_files/sample_file.pdf")
1017
        output2 = file_input.postprocess("test/test_files/sample_file.pdf")
1018
        assert output1 == output2
1019

1020
    def test_preprocess_with_multiple_files(self):
1021
        file_data = FileData(path=media_data.BASE64_FILE["path"])
1022
        list_file_data = ListFiles(root=[file_data, file_data])
1023
        file_input = gr.File(file_count="directory")
1024
        output = file_input.preprocess(list_file_data)
1025
        assert isinstance(output, list)
1026
        assert isinstance(output[0], str)
1027

1028
    def test_file_type_must_be_list(self):
1029
        with pytest.raises(
1030
            ValueError, match="Parameter file_types must be a list. Received str"
1031
        ):
1032
            gr.File(file_types=".json")
1033

1034
    def test_in_interface_as_input(self):
1035
        """
1036
        Interface, process
1037
        """
1038
        x_file = media_data.BASE64_FILE["path"]
1039

1040
        def get_size_of_file(file_obj):
1041
            return os.path.getsize(file_obj.name)
1042

1043
        iface = gr.Interface(get_size_of_file, "file", "number")
1044
        assert iface(x_file) == 10558
1045

1046
    def test_as_component_as_output(self):
1047
        """
1048
        Interface, process
1049
        """
1050

1051
        def write_file(content):
1052
            with open("test.txt", "w") as f:
1053
                f.write(content)
1054
            return "test.txt"
1055

1056
        iface = gr.Interface(write_file, "text", "file")
1057
        assert iface("hello world").endswith(".txt")
1058

1059

1060
class TestUploadButton:
1061
    def test_component_functions(self):
1062
        """
1063
        preprocess
1064
        """
1065
        x_file = FileData(path=media_data.BASE64_FILE["path"])
1066
        upload_input = gr.UploadButton()
1067
        input = upload_input.preprocess(x_file)
1068
        assert isinstance(input, str)
1069

1070
        input1 = upload_input.preprocess(x_file)
1071
        input2 = upload_input.preprocess(x_file)
1072
        assert input1 == input1.name  # Testing backwards compatibility
1073
        assert input1 == input2
1074

1075
    def test_raises_if_file_types_is_not_list(self):
1076
        with pytest.raises(
1077
            ValueError, match="Parameter file_types must be a list. Received int"
1078
        ):
1079
            gr.UploadButton(file_types=2)
1080

1081
    def test_preprocess_with_multiple_files(self):
1082
        file_data = FileData(path=media_data.BASE64_FILE["path"])
1083
        list_file_data = ListFiles(root=[file_data, file_data])
1084
        upload_input = gr.UploadButton(file_count="directory")
1085
        output = upload_input.preprocess(list_file_data)
1086
        assert isinstance(output, list)
1087
        assert isinstance(output[0], str)
1088

1089

1090
class TestDataframe:
1091
    def test_component_functions(self):
1092
        """
1093
        Preprocess, serialize, get_config
1094
        """
1095
        x_data = {
1096
            "data": [["Tim", 12, False], ["Jan", 24, True]],
1097
            "headers": ["Name", "Age", "Member"],
1098
            "metadata": None,
1099
        }
1100
        x_payload = DataframeData(**x_data)
1101
        dataframe_input = gr.Dataframe(headers=["Name", "Age", "Member"])
1102
        output = dataframe_input.preprocess(x_payload)
1103
        assert output["Age"][1] == 24
1104
        assert not output["Member"][0]
1105
        assert dataframe_input.postprocess(output) == x_payload
1106

1107
        dataframe_input = gr.Dataframe(
1108
            headers=["Name", "Age", "Member"], label="Dataframe Input"
1109
        )
1110
        assert dataframe_input.get_config() == {
1111
            "value": {
1112
                "headers": ["Name", "Age", "Member"],
1113
                "data": [["", "", ""]],
1114
                "metadata": None,
1115
            },
1116
            "_selectable": False,
1117
            "headers": ["Name", "Age", "Member"],
1118
            "row_count": (1, "dynamic"),
1119
            "col_count": (3, "dynamic"),
1120
            "datatype": "str",
1121
            "type": "pandas",
1122
            "label": "Dataframe Input",
1123
            "show_label": True,
1124
            "scale": None,
1125
            "min_width": 160,
1126
            "interactive": None,
1127
            "visible": True,
1128
            "elem_id": None,
1129
            "elem_classes": [],
1130
            "wrap": False,
1131
            "proxy_url": None,
1132
            "name": "dataframe",
1133
            "height": 500,
1134
            "latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],
1135
            "line_breaks": True,
1136
            "column_widths": [],
1137
        }
1138
        dataframe_input = gr.Dataframe()
1139
        output = dataframe_input.preprocess(DataframeData(**x_data))
1140
        assert output["Age"][1] == 24
1141

1142
        x_data = {
1143
            "data": [["Tim", 12, False], ["Jan", 24, True]],
1144
            "headers": ["Name", "Age", "Member"],
1145
            "metadata": {"display_value": None, "styling": None},
1146
        }
1147
        dataframe_input.preprocess(DataframeData(**x_data))
1148

1149
        with pytest.raises(ValueError):
1150
            gr.Dataframe(type="unknown")
1151

1152
        dataframe_output = gr.Dataframe()
1153
        assert dataframe_output.get_config() == {
1154
            "value": {
1155
                "headers": ["1", "2", "3"],
1156
                "data": [["", "", ""]],
1157
                "metadata": None,
1158
            },
1159
            "_selectable": False,
1160
            "headers": ["1", "2", "3"],
1161
            "row_count": (1, "dynamic"),
1162
            "col_count": (3, "dynamic"),
1163
            "datatype": "str",
1164
            "type": "pandas",
1165
            "label": None,
1166
            "show_label": True,
1167
            "scale": None,
1168
            "min_width": 160,
1169
            "interactive": None,
1170
            "visible": True,
1171
            "elem_id": None,
1172
            "elem_classes": [],
1173
            "wrap": False,
1174
            "proxy_url": None,
1175
            "name": "dataframe",
1176
            "height": 500,
1177
            "latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],
1178
            "line_breaks": True,
1179
            "column_widths": [],
1180
        }
1181

1182
        dataframe_input = gr.Dataframe(column_widths=["100px", 200, "50%"])
1183
        assert dataframe_input.get_config()["column_widths"] == [
1184
            "100px",
1185
            "200px",
1186
            "50%",
1187
        ]
1188

1189
    def test_postprocess(self):
1190
        """
1191
        postprocess
1192
        """
1193
        dataframe_output = gr.Dataframe()
1194
        output = dataframe_output.postprocess([]).model_dump()
1195
        assert output == {"data": [[]], "headers": ["1", "2", "3"], "metadata": None}
1196
        output = dataframe_output.postprocess(np.zeros((2, 2))).model_dump()
1197
        assert output == {
1198
            "data": [[0, 0], [0, 0]],
1199
            "headers": ["1", "2"],
1200
            "metadata": None,
1201
        }
1202
        output = dataframe_output.postprocess([[1, 3, 5]]).model_dump()
1203
        assert output == {
1204
            "data": [[1, 3, 5]],
1205
            "headers": ["1", "2", "3"],
1206
            "metadata": None,
1207
        }
1208
        output = dataframe_output.postprocess(
1209
            pd.DataFrame([[2, True], [3, True], [4, False]], columns=["num", "prime"])
1210
        ).model_dump()
1211
        assert output == {
1212
            "headers": ["num", "prime"],
1213
            "data": [[2, True], [3, True], [4, False]],
1214
            "metadata": None,
1215
        }
1216
        with pytest.raises(ValueError):
1217
            gr.Dataframe(type="unknown")
1218

1219
        # When the headers don't match the data
1220
        dataframe_output = gr.Dataframe(headers=["one", "two", "three"])
1221
        output = dataframe_output.postprocess([[2, True], [3, True]]).model_dump()
1222
        assert output == {
1223
            "headers": ["one", "two"],
1224
            "data": [[2, True], [3, True]],
1225
            "metadata": None,
1226
        }
1227
        dataframe_output = gr.Dataframe(headers=["one", "two", "three"])
1228
        output = dataframe_output.postprocess(
1229
            [[2, True, "ab", 4], [3, True, "cd", 5]]
1230
        ).model_dump()
1231
        assert output == {
1232
            "headers": ["one", "two", "three", "4"],
1233
            "data": [[2, True, "ab", 4], [3, True, "cd", 5]],
1234
            "metadata": None,
1235
        }
1236

1237
    def test_dataframe_postprocess_all_types(self):
1238
        df = pd.DataFrame(
1239
            {
1240
                "date_1": pd.date_range("2021-01-01", periods=2),
1241
                "date_2": pd.date_range("2022-02-15", periods=2).strftime(
1242
                    "%B %d, %Y, %r"
1243
                ),
1244
                "number": np.array([0.2233, 0.57281]),
1245
                "number_2": np.array([84, 23]).astype(np.int64),
1246
                "bool": [True, False],
1247
                "markdown": ["# Hello", "# Goodbye"],
1248
            }
1249
        )
1250
        component = gr.Dataframe(
1251
            datatype=["date", "date", "number", "number", "bool", "markdown"]
1252
        )
1253
        output = component.postprocess(df).model_dump()
1254
        assert output == {
1255
            "headers": list(df.columns),
1256
            "data": [
1257
                [
1258
                    pd.Timestamp("2021-01-01 00:00:00"),
1259
                    "February 15, 2022, 12:00:00 AM",
1260
                    0.2233,
1261
                    84,
1262
                    True,
1263
                    "# Hello",
1264
                ],
1265
                [
1266
                    pd.Timestamp("2021-01-02 00:00:00"),
1267
                    "February 16, 2022, 12:00:00 AM",
1268
                    0.57281,
1269
                    23,
1270
                    False,
1271
                    "# Goodbye",
1272
                ],
1273
            ],
1274
            "metadata": None,
1275
        }
1276

1277
    def test_dataframe_postprocess_only_dates(self):
1278
        df = pd.DataFrame(
1279
            {
1280
                "date_1": pd.date_range("2021-01-01", periods=2),
1281
                "date_2": pd.date_range("2022-02-15", periods=2),
1282
            }
1283
        )
1284
        component = gr.Dataframe(datatype=["date", "date"])
1285
        output = component.postprocess(df).model_dump()
1286
        assert output == {
1287
            "headers": list(df.columns),
1288
            "data": [
1289
                [
1290
                    pd.Timestamp("2021-01-01 00:00:00"),
1291
                    pd.Timestamp("2022-02-15 00:00:00"),
1292
                ],
1293
                [
1294
                    pd.Timestamp("2021-01-02 00:00:00"),
1295
                    pd.Timestamp("2022-02-16 00:00:00"),
1296
                ],
1297
            ],
1298
            "metadata": None,
1299
        }
1300

1301
    def test_dataframe_postprocess_styler(self):
1302
        component = gr.Dataframe()
1303
        df = pd.DataFrame(
1304
            {
1305
                "name": ["Adam", "Mike"] * 4,
1306
                "gpa": [1.1, 1.12] * 4,
1307
                "sat": [800, 800] * 4,
1308
            }
1309
        )
1310
        s = df.style.format(precision=1, decimal=",")
1311
        output = component.postprocess(s).model_dump()
1312
        assert output == {
1313
            "data": [
1314
                ["Adam", 1.1, 800],
1315
                ["Mike", 1.12, 800],
1316
                ["Adam", 1.1, 800],
1317
                ["Mike", 1.12, 800],
1318
                ["Adam", 1.1, 800],
1319
                ["Mike", 1.12, 800],
1320
                ["Adam", 1.1, 800],
1321
                ["Mike", 1.12, 800],
1322
            ],
1323
            "headers": ["name", "gpa", "sat"],
1324
            "metadata": {
1325
                "display_value": [
1326
                    ["Adam", "1,1", "800"],
1327
                    ["Mike", "1,1", "800"],
1328
                    ["Adam", "1,1", "800"],
1329
                    ["Mike", "1,1", "800"],
1330
                    ["Adam", "1,1", "800"],
1331
                    ["Mike", "1,1", "800"],
1332
                    ["Adam", "1,1", "800"],
1333
                    ["Mike", "1,1", "800"],
1334
                ],
1335
                "styling": [
1336
                    ["", "", ""],
1337
                    ["", "", ""],
1338
                    ["", "", ""],
1339
                    ["", "", ""],
1340
                    ["", "", ""],
1341
                    ["", "", ""],
1342
                    ["", "", ""],
1343
                    ["", "", ""],
1344
                ],
1345
            },
1346
        }
1347

1348
        df = pd.DataFrame(
1349
            {
1350
                "A": [14, 4, 5, 4, 1],
1351
                "B": [5, 2, 54, 3, 2],
1352
                "C": [20, 20, 7, 3, 8],
1353
                "D": [14, 3, 6, 2, 6],
1354
                "E": [23, 45, 64, 32, 23],
1355
            }
1356
        )
1357

1358
        t = df.style.highlight_max(color="lightgreen", axis=0)
1359
        output = component.postprocess(t).model_dump()
1360
        assert output == {
1361
            "data": [
1362
                [14, 5, 20, 14, 23],
1363
                [4, 2, 20, 3, 45],
1364
                [5, 54, 7, 6, 64],
1365
                [4, 3, 3, 2, 32],
1366
                [1, 2, 8, 6, 23],
1367
            ],
1368
            "headers": ["A", "B", "C", "D", "E"],
1369
            "metadata": {
1370
                "display_value": [
1371
                    ["14", "5", "20", "14", "23"],
1372
                    ["4", "2", "20", "3", "45"],
1373
                    ["5", "54", "7", "6", "64"],
1374
                    ["4", "3", "3", "2", "32"],
1375
                    ["1", "2", "8", "6", "23"],
1376
                ],
1377
                "styling": [
1378
                    [
1379
                        "background-color: lightgreen",
1380
                        "",
1381
                        "background-color: lightgreen",
1382
                        "background-color: lightgreen",
1383
                        "",
1384
                    ],
1385
                    ["", "", "background-color: lightgreen", "", ""],
1386
                    [
1387
                        "",
1388
                        "background-color: lightgreen",
1389
                        "",
1390
                        "",
1391
                        "background-color: lightgreen",
1392
                    ],
1393
                    ["", "", "", "", ""],
1394
                    ["", "", "", "", ""],
1395
                ],
1396
            },
1397
        }
1398

1399

1400
class TestDataset:
1401
    def test_preprocessing(self):
1402
        test_file_dir = Path(__file__).parent / "test_files"
1403
        bus = str(Path(test_file_dir, "bus.png").resolve())
1404

1405
        dataset = gr.Dataset(
1406
            components=["number", "textbox", "image", "html", "markdown"],
1407
            samples=[
1408
                [5, "hello", bus, "<b>Bold</b>", "**Bold**"],
1409
                [15, "hi", bus, "<i>Italics</i>", "*Italics*"],
1410
            ],
1411
        )
1412

1413
        row = dataset.preprocess(1)
1414
        assert row[0] == 15
1415
        assert row[1] == "hi"
1416
        assert row[2]["path"].endswith("bus.png")
1417
        assert row[3] == "<i>Italics</i>"
1418
        assert row[4] == "*Italics*"
1419

1420
        dataset = gr.Dataset(
1421
            components=["number", "textbox", "image", "html", "markdown"],
1422
            samples=[
1423
                [5, "hello", bus, "<b>Bold</b>", "**Bold**"],
1424
                [15, "hi", bus, "<i>Italics</i>", "*Italics*"],
1425
            ],
1426
            type="index",
1427
        )
1428

1429
        assert dataset.preprocess(1) == 1
1430

1431
        radio = gr.Radio(choices=[("name 1", "value 1"), ("name 2", "value 2")])
1432
        dataset = gr.Dataset(samples=[["value 1"], ["value 2"]], components=[radio])
1433
        assert dataset.samples == [["value 1"], ["value 2"]]
1434

1435
    def test_postprocessing(self):
1436
        test_file_dir = Path(Path(__file__).parent, "test_files")
1437
        bus = Path(test_file_dir, "bus.png")
1438

1439
        dataset = gr.Dataset(
1440
            components=["number", "textbox", "image", "html", "markdown"], type="index"
1441
        )
1442

1443
        output = dataset.postprocess(
1444
            samples=[
1445
                [5, "hello", bus, "<b>Bold</b>", "**Bold**"],
1446
                [15, "hi", bus, "<i>Italics</i>", "*Italics*"],
1447
            ],
1448
        )
1449

1450
        assert output == {
1451
            "samples": [
1452
                [5, "hello", bus, "<b>Bold</b>", "**Bold**"],
1453
                [15, "hi", bus, "<i>Italics</i>", "*Italics*"],
1454
            ],
1455
            "__type__": "update",
1456
        }
1457

1458

1459
class TestVideo:
1460
    def test_component_functions(self):
1461
        """
1462
        Preprocess, serialize, deserialize, get_config
1463
        """
1464
        x_video = VideoData(
1465
            video=FileData(path=deepcopy(media_data.BASE64_VIDEO)["path"])
1466
        )
1467
        video_input = gr.Video()
1468

1469
        x_video = processing_utils.move_files_to_cache([x_video], video_input)[0]
1470

1471
        output1 = video_input.preprocess(x_video)
1472
        assert isinstance(output1, str)
1473
        output2 = video_input.preprocess(x_video)
1474
        assert output1 == output2
1475

1476
        video_input = gr.Video(include_audio=False)
1477
        output1 = video_input.preprocess(x_video)
1478
        output2 = video_input.preprocess(x_video)
1479
        assert output1 == output2
1480

1481
        video_input = gr.Video(label="Upload Your Video")
1482
        assert video_input.get_config() == {
1483
            "autoplay": False,
1484
            "sources": ["upload", "webcam"],
1485
            "name": "video",
1486
            "show_share_button": False,
1487
            "show_label": True,
1488
            "label": "Upload Your Video",
1489
            "container": True,
1490
            "min_width": 160,
1491
            "scale": None,
1492
            "show_download_button": None,
1493
            "height": None,
1494
            "width": None,
1495
            "elem_id": None,
1496
            "elem_classes": [],
1497
            "visible": True,
1498
            "value": None,
1499
            "interactive": None,
1500
            "proxy_url": None,
1501
            "mirror_webcam": True,
1502
            "include_audio": True,
1503
            "format": None,
1504
            "min_length": None,
1505
            "max_length": None,
1506
            "_selectable": False,
1507
        }
1508
        assert video_input.preprocess(None) is None
1509
        video_input = gr.Video(format="avi")
1510
        output_video = video_input.preprocess(x_video)
1511
        assert output_video[-3:] == "avi"
1512
        assert "flip" not in output_video
1513

1514
        # Output functionalities
1515
        y_vid_path = "test/test_files/video_sample.mp4"
1516
        subtitles_path = "test/test_files/s1.srt"
1517
        video_output = gr.Video()
1518
        output1 = video_output.postprocess(y_vid_path).model_dump()["video"]["path"]
1519
        assert output1.endswith("mp4")
1520
        output2 = video_output.postprocess(y_vid_path).model_dump()["video"]["path"]
1521
        assert output1 == output2
1522
        assert (
1523
            video_output.postprocess(y_vid_path).model_dump()["video"]["orig_name"]
1524
            == "video_sample.mp4"
1525
        )
1526
        output_with_subtitles = video_output.postprocess(
1527
            (y_vid_path, subtitles_path)
1528
        ).model_dump()
1529
        assert output_with_subtitles["subtitles"]["path"].endswith(".vtt")
1530

1531
        p_video = gr.Video()
1532
        video_with_subtitle = gr.Video()
1533
        postprocessed_video = p_video.postprocess(Path(y_vid_path)).model_dump()
1534
        postprocessed_video_with_subtitle = video_with_subtitle.postprocess(
1535
            (Path(y_vid_path), Path(subtitles_path))
1536
        ).model_dump()
1537

1538
        processed_video = {
1539
            "video": {
1540
                "path": "video_sample.mp4",
1541
                "orig_name": "video_sample.mp4",
1542
                "mime_type": None,
1543
                "size": None,
1544
                "url": None,
1545
                "is_stream": False,
1546
                "meta": {"_type": "gradio.FileData"},
1547
            },
1548
            "subtitles": None,
1549
        }
1550

1551
        processed_video_with_subtitle = {
1552
            "video": {
1553
                "path": "video_sample.mp4",
1554
                "orig_name": "video_sample.mp4",
1555
                "mime_type": None,
1556
                "size": None,
1557
                "url": None,
1558
                "is_stream": False,
1559
                "meta": {"_type": "gradio.FileData"},
1560
            },
1561
            "subtitles": {
1562
                "path": "s1.srt",
1563
                "mime_type": None,
1564
                "orig_name": None,
1565
                "size": None,
1566
                "url": None,
1567
                "is_stream": False,
1568
                "meta": {"_type": "gradio.FileData"},
1569
            },
1570
        }
1571
        postprocessed_video["video"]["path"] = os.path.basename(
1572
            postprocessed_video["video"]["path"]
1573
        )
1574
        assert processed_video == postprocessed_video
1575
        postprocessed_video_with_subtitle["video"]["path"] = os.path.basename(
1576
            postprocessed_video_with_subtitle["video"]["path"]
1577
        )
1578
        if postprocessed_video_with_subtitle["subtitles"]["path"]:
1579
            postprocessed_video_with_subtitle["subtitles"]["path"] = "s1.srt"
1580
        assert processed_video_with_subtitle == postprocessed_video_with_subtitle
1581

1582
    def test_in_interface(self):
1583
        """
1584
        Interface, process
1585
        """
1586
        x_video = media_data.BASE64_VIDEO["path"]
1587
        iface = gr.Interface(lambda x: x, "video", "playable_video")
1588
        assert iface({"video": x_video})["video"].endswith(".mp4")
1589

1590
    def test_with_waveform(self):
1591
        """
1592
        Interface, process
1593
        """
1594
        x_audio = media_data.BASE64_AUDIO["path"]
1595
        iface = gr.Interface(lambda x: gr.make_waveform(x), "audio", "video")
1596
        assert iface(x_audio)["video"].endswith(".mp4")
1597

1598
    def test_video_postprocess_converts_to_playable_format(self):
1599
        test_file_dir = Path(Path(__file__).parent, "test_files")
1600
        # This file has a playable container but not playable codec
1601
        with tempfile.NamedTemporaryFile(
1602
            suffix="bad_video.mp4", delete=False
1603
        ) as tmp_not_playable_vid:
1604
            bad_vid = str(test_file_dir / "bad_video_sample.mp4")
1605
            assert not processing_utils.video_is_playable(bad_vid)
1606
            shutil.copy(bad_vid, tmp_not_playable_vid.name)
1607
            output = gr.Video().postprocess(tmp_not_playable_vid.name).model_dump()
1608
            assert processing_utils.video_is_playable(output["video"]["path"])
1609

1610
        # This file has a playable codec but not a playable container
1611
        with tempfile.NamedTemporaryFile(
1612
            suffix="playable_but_bad_container.mkv", delete=False
1613
        ) as tmp_not_playable_vid:
1614
            bad_vid = str(test_file_dir / "playable_but_bad_container.mkv")
1615
            assert not processing_utils.video_is_playable(bad_vid)
1616
            shutil.copy(bad_vid, tmp_not_playable_vid.name)
1617
            output = gr.Video().postprocess(tmp_not_playable_vid.name).model_dump()
1618
            assert processing_utils.video_is_playable(output["video"]["path"])
1619

1620
    @patch("pathlib.Path.exists", MagicMock(return_value=False))
1621
    @patch("gradio.components.video.FFmpeg")
1622
    def test_video_preprocessing_flips_video_for_webcam(self, mock_ffmpeg):
1623
        # Ensures that the cached temp video file is not used so that ffmpeg is called for each test
1624
        x_video = VideoData(video=FileData(path=media_data.BASE64_VIDEO["path"]))
1625
        video_input = gr.Video(sources=["webcam"])
1626
        _ = video_input.preprocess(x_video)
1627

1628
        # Dict mapping filename to FFmpeg options
1629
        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
1630
        assert "hflip" in list(output_params.values())[0]
1631
        assert "flip" in list(output_params.keys())[0]
1632

1633
        mock_ffmpeg.reset_mock()
1634
        _ = gr.Video(
1635
            sources=["webcam"], mirror_webcam=False, include_audio=True
1636
        ).preprocess(x_video)
1637
        mock_ffmpeg.assert_not_called()
1638

1639
        mock_ffmpeg.reset_mock()
1640
        _ = gr.Video(sources=["upload"], format="mp4", include_audio=True).preprocess(
1641
            x_video
1642
        )
1643
        mock_ffmpeg.assert_not_called()
1644

1645
        mock_ffmpeg.reset_mock()
1646
        output_file = gr.Video(
1647
            sources=["webcam"], mirror_webcam=True, format="avi"
1648
        ).preprocess(x_video)
1649
        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
1650
        assert "hflip" in list(output_params.values())[0]
1651
        assert "flip" in list(output_params.keys())[0]
1652
        assert ".avi" in list(output_params.keys())[0]
1653
        assert ".avi" in output_file
1654

1655
        mock_ffmpeg.reset_mock()
1656
        output_file = gr.Video(
1657
            sources=["webcam"], mirror_webcam=False, format="avi", include_audio=False
1658
        ).preprocess(x_video)
1659
        output_params = mock_ffmpeg.call_args_list[0][1]["outputs"]
1660
        assert list(output_params.values())[0] == ["-an"]
1661
        assert "flip" not in Path(list(output_params.keys())[0]).name
1662
        assert ".avi" in list(output_params.keys())[0]
1663
        assert ".avi" in output_file
1664

1665

1666
class TestNames:
1667
    # This test ensures that `components.get_component_instance()` works correctly when instantiating from components
1668
    def test_no_duplicate_uncased_names(self, io_components):
1669
        unique_subclasses_uncased = {s.__name__.lower() for s in io_components}
1670
        assert len(io_components) == len(unique_subclasses_uncased)
1671

1672

1673
class TestLabel:
1674
    def test_component_functions(self):
1675
        """
1676
        Process, postprocess, deserialize
1677
        """
1678
        y = "happy"
1679
        label_output = gr.Label()
1680
        label = label_output.postprocess(y).model_dump()
1681
        assert label == {"label": "happy", "confidences": None}
1682

1683
        y = {3: 0.7, 1: 0.2, 0: 0.1}
1684
        label = label_output.postprocess(y).model_dump()
1685
        assert label == {
1686
            "label": 3,
1687
            "confidences": [
1688
                {"label": 3, "confidence": 0.7},
1689
                {"label": 1, "confidence": 0.2},
1690
                {"label": 0, "confidence": 0.1},
1691
            ],
1692
        }
1693
        label_output = gr.Label(num_top_classes=2)
1694
        label = label_output.postprocess(y).model_dump()
1695

1696
        assert label == {
1697
            "label": 3,
1698
            "confidences": [
1699
                {"label": 3, "confidence": 0.7},
1700
                {"label": 1, "confidence": 0.2},
1701
            ],
1702
        }
1703
        with pytest.raises(ValueError):
1704
            label_output.postprocess([1, 2, 3]).model_dump()
1705

1706
        test_file_dir = Path(Path(__file__).parent, "test_files")
1707
        path = str(Path(test_file_dir, "test_label_json.json"))
1708
        label_dict = label_output.postprocess(path).model_dump()
1709
        assert label_dict["label"] == "web site"
1710

1711
        assert label_output.get_config() == {
1712
            "name": "label",
1713
            "show_label": True,
1714
            "num_top_classes": 2,
1715
            "value": {},
1716
            "label": None,
1717
            "container": True,
1718
            "min_width": 160,
1719
            "scale": None,
1720
            "elem_id": None,
1721
            "elem_classes": [],
1722
            "visible": True,
1723
            "proxy_url": None,
1724
            "color": None,
1725
            "_selectable": False,
1726
        }
1727

1728
    def test_color_argument(self):
1729
        label = gr.Label(value=-10, color="red")
1730
        assert label.get_config()["color"] == "red"
1731

1732
    def test_in_interface(self):
1733
        """
1734
        Interface, process
1735
        """
1736
        x_img = "test/test_files/bus.png"
1737

1738
        def rgb_distribution(img):
1739
            rgb_dist = np.mean(img, axis=(0, 1))
1740
            rgb_dist /= np.sum(rgb_dist)
1741
            rgb_dist = np.round(rgb_dist, decimals=2)
1742
            return {
1743
                "red": rgb_dist[0],
1744
                "green": rgb_dist[1],
1745
                "blue": rgb_dist[2],
1746
            }
1747

1748
        iface = gr.Interface(rgb_distribution, "image", "label")
1749
        output = iface(x_img)
1750
        assert output == {
1751
            "label": "red",
1752
            "confidences": [
1753
                {"label": "red", "confidence": 0.44},
1754
                {"label": "green", "confidence": 0.28},
1755
                {"label": "blue", "confidence": 0.28},
1756
            ],
1757
        }
1758

1759

1760
class TestHighlightedText:
1761
    def test_postprocess(self):
1762
        """
1763
        postprocess
1764
        """
1765
        component = gr.HighlightedText()
1766
        value = [
1767
            ("", None),
1768
            ("Wolfgang", "PER"),
1769
            (" lives in ", None),
1770
            ("Berlin", "LOC"),
1771
            ("", None),
1772
        ]
1773
        result = [
1774
            {"token": "", "class_or_confidence": None},
1775
            {"token": "Wolfgang", "class_or_confidence": "PER"},
1776
            {"token": " lives in ", "class_or_confidence": None},
1777
            {"token": "Berlin", "class_or_confidence": "LOC"},
1778
            {"token": "", "class_or_confidence": None},
1779
        ]
1780
        result_ = component.postprocess(value).model_dump()
1781
        assert result == result_
1782

1783
        text = "Wolfgang lives in Berlin"
1784
        entities = [
1785
            {"entity": "PER", "start": 0, "end": 8},
1786
            {"entity": "LOC", "start": 18, "end": 24},
1787
        ]
1788
        result_ = component.postprocess(
1789
            {"text": text, "entities": entities}
1790
        ).model_dump()
1791
        assert result == result_
1792

1793
        text = "Wolfgang lives in Berlin"
1794
        entities = [
1795
            {"entity_group": "PER", "start": 0, "end": 8},
1796
            {"entity": "LOC", "start": 18, "end": 24},
1797
        ]
1798
        result_ = component.postprocess(
1799
            {"text": text, "entities": entities}
1800
        ).model_dump()
1801
        assert result == result_
1802

1803
        # Test split entity is merged when combine adjacent is set
1804
        text = "Wolfgang lives in Berlin"
1805
        entities = [
1806
            {"entity": "PER", "start": 0, "end": 4},
1807
            {"entity": "PER", "start": 4, "end": 8},
1808
            {"entity": "LOC", "start": 18, "end": 24},
1809
        ]
1810
        # After a merge empty entries are stripped except the leading one
1811
        result_after_merge = [
1812
            {"token": "", "class_or_confidence": None},
1813
            {"token": "Wolfgang", "class_or_confidence": "PER"},
1814
            {"token": " lives in ", "class_or_confidence": None},
1815
            {"token": "Berlin", "class_or_confidence": "LOC"},
1816
        ]
1817
        result_ = component.postprocess(
1818
            {"text": text, "entities": entities}
1819
        ).model_dump()
1820
        assert result != result_
1821
        assert result_after_merge != result_
1822

1823
        component = gr.HighlightedText(combine_adjacent=True)
1824
        result_ = component.postprocess(
1825
            {"text": text, "entities": entities}
1826
        ).model_dump()
1827
        assert result_after_merge == result_
1828

1829
        component = gr.HighlightedText()
1830

1831
        text = "Wolfgang lives in Berlin"
1832
        entities = [
1833
            {"entity": "LOC", "start": 18, "end": 24},
1834
            {"entity": "PER", "start": 0, "end": 8},
1835
        ]
1836
        result_ = component.postprocess(
1837
            {"text": text, "entities": entities}
1838
        ).model_dump()
1839
        assert result == result_
1840

1841
        text = "I live there"
1842
        entities = []
1843
        result_ = component.postprocess(
1844
            {"text": text, "entities": entities}
1845
        ).model_dump()
1846
        assert [{"token": text, "class_or_confidence": None}] == result_
1847

1848
        text = "Wolfgang"
1849
        entities = [
1850
            {"entity": "PER", "start": 0, "end": 8},
1851
        ]
1852
        result_ = component.postprocess(
1853
            {"text": text, "entities": entities}
1854
        ).model_dump()
1855
        assert [
1856
            {"token": "", "class_or_confidence": None},
1857
            {"token": text, "class_or_confidence": "PER"},
1858
            {"token": "", "class_or_confidence": None},
1859
        ] == result_
1860

1861
    def test_component_functions(self):
1862
        """
1863
        get_config
1864
        """
1865
        ht_output = gr.HighlightedText(color_map={"pos": "green", "neg": "red"})
1866
        assert ht_output.get_config() == {
1867
            "color_map": {"pos": "green", "neg": "red"},
1868
            "name": "highlightedtext",
1869
            "show_label": True,
1870
            "label": None,
1871
            "show_legend": False,
1872
            "container": True,
1873
            "min_width": 160,
1874
            "scale": None,
1875
            "elem_id": None,
1876
            "elem_classes": [],
1877
            "visible": True,
1878
            "value": None,
1879
            "proxy_url": None,
1880
            "_selectable": False,
1881
            "combine_adjacent": False,
1882
            "adjacent_separator": "",
1883
            "interactive": None,
1884
        }
1885

1886
    def test_in_interface(self):
1887
        """
1888
        Interface, process
1889
        """
1890

1891
        def highlight_vowels(sentence):
1892
            phrases, cur_phrase = [], ""
1893
            vowels, mode = "aeiou", None
1894
            for letter in sentence:
1895
                letter_mode = "vowel" if letter in vowels else "non"
1896
                if mode is None:
1897
                    mode = letter_mode
1898
                elif mode != letter_mode:
1899
                    phrases.append((cur_phrase, mode))
1900
                    cur_phrase = ""
1901
                    mode = letter_mode
1902
                cur_phrase += letter
1903
            phrases.append((cur_phrase, mode))
1904
            return phrases
1905

1906
        iface = gr.Interface(highlight_vowels, "text", "highlight")
1907
        output = iface("Helloooo")
1908
        assert output == [
1909
            {"token": "H", "class_or_confidence": "non"},
1910
            {"token": "e", "class_or_confidence": "vowel"},
1911
            {"token": "ll", "class_or_confidence": "non"},
1912
            {"token": "oooo", "class_or_confidence": "vowel"},
1913
        ]
1914

1915

1916
class TestAnnotatedImage:
1917
    def test_postprocess(self):
1918
        """
1919
        postprocess
1920
        """
1921
        component = gr.AnnotatedImage()
1922
        img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
1923
        mask1 = [40, 40, 50, 50]
1924
        mask2 = np.zeros((100, 100), dtype=np.uint8)
1925
        mask2[10:20, 10:20] = 1
1926

1927
        input = (img, [(mask1, "mask1"), (mask2, "mask2")])
1928
        result = component.postprocess(input).model_dump()
1929

1930
        base_img_out = PIL.Image.open(result["image"]["path"])
1931

1932
        assert result["annotations"][0]["label"] == "mask1"
1933

1934
        mask1_img_out = PIL.Image.open(result["annotations"][0]["image"]["path"])
1935
        assert mask1_img_out.size == base_img_out.size
1936
        mask1_array_out = np.array(mask1_img_out)
1937
        assert np.max(mask1_array_out[40:50, 40:50]) == 255
1938
        assert np.max(mask1_array_out[50:60, 50:60]) == 0
1939

1940
    def test_component_functions(self):
1941
        ht_output = gr.AnnotatedImage(label="sections", show_legend=False)
1942
        assert ht_output.get_config() == {
1943
            "name": "annotatedimage",
1944
            "show_label": True,
1945
            "label": "sections",
1946
            "show_legend": False,
1947
            "container": True,
1948
            "min_width": 160,
1949
            "scale": None,
1950
            "color_map": None,
1951
            "height": None,
1952
            "width": None,
1953
            "elem_id": None,
1954
            "elem_classes": [],
1955
            "visible": True,
1956
            "value": None,
1957
            "proxy_url": None,
1958
            "_selectable": False,
1959
        }
1960

1961
    def test_in_interface(self):
1962
        def mask(img):
1963
            top_left_corner = [0, 0, 20, 20]
1964
            random_mask = np.random.randint(0, 2, img.shape[:2])
1965
            return (img, [(top_left_corner, "left corner"), (random_mask, "random")])
1966

1967
        iface = gr.Interface(mask, "image", gr.AnnotatedImage())
1968
        output = iface("test/test_files/bus.png")
1969
        output_img, (mask1, _) = output["image"], output["annotations"]
1970
        input_img = PIL.Image.open("test/test_files/bus.png")
1971
        output_img = PIL.Image.open(output_img)
1972
        mask1_img = PIL.Image.open(mask1["image"])
1973

1974
        assert output_img.size == input_img.size
1975
        assert mask1_img.size == input_img.size
1976

1977

1978
class TestChatbot:
1979
    def test_component_functions(self):
1980
        """
1981
        Postprocess, get_config
1982
        """
1983
        chatbot = gr.Chatbot()
1984
        assert chatbot.postprocess(
1985
            [["You are **cool**\nand fun", "so are *you*"]]
1986
        ).model_dump() == [("You are **cool**\nand fun", "so are *you*")]
1987

1988
        multimodal_msg = [
1989
            [("test/test_files/video_sample.mp4",), "cool video"],
1990
            [("test/test_files/audio_sample.wav",), "cool audio"],
1991
            [("test/test_files/bus.png", "A bus"), "cool pic"],
1992
            [(Path("test/test_files/video_sample.mp4"),), "cool video"],
1993
            [(Path("test/test_files/audio_sample.wav"),), "cool audio"],
1994
            [(Path("test/test_files/bus.png"), "A bus"), "cool pic"],
1995
        ]
1996
        postprocessed_multimodal_msg = chatbot.postprocess(multimodal_msg).model_dump()
1997
        for msg in postprocessed_multimodal_msg:
1998
            assert "file" in msg[0]
1999
            assert msg[1] in {"cool video", "cool audio", "cool pic"}
2000
            assert msg[0]["file"]["path"].split(".")[-1] in {"mp4", "wav", "png"}
2001
            if msg[0]["alt_text"]:
2002
                assert msg[0]["alt_text"] == "A bus"
2003

2004
        assert chatbot.get_config() == {
2005
            "value": [],
2006
            "label": None,
2007
            "show_label": True,
2008
            "name": "chatbot",
2009
            "show_share_button": False,
2010
            "visible": True,
2011
            "elem_id": None,
2012
            "elem_classes": [],
2013
            "container": True,
2014
            "min_width": 160,
2015
            "scale": None,
2016
            "height": None,
2017
            "proxy_url": None,
2018
            "_selectable": False,
2019
            "latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],
2020
            "likeable": False,
2021
            "rtl": False,
2022
            "show_copy_button": False,
2023
            "avatar_images": [None, None],
2024
            "sanitize_html": True,
2025
            "render_markdown": True,
2026
            "bubble_full_width": True,
2027
            "line_breaks": True,
2028
            "layout": None,
2029
        }
2030

2031
    def test_avatar_images_are_moved_to_cache(self):
2032
        chatbot = gr.Chatbot(avatar_images=("test/test_files/bus.png", None))
2033
        assert chatbot.avatar_images[0]
2034
        assert utils.is_in_or_equal(
2035
            chatbot.avatar_images[0]["path"], chatbot.GRADIO_CACHE
2036
        )
2037
        assert chatbot.avatar_images[1] is None
2038

2039

2040
class TestJSON:
2041
    def test_component_functions(self):
2042
        """
2043
        Postprocess
2044
        """
2045
        js_output = gr.JSON()
2046
        assert js_output.postprocess('{"a":1, "b": 2}'), '"{\\"a\\":1, \\"b\\": 2}"'
2047
        assert js_output.get_config() == {
2048
            "container": True,
2049
            "min_width": 160,
2050
            "scale": None,
2051
            "elem_id": None,
2052
            "elem_classes": [],
2053
            "visible": True,
2054
            "value": None,
2055
            "show_label": True,
2056
            "label": None,
2057
            "name": "json",
2058
            "proxy_url": None,
2059
            "_selectable": False,
2060
        }
2061

2062
    def test_chatbot_selectable_in_config(self):
2063
        with gr.Blocks() as demo:
2064
            cb = gr.Chatbot(label="Chatbot")
2065
            cb.like(lambda: print("foo"))
2066
            gr.Chatbot(label="Chatbot2")
2067

2068
        assertion_count = 0
2069
        for component in demo.config["components"]:
2070
            if component["props"]["label"] == "Chatbot":
2071
                assertion_count += 1
2072
                assert component["props"]["likeable"]
2073
            elif component["props"]["label"] == "Chatbot2":
2074
                assertion_count += 1
2075
                assert not component["props"]["likeable"]
2076

2077
        assert assertion_count == 2
2078

2079
    @pytest.mark.asyncio
2080
    async def test_in_interface(self):
2081
        """
2082
        Interface, process
2083
        """
2084

2085
        def get_avg_age_per_gender(data):
2086
            return {
2087
                "M": int(data[data["gender"] == "M"]["age"].mean()),
2088
                "F": int(data[data["gender"] == "F"]["age"].mean()),
2089
                "O": int(data[data["gender"] == "O"]["age"].mean()),
2090
            }
2091

2092
        iface = gr.Interface(
2093
            get_avg_age_per_gender,
2094
            gr.Dataframe(headers=["gender", "age"]),
2095
            "json",
2096
        )
2097
        y_data = [
2098
            ["M", 30],
2099
            ["F", 20],
2100
            ["M", 40],
2101
            ["O", 20],
2102
            ["F", 30],
2103
        ]
2104
        assert (
2105
            await iface.process_api(
2106
                0, [{"data": y_data, "headers": ["gender", "age"]}], state={}
2107
            )
2108
        )["data"][0] == {
2109
            "M": 35,
2110
            "F": 25,
2111
            "O": 20,
2112
        }
2113

2114

2115
class TestHTML:
2116
    def test_component_functions(self):
2117
        """
2118
        get_config
2119
        """
2120
        html_component = gr.components.HTML("#Welcome onboard", label="HTML Input")
2121
        assert html_component.get_config() == {
2122
            "value": "#Welcome onboard",
2123
            "label": "HTML Input",
2124
            "show_label": True,
2125
            "visible": True,
2126
            "elem_id": None,
2127
            "elem_classes": [],
2128
            "proxy_url": None,
2129
            "name": "html",
2130
            "_selectable": False,
2131
        }
2132

2133
    def test_in_interface(self):
2134
        """
2135
        Interface, process
2136
        """
2137

2138
        def bold_text(text):
2139
            return f"<strong>{text}</strong>"
2140

2141
        iface = gr.Interface(bold_text, "text", "html")
2142
        assert iface("test") == "<strong>test</strong>"
2143

2144

2145
class TestMarkdown:
2146
    def test_component_functions(self):
2147
        markdown_component = gr.Markdown("# Let's learn about $x$", label="Markdown")
2148
        assert markdown_component.get_config()["value"] == "# Let's learn about $x$"
2149

2150
    def test_in_interface(self):
2151
        """
2152
        Interface, process
2153
        """
2154
        iface = gr.Interface(lambda x: x, "text", "markdown")
2155
        input_data = "    Here's an [image](https://gradio.app/images/gradio_logo.png)"
2156
        output_data = iface(input_data)
2157
        assert output_data == input_data.strip()
2158

2159

2160
class TestModel3D:
2161
    def test_component_functions(self):
2162
        """
2163
        get_config
2164
        """
2165
        model_component = gr.components.Model3D(None, label="Model")
2166
        assert model_component.get_config() == {
2167
            "value": None,
2168
            "clear_color": [0, 0, 0, 0],
2169
            "label": "Model",
2170
            "show_label": True,
2171
            "container": True,
2172
            "scale": None,
2173
            "min_width": 160,
2174
            "visible": True,
2175
            "elem_id": None,
2176
            "elem_classes": [],
2177
            "proxy_url": None,
2178
            "interactive": None,
2179
            "name": "model3d",
2180
            "camera_position": (None, None, None),
2181
            "height": None,
2182
            "zoom_speed": 1,
2183
            "pan_speed": 1,
2184
            "_selectable": False,
2185
        }
2186

2187
        file = "test/test_files/Box.gltf"
2188
        output1 = model_component.postprocess(file)
2189
        output2 = model_component.postprocess(Path(file))
2190
        assert output1
2191
        assert output2
2192
        assert Path(output1.path).name == Path(output2.path).name
2193

2194
    def test_in_interface(self):
2195
        """
2196
        Interface, process
2197
        """
2198
        iface = gr.Interface(lambda x: x, "model3d", "model3d")
2199
        input_data = "test/test_files/Box.gltf"
2200
        output_data = iface(input_data)
2201
        assert output_data.endswith(".gltf")
2202

2203

2204
class TestColorPicker:
2205
    def test_component_functions(self):
2206
        """
2207
        Preprocess, postprocess, serialize, tokenize, get_config
2208
        """
2209
        color_picker_input = gr.ColorPicker()
2210
        assert color_picker_input.preprocess("#000000") == "#000000"
2211
        assert color_picker_input.postprocess("#000000") == "#000000"
2212
        assert color_picker_input.postprocess(None) is None
2213
        assert color_picker_input.postprocess("#FFFFFF") == "#FFFFFF"
2214

2215
        assert color_picker_input.get_config() == {
2216
            "value": None,
2217
            "show_label": True,
2218
            "label": None,
2219
            "container": True,
2220
            "min_width": 160,
2221
            "scale": None,
2222
            "elem_id": None,
2223
            "elem_classes": [],
2224
            "visible": True,
2225
            "interactive": None,
2226
            "proxy_url": None,
2227
            "name": "colorpicker",
2228
            "info": None,
2229
            "_selectable": False,
2230
        }
2231

2232
    def test_in_interface_as_input(self):
2233
        """
2234
        Interface, process
2235
        """
2236
        iface = gr.Interface(lambda x: x, "colorpicker", "colorpicker")
2237
        assert iface("#000000") == "#000000"
2238

2239
    def test_in_interface_as_output(self):
2240
        """
2241
        Interface, process
2242

2243
        """
2244
        iface = gr.Interface(lambda x: x, "colorpicker", gr.ColorPicker())
2245
        assert iface("#000000") == "#000000"
2246

2247
    def test_static(self):
2248
        """
2249
        postprocess
2250
        """
2251
        component = gr.ColorPicker("#000000")
2252
        assert component.get_config().get("value") == "#000000"
2253

2254

2255
class TestGallery:
2256
    def test_postprocess(self):
2257
        url = "https://huggingface.co/Norod78/SDXL-VintageMagStyle-Lora/resolve/main/Examples/00015-20230906102032-7778-Wonderwoman VintageMagStyle   _lora_SDXL-VintageMagStyle-Lora_1_, Very detailed, clean, high quality, sharp image.jpg"
2258
        gallery = gr.Gallery([url])
2259
        assert gallery.get_config()["value"] == [
2260
            {
2261
                "image": {
2262
                    "path": url,
2263
                    "orig_name": "00015-20230906102032-7778-Wonderwoman VintageMagStyle   _lora_SDXL-VintageMagStyle-Lora_1_, Very detailed, clean, high quality, sharp image.jpg",
2264
                    "mime_type": None,
2265
                    "size": None,
2266
                    "url": url,
2267
                    "is_stream": False,
2268
                    "meta": {"_type": "gradio.FileData"},
2269
                },
2270
                "caption": None,
2271
            }
2272
        ]
2273

2274
    def test_gallery(self):
2275
        gallery = gr.Gallery()
2276
        Path(Path(__file__).parent, "test_files")
2277

2278
        postprocessed_gallery = gallery.postprocess(
2279
            [
2280
                (str(Path("test/test_files/foo.png")), "foo_caption"),
2281
                (Path("test/test_files/bar.png"), "bar_caption"),
2282
                str(Path("test/test_files/baz.png")),
2283
                Path("test/test_files/qux.png"),
2284
            ]
2285
        ).model_dump()
2286

2287
        # Using str(Path(...)) to ensure that the test passes on all platforms
2288
        assert postprocessed_gallery == [
2289
            {
2290
                "image": {
2291
                    "path": str(Path("test") / "test_files" / "foo.png"),
2292
                    "orig_name": "foo.png",
2293
                    "mime_type": None,
2294
                    "size": None,
2295
                    "url": None,
2296
                    "is_stream": False,
2297
                    "meta": {"_type": "gradio.FileData"},
2298
                },
2299
                "caption": "foo_caption",
2300
            },
2301
            {
2302
                "image": {
2303
                    "path": str(Path("test") / "test_files" / "bar.png"),
2304
                    "orig_name": "bar.png",
2305
                    "mime_type": None,
2306
                    "size": None,
2307
                    "url": None,
2308
                    "is_stream": False,
2309
                    "meta": {"_type": "gradio.FileData"},
2310
                },
2311
                "caption": "bar_caption",
2312
            },
2313
            {
2314
                "image": {
2315
                    "path": str(Path("test") / "test_files" / "baz.png"),
2316
                    "orig_name": "baz.png",
2317
                    "mime_type": None,
2318
                    "size": None,
2319
                    "url": None,
2320
                    "is_stream": False,
2321
                    "meta": {"_type": "gradio.FileData"},
2322
                },
2323
                "caption": None,
2324
            },
2325
            {
2326
                "image": {
2327
                    "path": str(Path("test") / "test_files" / "qux.png"),
2328
                    "orig_name": "qux.png",
2329
                    "mime_type": None,
2330
                    "size": None,
2331
                    "url": None,
2332
                    "is_stream": False,
2333
                    "meta": {"_type": "gradio.FileData"},
2334
                },
2335
                "caption": None,
2336
            },
2337
        ]
2338

2339
    def test_gallery_preprocess(self):
2340
        from gradio.components.gallery import GalleryData, GalleryImage
2341

2342
        gallery = gr.Gallery()
2343
        img = GalleryImage(image=FileData(path="test/test_files/bus.png"))
2344
        data = GalleryData(root=[img])
2345

2346
        preprocess = gallery.preprocess(data)
2347
        assert preprocess[0][0] == "test/test_files/bus.png"
2348

2349
        gallery = gr.Gallery(type="numpy")
2350
        assert (
2351
            gallery.preprocess(data)[0][0]
2352
            == np.array(PIL.Image.open("test/test_files/bus.png"))
2353
        ).all()
2354

2355
        gallery = gr.Gallery(type="pil")
2356
        assert gallery.preprocess(data)[0][0] == PIL.Image.open(
2357
            "test/test_files/bus.png"
2358
        )
2359

2360
        img_captions = GalleryImage(
2361
            image=FileData(path="test/test_files/bus.png"), caption="bus"
2362
        )
2363
        data = GalleryData(root=[img_captions])
2364
        preprocess = gr.Gallery().preprocess(data)
2365
        assert preprocess[0] == ("test/test_files/bus.png", "bus")
2366

2367

2368
class TestState:
2369
    def test_as_component(self):
2370
        state = gr.State(value=5)
2371
        assert state.preprocess(10) == 10
2372
        assert state.preprocess("abc") == "abc"
2373
        assert state.stateful
2374

2375
    def test_initial_value_deepcopy(self):
2376
        with pytest.raises(TypeError):
2377
            gr.State(value=gr)  # modules are not deepcopyable
2378

2379
    @pytest.mark.asyncio
2380
    async def test_in_interface(self):
2381
        def test(x, y=" def"):
2382
            return (x + y, x + y)
2383

2384
        io = gr.Interface(test, ["text", "state"], ["text", "state"])
2385
        result = await io.call_function(0, ["abc"])
2386
        assert result["prediction"][0] == "abc def"
2387
        result = await io.call_function(0, ["abc", result["prediction"][0]])
2388
        assert result["prediction"][0] == "abcabc def"
2389

2390
    @pytest.mark.asyncio
2391
    async def test_in_blocks(self):
2392
        with gr.Blocks() as demo:
2393
            score = gr.State()
2394
            btn = gr.Button()
2395
            btn.click(lambda x: x + 1, score, score)
2396

2397
        result = await demo.call_function(0, [0])
2398
        assert result["prediction"] == 1
2399
        result = await demo.call_function(0, [result["prediction"]])
2400
        assert result["prediction"] == 2
2401

2402

2403
def test_dataframe_process_example_converts_dataframes():
2404
    df_comp = gr.Dataframe()
2405
    assert df_comp.process_example(
2406
        pd.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]})
2407
    ) == [
2408
        [1, 5],
2409
        [2, 6],
2410
        [3, 7],
2411
        [4, 8],
2412
    ]
2413
    assert df_comp.process_example(np.array([[1, 2], [3, 4.0]])) == [
2414
        [1.0, 2.0],
2415
        [3.0, 4.0],
2416
    ]
2417

2418

2419
@pytest.mark.parametrize("component", [gr.Model3D, gr.File, gr.Audio])
2420
def test_process_example_returns_file_basename(component):
2421
    component = component()
2422
    assert (
2423
        component.process_example("/home/freddy/sources/example.ext") == "example.ext"
2424
    )
2425
    assert component.process_example(None) == ""
2426

2427

2428
@patch(
2429
    "gradio.components.Component.process_example",
2430
    spec=gr.components.Component.process_example,
2431
)
2432
@patch("gradio.components.Image.process_example", spec=gr.Image.process_example)
2433
@patch("gradio.components.File.process_example", spec=gr.File.process_example)
2434
@patch("gradio.components.Dataframe.process_example", spec=gr.DataFrame.process_example)
2435
@patch("gradio.components.Model3D.process_example", spec=gr.Model3D.process_example)
2436
def test_dataset_calls_process_example(*mocks):
2437
    gr.Dataset(
2438
        components=[gr.Dataframe(), gr.File(), gr.Image(), gr.Model3D(), gr.Textbox()],
2439
        samples=[
2440
            [
2441
                pd.DataFrame({"a": np.array([1, 2, 3])}),
2442
                "foo.png",
2443
                "bar.jpeg",
2444
                "duck.obj",
2445
                "hello",
2446
            ]
2447
        ],
2448
    )
2449
    assert all(m.called for m in mocks)
2450

2451

2452
cars = vega_datasets.data.cars()
2453
stocks = vega_datasets.data.stocks()
2454
barley = vega_datasets.data.barley()
2455
simple = pd.DataFrame(
2456
    {
2457
        "a": ["A", "B", "C", "D", "E", "F", "G", "H", "I"],
2458
        "b": [28, 55, 43, 91, 81, 53, 19, 87, 52],
2459
    }
2460
)
2461

2462

2463
class TestScatterPlot:
2464
    @patch.dict("sys.modules", {"bokeh": MagicMock(__version__="3.0.3")})
2465
    def test_get_config(self):
2466
        print(gr.ScatterPlot().get_config())
2467
        assert gr.ScatterPlot().get_config() == {
2468
            "caption": None,
2469
            "elem_id": None,
2470
            "elem_classes": [],
2471
            "interactive": None,
2472
            "label": None,
2473
            "name": "plot",
2474
            "bokeh_version": "3.0.3",
2475
            "show_actions_button": False,
2476
            "proxy_url": None,
2477
            "show_label": True,
2478
            "container": True,
2479
            "min_width": 160,
2480
            "scale": None,
2481
            "value": None,
2482
            "visible": True,
2483
            "x": None,
2484
            "y": None,
2485
            "color": None,
2486
            "size": None,
2487
            "shape": None,
2488
            "title": None,
2489
            "tooltip": None,
2490
            "x_title": None,
2491
            "y_title": None,
2492
            "color_legend_title": None,
2493
            "size_legend_title": None,
2494
            "shape_legend_title": None,
2495
            "color_legend_position": None,
2496
            "size_legend_position": None,
2497
            "shape_legend_position": None,
2498
            "height": None,
2499
            "width": None,
2500
            "x_lim": None,
2501
            "y_lim": None,
2502
            "x_label_angle": None,
2503
            "y_label_angle": None,
2504
            "_selectable": False,
2505
        }
2506

2507
    def test_no_color(self):
2508
        plot = gr.ScatterPlot(
2509
            x="Horsepower",
2510
            y="Miles_per_Gallon",
2511
            tooltip="Name",
2512
            title="Car Data",
2513
            x_title="Horse",
2514
        )
2515
        output = plot.postprocess(cars).model_dump()
2516
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2517
        config = json.loads(output["plot"])
2518
        assert config["encoding"]["x"]["field"] == "Horsepower"
2519
        assert config["encoding"]["x"]["title"] == "Horse"
2520
        assert config["encoding"]["y"]["field"] == "Miles_per_Gallon"
2521
        assert config["title"] == "Car Data"
2522
        assert "height" not in config
2523
        assert "width" not in config
2524

2525
    def test_no_interactive(self):
2526
        plot = gr.ScatterPlot(
2527
            x="Horsepower", y="Miles_per_Gallon", tooltip="Name", interactive=False
2528
        )
2529
        output = plot.postprocess(cars).model_dump()
2530
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2531
        config = json.loads(output["plot"])
2532
        assert "selection" not in config
2533

2534
    def test_height_width(self):
2535
        plot = gr.ScatterPlot(
2536
            x="Horsepower", y="Miles_per_Gallon", height=100, width=200
2537
        )
2538
        output = plot.postprocess(cars).model_dump()
2539
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2540
        config = json.loads(output["plot"])
2541
        assert config["height"] == 100
2542
        assert config["width"] == 200
2543

2544
    def test_xlim_ylim(self):
2545
        plot = gr.ScatterPlot(
2546
            x="Horsepower", y="Miles_per_Gallon", x_lim=[200, 400], y_lim=[300, 500]
2547
        )
2548
        output = plot.postprocess(cars).model_dump()
2549
        config = json.loads(output["plot"])
2550
        assert config["encoding"]["x"]["scale"] == {"domain": [200, 400]}
2551
        assert config["encoding"]["y"]["scale"] == {"domain": [300, 500]}
2552

2553
    def test_color_encoding(self):
2554
        plot = gr.ScatterPlot(
2555
            x="Horsepower",
2556
            y="Miles_per_Gallon",
2557
            tooltip="Name",
2558
            title="Car Data",
2559
            color="Origin",
2560
        )
2561
        output = plot.postprocess(cars).model_dump()
2562
        config = json.loads(output["plot"])
2563
        assert config["encoding"]["color"]["field"] == "Origin"
2564
        assert config["encoding"]["color"]["scale"] == {
2565
            "domain": ["USA", "Europe", "Japan"],
2566
            "range": [0, 1, 2],
2567
        }
2568
        assert config["encoding"]["color"]["type"] == "nominal"
2569

2570
    def test_two_encodings(self):
2571
        plot = gr.ScatterPlot(
2572
            show_label=False,
2573
            title="Two encodings",
2574
            x="Horsepower",
2575
            y="Miles_per_Gallon",
2576
            color="Acceleration",
2577
            shape="Origin",
2578
        )
2579
        output = plot.postprocess(cars).model_dump()
2580
        config = json.loads(output["plot"])
2581
        assert config["encoding"]["color"]["field"] == "Acceleration"
2582
        assert config["encoding"]["color"]["scale"] == {
2583
            "domain": [cars.Acceleration.min(), cars.Acceleration.max()],
2584
            "range": [0, 1],
2585
        }
2586
        assert config["encoding"]["color"]["type"] == "quantitative"
2587

2588
        assert config["encoding"]["shape"]["field"] == "Origin"
2589
        assert config["encoding"]["shape"]["type"] == "nominal"
2590

2591
    def test_legend_position(self):
2592
        plot = gr.ScatterPlot(
2593
            show_label=False,
2594
            title="Two encodings",
2595
            x="Horsepower",
2596
            y="Miles_per_Gallon",
2597
            color="Acceleration",
2598
            color_legend_position="none",
2599
            color_legend_title="Foo",
2600
            shape="Origin",
2601
            shape_legend_position="none",
2602
            shape_legend_title="Bar",
2603
            size="Acceleration",
2604
            size_legend_title="Accel",
2605
            size_legend_position="none",
2606
        )
2607
        output = plot.postprocess(cars).model_dump()
2608
        config = json.loads(output["plot"])
2609
        assert config["encoding"]["color"]["legend"] is None
2610
        assert config["encoding"]["shape"]["legend"] is None
2611
        assert config["encoding"]["size"]["legend"] is None
2612

2613
    def test_scatterplot_accepts_fn_as_value(self):
2614
        plot = gr.ScatterPlot(
2615
            value=lambda: cars.sample(frac=0.1, replace=False),
2616
            x="Horsepower",
2617
            y="Miles_per_Gallon",
2618
            color="Origin",
2619
        )
2620
        assert isinstance(plot.value, dict)
2621
        assert isinstance(plot.value["plot"], str)
2622

2623

2624
class TestLinePlot:
2625
    @patch.dict("sys.modules", {"bokeh": MagicMock(__version__="3.0.3")})
2626
    def test_get_config(self):
2627
        assert gr.LinePlot().get_config() == {
2628
            "caption": None,
2629
            "elem_id": None,
2630
            "elem_classes": [],
2631
            "interactive": None,
2632
            "label": None,
2633
            "name": "plot",
2634
            "bokeh_version": "3.0.3",
2635
            "show_actions_button": False,
2636
            "proxy_url": None,
2637
            "show_label": True,
2638
            "container": True,
2639
            "min_width": 160,
2640
            "scale": None,
2641
            "value": None,
2642
            "visible": True,
2643
            "x": None,
2644
            "y": None,
2645
            "color": None,
2646
            "stroke_dash": None,
2647
            "overlay_point": None,
2648
            "title": None,
2649
            "tooltip": None,
2650
            "x_title": None,
2651
            "y_title": None,
2652
            "color_legend_title": None,
2653
            "stroke_dash_legend_title": None,
2654
            "color_legend_position": None,
2655
            "stroke_dash_legend_position": None,
2656
            "height": None,
2657
            "width": None,
2658
            "x_lim": None,
2659
            "y_lim": None,
2660
            "x_label_angle": None,
2661
            "y_label_angle": None,
2662
            "_selectable": False,
2663
        }
2664

2665
    def test_no_color(self):
2666
        plot = gr.LinePlot(
2667
            x="date",
2668
            y="price",
2669
            tooltip=["symbol", "price"],
2670
            title="Stock Performance",
2671
            x_title="Trading Day",
2672
        )
2673
        output = plot.postprocess(stocks).model_dump()
2674
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2675
        config = json.loads(output["plot"])
2676
        for layer in config["layer"]:
2677
            assert layer["mark"]["type"] in ["line", "point"]
2678
            assert layer["encoding"]["x"]["field"] == "date"
2679
            assert layer["encoding"]["x"]["title"] == "Trading Day"
2680
            assert layer["encoding"]["y"]["field"] == "price"
2681

2682
        assert config["title"] == "Stock Performance"
2683
        assert "height" not in config
2684
        assert "width" not in config
2685

2686
    def test_height_width(self):
2687
        plot = gr.LinePlot(x="date", y="price", height=100, width=200)
2688
        output = plot.postprocess(stocks).model_dump()
2689
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2690
        config = json.loads(output["plot"])
2691
        assert config["height"] == 100
2692
        assert config["width"] == 200
2693

2694
    def test_xlim_ylim(self):
2695
        plot = gr.LinePlot(x="date", y="price", x_lim=[200, 400], y_lim=[300, 500])
2696
        output = plot.postprocess(stocks).model_dump()
2697
        config = json.loads(output["plot"])
2698
        for layer in config["layer"]:
2699
            assert layer["encoding"]["x"]["scale"] == {"domain": [200, 400]}
2700
            assert layer["encoding"]["y"]["scale"] == {"domain": [300, 500]}
2701

2702
    def test_color_encoding(self):
2703
        plot = gr.LinePlot(
2704
            x="date", y="price", tooltip="symbol", color="symbol", overlay_point=True
2705
        )
2706
        output = plot.postprocess(stocks).model_dump()
2707
        config = json.loads(output["plot"])
2708
        for layer in config["layer"]:
2709
            assert layer["encoding"]["color"]["field"] == "symbol"
2710
            assert layer["encoding"]["color"]["scale"] == {
2711
                "domain": ["MSFT", "AMZN", "IBM", "GOOG", "AAPL"],
2712
                "range": [0, 1, 2, 3, 4],
2713
            }
2714
            assert layer["encoding"]["color"]["type"] == "nominal"
2715
            if layer["mark"]["type"] == "point":
2716
                assert layer["encoding"]["opacity"] == {}
2717

2718
    def test_lineplot_accepts_fn_as_value(self):
2719
        plot = gr.LinePlot(
2720
            value=lambda: stocks.sample(frac=0.1, replace=False),
2721
            x="date",
2722
            y="price",
2723
            color="symbol",
2724
        )
2725
        assert isinstance(plot.value, dict)
2726
        assert isinstance(plot.value["plot"], str)
2727

2728

2729
class TestBarPlot:
2730
    @patch.dict("sys.modules", {"bokeh": MagicMock(__version__="3.0.3")})
2731
    def test_get_config(self):
2732
        assert gr.BarPlot().get_config() == {
2733
            "caption": None,
2734
            "elem_id": None,
2735
            "elem_classes": [],
2736
            "interactive": None,
2737
            "label": None,
2738
            "name": "plot",
2739
            "bokeh_version": "3.0.3",
2740
            "show_actions_button": False,
2741
            "proxy_url": None,
2742
            "show_label": True,
2743
            "container": True,
2744
            "min_width": 160,
2745
            "scale": None,
2746
            "value": None,
2747
            "visible": True,
2748
            "x": None,
2749
            "y": None,
2750
            "color": None,
2751
            "vertical": True,
2752
            "group": None,
2753
            "title": None,
2754
            "tooltip": None,
2755
            "x_title": None,
2756
            "y_title": None,
2757
            "color_legend_title": None,
2758
            "group_title": None,
2759
            "color_legend_position": None,
2760
            "height": None,
2761
            "width": None,
2762
            "y_lim": None,
2763
            "x_label_angle": None,
2764
            "y_label_angle": None,
2765
            "sort": None,
2766
            "_selectable": False,
2767
        }
2768

2769
    def test_no_color(self):
2770
        plot = gr.BarPlot(
2771
            x="a",
2772
            y="b",
2773
            tooltip=["a", "b"],
2774
            title="Made Up Bar Plot",
2775
            x_title="Variable A",
2776
            sort="x",
2777
        )
2778
        output = plot.postprocess(simple).model_dump()
2779
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2780
        assert output["chart"] == "bar"
2781
        config = json.loads(output["plot"])
2782
        assert config["encoding"]["x"]["sort"] == "x"
2783
        assert config["encoding"]["x"]["field"] == "a"
2784
        assert config["encoding"]["x"]["title"] == "Variable A"
2785
        assert config["encoding"]["y"]["field"] == "b"
2786
        assert config["encoding"]["y"]["title"] == "b"
2787

2788
        assert config["title"] == "Made Up Bar Plot"
2789
        assert "height" not in config
2790
        assert "width" not in config
2791

2792
    def test_height_width(self):
2793
        plot = gr.BarPlot(x="a", y="b", height=100, width=200)
2794
        output = plot.postprocess(simple).model_dump()
2795
        assert sorted(output.keys()) == ["chart", "plot", "type"]
2796
        config = json.loads(output["plot"])
2797
        assert config["height"] == 100
2798
        assert config["width"] == 200
2799

2800
    def test_ylim(self):
2801
        plot = gr.BarPlot(x="a", y="b", y_lim=[15, 100])
2802
        output = plot.postprocess(simple).model_dump()
2803
        config = json.loads(output["plot"])
2804
        assert config["encoding"]["y"]["scale"] == {"domain": [15, 100]}
2805

2806
    def test_horizontal(self):
2807
        output = gr.BarPlot(
2808
            simple,
2809
            x="a",
2810
            y="b",
2811
            x_title="Variable A",
2812
            y_title="Variable B",
2813
            title="Simple Bar Plot with made up data",
2814
            tooltip=["a", "b"],
2815
            vertical=False,
2816
            y_lim=[20, 100],
2817
        ).get_config()
2818
        assert output["value"]["chart"] == "bar"
2819
        config = json.loads(output["value"]["plot"])
2820
        assert config["encoding"]["x"]["field"] == "b"
2821
        assert config["encoding"]["x"]["scale"] == {"domain": [20, 100]}
2822
        assert config["encoding"]["x"]["title"] == "Variable B"
2823

2824
        assert config["encoding"]["y"]["field"] == "a"
2825
        assert config["encoding"]["y"]["title"] == "Variable A"
2826

2827
    def test_barplot_accepts_fn_as_value(self):
2828
        plot = gr.BarPlot(
2829
            value=lambda: barley.sample(frac=0.1, replace=False),
2830
            x="year",
2831
            y="yield",
2832
        )
2833
        assert isinstance(plot.value, dict)
2834
        assert isinstance(plot.value["plot"], str)
2835

2836

2837
class TestCode:
2838
    def test_component_functions(self):
2839
        """
2840
        Preprocess, postprocess, serialize, get_config
2841
        """
2842
        code = gr.Code()
2843

2844
        assert code.preprocess("# hello friends") == "# hello friends"
2845
        assert code.preprocess("def fn(a):\n  return a") == "def fn(a):\n  return a"
2846

2847
        assert (
2848
            code.postprocess(
2849
                """
2850
            def fn(a):
2851
                return a
2852
            """
2853
            )
2854
            == """def fn(a):
2855
                return a"""
2856
        )
2857

2858
        test_file_dir = Path(Path(__file__).parent, "test_files")
2859
        path = str(Path(test_file_dir, "test_label_json.json"))
2860
        with open(path) as f:
2861
            assert code.postprocess(path) == path
2862
            assert code.postprocess((path,)) == f.read()
2863

2864
        assert code.get_config() == {
2865
            "value": None,
2866
            "language": None,
2867
            "lines": 5,
2868
            "name": "code",
2869
            "show_label": True,
2870
            "label": None,
2871
            "container": True,
2872
            "min_width": 160,
2873
            "scale": None,
2874
            "elem_id": None,
2875
            "elem_classes": [],
2876
            "visible": True,
2877
            "interactive": None,
2878
            "proxy_url": None,
2879
            "_selectable": False,
2880
        }
2881

2882

2883
class TestFileExplorer:
2884
    def test_component_functions(self):
2885
        """
2886
        Preprocess, get_config
2887
        """
2888
        file_explorer = gr.FileExplorer(file_count="single")
2889

2890
        config = file_explorer.get_config()
2891
        assert config["glob"] == "**/*"
2892
        assert config["value"] is None
2893
        assert config["file_count"] == "single"
2894
        assert config["server_fns"] == ["ls"]
2895

2896
        input_data = FileExplorerData(root=[["test/test_files/bus.png"]])
2897
        preprocessed_data = file_explorer.preprocess(input_data)
2898
        assert isinstance(preprocessed_data, str)
2899
        assert Path(preprocessed_data).name == "bus.png"
2900

2901
        input_data = FileExplorerData(root=[])
2902
        preprocessed_data = file_explorer.preprocess(input_data)
2903
        assert preprocessed_data is None
2904

2905
        file_explorer = gr.FileExplorer(file_count="multiple")
2906

2907
        config = file_explorer.get_config()
2908
        assert config["glob"] == "**/*"
2909
        assert config["value"] is None
2910
        assert config["file_count"] == "multiple"
2911
        assert config["server_fns"] == ["ls"]
2912

2913
        input_data = FileExplorerData(root=[["test/test_files/bus.png"]])
2914
        preprocessed_data = file_explorer.preprocess(input_data)
2915
        assert isinstance(preprocessed_data, list)
2916
        assert Path(preprocessed_data[0]).name == "bus.png"
2917

2918
        input_data = FileExplorerData(root=[])
2919
        preprocessed_data = file_explorer.preprocess(input_data)
2920
        assert preprocessed_data == []
2921

2922
    def test_file_explorer_txt_only_glob(self, tmpdir):
2923
        tmpdir.mkdir("foo")
2924
        (Path(tmpdir) / "foo" / "bar").mkdir()
2925
        (Path(tmpdir) / "foo" / "file.txt").touch()
2926
        (Path(tmpdir) / "foo" / "file2.txt").touch()
2927
        (Path(tmpdir) / "foo" / "file3.log").touch()
2928
        (Path(tmpdir) / "foo" / "img.png").touch()
2929
        (Path(tmpdir) / "foo" / "bar" / "bar.txt").touch()
2930

2931
        file_explorer = gr.FileExplorer(glob="*.txt", root=Path(tmpdir))
2932
        tree = file_explorer.ls(["foo"])
2933

2934
        answer = [
2935
            {"name": "bar", "type": "folder", "valid": False},
2936
            {"name": "file.txt", "type": "file", "valid": True},
2937
            {"name": "file2.txt", "type": "file", "valid": True},
2938
        ]
2939
        assert tree == answer
2940

2941

2942
def test_component_class_ids():
2943
    button_id = gr.Button().component_class_id
2944
    textbox_id = gr.Textbox().component_class_id
2945
    json_id = gr.JSON().component_class_id
2946
    mic_id = gr.Mic().component_class_id
2947
    microphone_id = gr.Microphone().component_class_id
2948
    audio_id = gr.Audio().component_class_id
2949

2950
    assert button_id == gr.Button().component_class_id
2951
    assert textbox_id == gr.Textbox().component_class_id
2952
    assert json_id == gr.JSON().component_class_id
2953
    assert mic_id == gr.Mic().component_class_id
2954
    assert microphone_id == gr.Microphone().component_class_id
2955
    assert audio_id == gr.Audio().component_class_id
2956
    assert mic_id == microphone_id
2957

2958
    # Make sure that the ids are unique
2959
    assert len({button_id, textbox_id, json_id, microphone_id, audio_id}) == 5
2960

2961

2962
def test_constructor_args():
2963
    assert gr.Textbox(max_lines=314).constructor_args == {"max_lines": 314}
2964
    assert gr.LoginButton(visible=False, value="Log in please").constructor_args == {
2965
        "visible": False,
2966
        "value": "Log in please",
2967
    }
2968

2969

2970
def test_template_component_configs(io_components):
2971
    template_components = [c for c in io_components if getattr(c, "is_template", False)]
2972
    for component in template_components:
2973
        component_parent_class = inspect.getmro(component)[1]
2974
        template_config = component().get_config()
2975
        parent_config = component_parent_class().get_config()
2976
        assert set(parent_config.keys()).issubset(set(template_config.keys()))
2977

2978

2979
def test_component_example_values(io_components):
2980
    for component in io_components:
2981
        if component == PDF:
2982
            continue
2983
        elif component in [gr.BarPlot, gr.LinePlot, gr.ScatterPlot]:
2984
            c: Component = component(x="x", y="y")
2985
        else:
2986
            c: Component = component()
2987
        c.postprocess(c.example_value())
2988

2989

2990
def test_component_example_payloads(io_components):
2991
    for component in io_components:
2992
        if component == PDF:
2993
            continue
2994
        elif component in [gr.BarPlot, gr.LinePlot, gr.ScatterPlot]:
2995
            c: Component = component(x="x", y="y")
2996
        else:
2997
            c: Component = component()
2998
        data = c.example_payload()
2999
        data = processing_utils.move_files_to_cache(
3000
            data,
3001
            c,
3002
            check_in_upload_folder=False,
3003
        )
3004
        if getattr(c, "data_model", None) and data is not None:
3005
            if issubclass(c.data_model, GradioModel):  # type: ignore
3006
                data = c.data_model(**data)  # type: ignore
3007
            elif issubclass(c.data_model, GradioRootModel):  # type: ignore
3008
                data = c.data_model(root=data)  # type: ignore
3009
        c.preprocess(data)
3010

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.