Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split test/test_components.py into test/components/test_*.py #8218

Merged
merged 3 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added test/components/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions test/components/plot_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pandas as pd
import vega_datasets

cars = vega_datasets.data.cars()
stocks = vega_datasets.data.stocks()
barley = vega_datasets.data.barley()
simple = pd.DataFrame(
{
"a": ["A", "B", "C", "D", "E", "F", "G", "H", "I"],
"b": [28, 55, 43, 91, 81, 53, 19, 87, 52],
}
)
77 changes: 77 additions & 0 deletions test/components/test_annotated_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import numpy as np
import PIL

import gradio as gr


class TestAnnotatedImage:
def test_postprocess(self):
"""
postprocess
"""
component = gr.AnnotatedImage()
img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
mask1 = [40, 40, 50, 50]
mask2 = np.zeros((100, 100), dtype=np.uint8)
mask2[10:20, 10:20] = 1

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

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

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

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

def test_annotated_image_format_parameter(self):
component = gr.AnnotatedImage(format="jpeg")
img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
mask1 = [40, 40, 50, 50]
data = (img, [(mask1, "mask1"), (mask1, "mask2")])
output = component.postprocess(data)
assert output.image.path.endswith(".jpeg")
assert output.annotations[0].image.path.endswith(".png")

def test_component_functions(self):
ht_output = gr.AnnotatedImage(label="sections", show_legend=False)
assert ht_output.get_config() == {
"name": "annotatedimage",
"show_label": True,
"label": "sections",
"show_legend": False,
"container": True,
"min_width": 160,
"scale": None,
"format": "webp",
"color_map": None,
"height": None,
"width": None,
"elem_id": None,
"elem_classes": [],
"visible": True,
"value": None,
"proxy_url": None,
"_selectable": False,
"key": None,
}

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

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

assert output_img.size == input_img.size
assert mask1_img.size == input_img.size
187 changes: 187 additions & 0 deletions test/components/test_audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import filecmp
from copy import deepcopy
from difflib import SequenceMatcher
from pathlib import Path

import numpy as np
import pytest
from gradio_client import media_data
from gradio_client import utils as client_utils
from scipy.io import wavfile

import gradio as gr
from gradio import processing_utils, utils
from gradio.data_classes import FileData


class TestAudio:
@pytest.mark.asyncio
async def test_component_functions(self, gradio_temp_dir):
"""
Preprocess, postprocess serialize, get_config, deserialize
type: filepath, numpy, file
"""
x_wav = FileData(path=media_data.BASE64_AUDIO["path"])
audio_input = gr.Audio()
output1 = audio_input.preprocess(x_wav)
assert output1[0] == 8000
assert output1[1].shape == (8046,)

x_wav = await processing_utils.async_move_files_to_cache([x_wav], audio_input)
x_wav = x_wav[0]
audio_input = gr.Audio(type="filepath")
output1 = audio_input.preprocess(x_wav)
assert Path(output1).name.endswith("audio_sample.wav")

audio_input = gr.Audio(label="Upload Your Audio")
assert audio_input.get_config() == {
"autoplay": False,
"sources": ["upload", "microphone"],
"name": "audio",
"show_download_button": None,
"show_share_button": False,
"streaming": False,
"show_label": True,
"label": "Upload Your Audio",
"container": True,
"editable": True,
"min_width": 160,
"scale": None,
"elem_id": None,
"elem_classes": [],
"visible": True,
"value": None,
"interactive": None,
"proxy_url": None,
"type": "numpy",
"format": "wav",
"streamable": False,
"max_length": None,
"min_length": None,
"waveform_options": {
"sample_rate": 44100,
"show_controls": False,
"show_recording_waveform": True,
"skip_length": 5,
"waveform_color": None,
"waveform_progress_color": None,
"trim_region_color": None,
},
"_selectable": False,
"key": None,
}
assert audio_input.preprocess(None) is None

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

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

# Output functionalities
y_audio = client_utils.decode_base64_to_file(
deepcopy(media_data.BASE64_AUDIO)["data"]
)
audio_output = gr.Audio(type="filepath")
assert filecmp.cmp(
y_audio.name, audio_output.postprocess(y_audio.name).model_dump()["path"]
)
assert audio_output.get_config() == {
"autoplay": False,
"name": "audio",
"show_download_button": None,
"show_share_button": False,
"streaming": False,
"show_label": True,
"label": None,
"max_length": None,
"min_length": None,
"container": True,
"editable": True,
"min_width": 160,
"scale": None,
"elem_id": None,
"elem_classes": [],
"visible": True,
"value": None,
"interactive": None,
"proxy_url": None,
"type": "filepath",
"format": "wav",
"streamable": False,
"sources": ["upload", "microphone"],
"waveform_options": {
"sample_rate": 44100,
"show_controls": False,
"show_recording_waveform": True,
"skip_length": 5,
"waveform_color": None,
"waveform_progress_color": None,
"trim_region_color": None,
},
"_selectable": False,
"key": None,
}

output1 = audio_output.postprocess(y_audio.name).model_dump()
output2 = audio_output.postprocess(Path(y_audio.name)).model_dump()
assert output1 == output2

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

def test_in_interface(self):
def reverse_audio(audio):
sr, data = audio
return (sr, np.flipud(data))

iface = gr.Interface(reverse_audio, "audio", "audio")
reversed_file = iface("test/test_files/audio_sample.wav")
reversed_reversed_file = iface(reversed_file)
reversed_reversed_data = client_utils.encode_url_or_file_to_base64(
reversed_reversed_file
)
similarity = SequenceMatcher(
a=reversed_reversed_data, b=media_data.BASE64_AUDIO["data"]
).ratio()
assert similarity > 0.99

def test_in_interface_as_output(self):
"""
Interface, process
"""

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

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

def test_audio_preprocess_can_be_read_by_scipy(self, gradio_temp_dir):
x_wav = FileData(
path=processing_utils.save_base64_to_cache(
media_data.BASE64_MICROPHONE["data"], cache_dir=gradio_temp_dir
)
)
audio_input = gr.Audio(type="filepath")
output = audio_input.preprocess(x_wav)
wavfile.read(output)

def test_prepost_process_to_mp3(self, gradio_temp_dir):
x_wav = FileData(
path=processing_utils.save_base64_to_cache(
media_data.BASE64_MICROPHONE["data"], cache_dir=gradio_temp_dir
)
)
audio_input = gr.Audio(type="filepath", format="mp3")
output = audio_input.preprocess(x_wav)
assert output.endswith("mp3")
output = audio_input.postprocess(
(48000, np.random.randint(-256, 256, (5, 3)).astype(np.int16))
).model_dump()
assert output["path"].endswith("mp3")
115 changes: 115 additions & 0 deletions test/components/test_bar_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import json
from unittest.mock import MagicMock, patch

import gradio as gr

from .plot_data import barley, simple


class TestBarPlot:
@patch.dict("sys.modules", {"bokeh": MagicMock(__version__="3.0.3")})
def test_get_config(self):
assert gr.BarPlot().get_config() == {
"caption": None,
"elem_id": None,
"elem_classes": [],
"interactive": None,
"label": None,
"name": "barplot",
"bokeh_version": "3.0.3",
"show_actions_button": False,
"proxy_url": None,
"show_label": True,
"container": True,
"min_width": 160,
"scale": None,
"value": None,
"visible": True,
"x": None,
"y": None,
"color": None,
"vertical": True,
"group": None,
"title": None,
"tooltip": None,
"x_title": None,
"y_title": None,
"color_legend_title": None,
"group_title": None,
"color_legend_position": None,
"height": None,
"width": None,
"y_lim": None,
"x_label_angle": None,
"y_label_angle": None,
"sort": None,
"_selectable": False,
"key": None,
}

def test_no_color(self):
plot = gr.BarPlot(
x="a",
y="b",
tooltip=["a", "b"],
title="Made Up Bar Plot",
x_title="Variable A",
sort="x",
)
output = plot.postprocess(simple).model_dump()
assert sorted(output.keys()) == ["chart", "plot", "type"]
assert output["chart"] == "bar"
config = json.loads(output["plot"])
assert config["encoding"]["x"]["sort"] == "x"
assert config["encoding"]["x"]["field"] == "a"
assert config["encoding"]["x"]["title"] == "Variable A"
assert config["encoding"]["y"]["field"] == "b"
assert config["encoding"]["y"]["title"] == "b"

assert config["title"] == "Made Up Bar Plot"
assert "height" not in config
assert "width" not in config

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

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

def test_horizontal(self):
output = gr.BarPlot(
simple,
x="a",
y="b",
x_title="Variable A",
y_title="Variable B",
title="Simple Bar Plot with made up data",
tooltip=["a", "b"],
vertical=False,
y_lim=[20, 100],
).get_config()
assert output["value"]["chart"] == "bar"
config = json.loads(output["value"]["plot"])
assert config["encoding"]["x"]["field"] == "b"
assert config["encoding"]["x"]["scale"] == {"domain": [20, 100]}
assert config["encoding"]["x"]["title"] == "Variable B"

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

def test_barplot_accepts_fn_as_value(self):
plot = gr.BarPlot(
value=lambda: barley.sample(frac=0.1, replace=False),
x="year",
y="yield",
)
assert isinstance(plot.value, dict)
assert isinstance(plot.value["plot"], str)
Loading