Files
pepperplus-ri/test/common/microphone_utils.py
Pim Hutting 051f904576 chore: add documentation RI
Code functionality left unchanged, only added docs where missing

close: N25B-298
2025-11-21 16:35:40 +01:00

245 lines
9.6 KiB
Python

from __future__ import unicode_literals # So that we can format strings with Unicode characters
import random
import sys
from StringIO import StringIO
from robot_interface.utils.microphone import (
choose_mic_default,
choose_mic_interactive,
choose_mic_arguments,
choose_mic,
get_microphones,
)
class MicrophoneUtils(object):
"""Shared tests for any PyAudio-like implementation, e.g. mock and real."""
def test_choose_mic_default(self, pyaudio_instance):
"""
Tests that the default microphone selection function returns a valid
microphone dictionary containing all necessary keys with correct types and values.
The result must contain at least "index", as this is used to identify the microphone,
and "name" for logging. It must have one or more channels (`maxInputChannels`),
and a default sample rate of at least 16000 Hz.
:param pyaudio_instance: A mocked or real PyAudio instance used to query microphone information.
:type pyaudio_instance: PyAudio
"""
result = choose_mic_default(pyaudio_instance)
assert "index" in result
assert isinstance(result["index"], (int, long))
assert "name" in result
assert isinstance(result["name"], (str, unicode))
assert "maxInputChannels" in result
assert isinstance(result["maxInputChannels"], (int, long))
assert result["maxInputChannels"] > 0
assert "defaultSampleRate" in result
assert isinstance(result["defaultSampleRate"], float)
assert result["defaultSampleRate"] >= 16000
def test_choose_mic_interactive_input_not_int(self, pyaudio_instance, mocker):
"""
Tests the robustness of the interactive selection when the user first enters
a non-integer value, ensuring the system prompts again without error and accepts
a valid integer on the second attempt.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
microphones = get_microphones(pyaudio_instance)
target_microphone = next(microphones)
mock_input = mocker.patch("__builtin__.raw_input", side_effect=["not an integer", "0"])
fake_out = StringIO()
mocker.patch.object(sys, "stdout", fake_out)
result = choose_mic_interactive(pyaudio_instance)
assert "index" in result
assert isinstance(result["index"], (int, long))
assert result["index"] == target_microphone["index"]
assert mock_input.called
assert any(p.startswith("Please enter a number") for p in fake_out.getvalue().splitlines())
def test_choose_mic_interactive_negative_index(self, pyaudio_instance, mocker):
"""
Tests that the interactive selection method prevents the user from entering
a negative integer as a microphone index.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
microphones = get_microphones(pyaudio_instance)
target_microphone = next(microphones)
mock_input = mocker.patch("__builtin__.raw_input", side_effect=["-1", "0"])
fake_out = StringIO()
mocker.patch.object(sys, "stdout", fake_out)
result = choose_mic_interactive(pyaudio_instance)
assert "index" in result
assert isinstance(result["index"], (int, long))
assert result["index"] == target_microphone["index"]
assert mock_input.called
assert any(p.startswith("Please enter a number") for p in fake_out.getvalue().splitlines())
def test_choose_mic_interactive_index_too_high(self, pyaudio_instance, mocker):
"""
Tests that the interactive selection method prevents the user from entering
an index that exceeds the total number of available microphones.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
real_count = len(list(get_microphones(pyaudio_instance)))
mock_input = mocker.patch("__builtin__.raw_input", side_effect=[str(real_count), "0"])
fake_out = StringIO()
mocker.patch.object(sys, "stdout", fake_out)
result = choose_mic_interactive(pyaudio_instance)
assert "index" in result
assert isinstance(result["index"], (int, long))
assert mock_input.called
assert any(p.startswith("Please enter a number") for p in fake_out.getvalue().splitlines())
def test_choose_mic_interactive_random_index(self, pyaudio_instance, mocker):
"""
Tests the core interactive functionality by simulating the selection of a
random valid microphone index and verifying that the correct microphone
information is returned.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
microphones = list(get_microphones(pyaudio_instance))
random_index = random.randrange(len(microphones))
mocker.patch("__builtin__.raw_input", side_effect=[str(random_index)])
result = choose_mic_interactive(pyaudio_instance)
assert "index" in result
assert isinstance(result["index"], (int, long))
assert result["index"] == microphones[random_index]["index"]
def test_choose_mic_no_arguments(self, pyaudio_instance, mocker):
mocker.patch.object(sys, "argv", [])
result = choose_mic_arguments(pyaudio_instance)
assert result is None
def test_choose_mic_arguments(self, pyaudio_instance, mocker):
"""
Tests `choose_mic_arguments` when the microphone name is passed as a separate
argument.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
for mic in get_microphones(pyaudio_instance):
mocker.patch.object(sys, "argv", ["--microphone", mic["name"]])
result = choose_mic_arguments(pyaudio_instance)
assert result is not None
assert result == mic
def test_choose_mic_arguments_eq(self, pyaudio_instance, mocker):
"""
Tests `choose_mic_arguments` when the microphone name is passed using an
equals sign (`--microphone=NAME`).
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
for mic in get_microphones(pyaudio_instance):
mocker.patch.object(sys, "argv", ["--microphone={}".format(mic["name"])])
result = choose_mic_arguments(pyaudio_instance)
assert result is not None
assert result == mic
def test_choose_mic_arguments_not_exist(self, pyaudio_instance, mocker):
"""
Tests `choose_mic_arguments` when a non-existent microphone name is passed
via command-line arguments, expecting the function to return None.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
mocker.patch.object(sys, "argv", ["--microphone", "Surely this microphone doesn't exist"])
result = choose_mic_arguments(pyaudio_instance)
assert result is None
def test_choose_mic_with_argument(self, pyaudio_instance, mocker):
"""
Tests `choose_mic` function when a valid microphone is
specified via command-line arguments.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
mic = next(get_microphones(pyaudio_instance))
mocker.patch.object(sys, "argv", ["--microphone", mic["name"]])
result = choose_mic(pyaudio_instance)
assert result is not None
assert result == mic
def test_choose_mic_no_argument(self, pyaudio_instance, mocker):
"""
Tests `choose_mic` function when no command-line arguments
are provided, verifying that the function falls back correctly to the
system's default microphone selection.
:param pyaudio_instance: A mocked or real PyAudio instance.
:type pyaudio_instance: PyAudio
:param mocker: The fixture used for mocking built-in functions and system objects.
:type mocker: pytest_mock.plugin.MockerFixture
"""
default_mic = choose_mic_default(pyaudio_instance)
mocker.patch.object(sys, "argv", [])
result = choose_mic(pyaudio_instance)
assert result is not None
assert result == default_mic