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. """ 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. """ 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. """ 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. """ 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. """ 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): """ Tests `choose_mic_arguments` when no command-line arguments are provided, """ 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. """ 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`). """ 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. """ 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. """ 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. """ 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