def test_transmit_no_speaker(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # transmit data transmission.transmit({ "some_feature1": 0, "some_feature2": 1 }, None, config) # check that the data is on the server response = requests.get("{}/devices/{}/recordings".format( config.get("server"), config.get("device_id"))) response_data = response.json() recording = requests.get("{}/recordings/{}".format( config.get("server"), response_data[0]["recordingId"])) recording_data = recording.json() assert len(response_data) == 1 assert json.loads(recording_data["data"]) == { "some_feature1": 0, "some_feature2": 1 } assert response_data[0]["speakerId"] == None # clean up server requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_save_settings_no_semaphore(monkeypatch): # initialize config config = settings.Config() # mock filename so it saves in a different file to examine monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # parameters new_settings = {"speaker_diarization": True} new_settings_id = "some id" semaphore = multiprocessing.Semaphore( config.get("num_cores") - 2) # the process will release one semaphore itself event = multiprocessing.Event() event.set() lock = multiprocessing.Lock() # call function process = multiprocessing.Process(target=settings.update_settings, args=(config, new_settings, new_settings_id, semaphore, event, lock)) process.start() # give the Process time to finish the updatep time.sleep(1) # stop the Process (it should be stuck, waiting) process.terminate() # test that setting is not updated in program assert not config.get("speaker_diarization") == True # test file is not created assert not "test_temp.ini" in os.listdir(get_package_dir())
def test_load_settings(): result = settings.Config() # check that settings are loaded properly with the right types assert all([ all( type(result.settings[name]) == int for name in [ "milliseconds_per_second", "num_cores", "min_empty_space_in_bytes", "vad_level", "num_bytes", "num_channels", "rate", "vad_frame_ms", "max_speakers", "speaker_reid_distance_threshold", "max_number_of_speakers" ]), all( type(result.settings[name]) == float for name in [ "periodic_sample_rate", "min_sample_length", "max_sample_length", "max_silence_length" ]), all( type(result.settings[name]) == bool for name in ["speaker_diarization"]), all( type(result.settings[name]) == datetime.timedelta for name in ["speaker_forget_interval"]), all( type(result.settings[name]) == str for name in ["server", "device_id", "settings_id"]) ]) # check that calculated settings are also included assert all(name in result.settings for name in [ "vad_frame_size", "vad_frame_bytes", "format", "periodic_sample_rate", "min_sample_frames", "max_sample_frames", "max_silence_frames" ])
def test_update_device_settings(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # get old settings ID old_settings_id = config.get("settings_id") # update a setting config.set("min_empty_space_in_bytes", 3) # call update settings on server settings_id = transmission.update_device_settings(config) # check that the settings ID is updated assert not settings_id == old_settings_id # check that the settings are up to date on the server response = requests.get("{}/devices/settings/{}".format( config.get("server"), settings_id)) response_data = response.json() assert json.loads( response_data["properties"])["min_empty_space_in_bytes"] == 3 # clean up server requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_check_for_updates(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # multiprocessing objects semaphore = multiprocessing.Semaphore(config.get("num_cores")) event = multiprocessing.Event() event.set() lock = multiprocessing.Lock() # register a device with the server transmission.register_device(config) settings_dictionary = {config.get("settings_id"): config} # keep old settings id old_settings_id = config.get("settings_id") # simulate changing a setting on the server requests.put("{}/devices/{}/settings".format(config.get("server"), config.get("device_id")), data={ "settings": json.dumps({ "stopRecordingThreshold": 25, "daysToForgetSpeaker": 21 }) }) # call check for updates transmission.check_for_updates(config, settings_dictionary, semaphore, event, lock) # check that settings were updated assert config.get("min_empty_space_in_bytes") == 25 assert config.get("speaker_forget_interval") == datetime.timedelta(days=21) assert not config.get("settings_id") == old_settings_id assert len(settings_dictionary) == 2 # check that settings dictionary was updated properly assert config.get("settings_id") in settings_dictionary assert old_settings_id in settings_dictionary assert settings_dictionary[old_settings_id].get( "min_empty_space_in_bytes") != 25 assert settings_dictionary[old_settings_id].get( "speaker_forget_interval") != datetime.timedelta(days=21) assert settings_dictionary[config.get("settings_id")].get( "min_empty_space_in_bytes") == 25 assert settings_dictionary[config.get("settings_id")].get( "speaker_forget_interval") == datetime.timedelta(days=21) # clean up server requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_register_device_already_registered(monkeypatch): # set up config with a registed device ID config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") config.set("device_id", "SOME_ID") # call device registration transmission.register_device(config) # check that the device id has not changed assert config.get("device_id") == "SOME_ID"
def test_register_device(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # call device registration transmission.register_device(config) # check that the device id was updated assert not config.get("device_id") == "None" assert not config.get("settings_id") == "None" # clean up server requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_update_speaker(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # testing data mean = numpy.array([1, 2]) covariance = numpy.array([[1, 2], [3, 4]]) # register speaker speaker_id = transmission.register_speaker(config, mean, covariance) # check that speaker was registered response = requests.get("{}/speakers/{}".format(config.get("server"), speaker_id)) assert response.status_code == 200 # update data new_mean = numpy.array([5, 6]) new_covariance = numpy.array([[0, 1], [1, 0]]) new_count = 5 # call update speaker transmission.update_speaker(config, speaker_id, new_mean, new_covariance, new_count) # check that the speaker was updated speakers = transmission.get_speakers(config) assert speaker_id in speakers assert (speakers[speaker_id]["mean"] == new_mean).all() assert (speakers[speaker_id]["covariance"] == new_covariance).all() assert speakers[speaker_id]["count"] == new_count assert type(speakers[speaker_id]["last_seen"]) == datetime.datetime # clean up server requests.delete("{}/speakers/{}".format(config.get("server"), speaker_id)) requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_transmit_speaker(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # testing speaker data mean = numpy.array([1, 2]) covariance = numpy.array([[1, 2], [3, 4]]) # register speaker speaker_id = transmission.register_speaker(config, mean, covariance) # transmit data transmission.transmit({ "some_feature1": 0, "some_feature2": 1 }, speaker_id, config) # check that the data is on the server response = requests.get("{}/devices/{}/recordings".format( config.get("server"), config.get("device_id"))) response_data = response.json() recording = requests.get("{}/recordings/{}".format( config.get("server"), response_data[0]["recordingId"])) recording_data = recording.json() assert len(response_data) == 1 assert json.loads(recording_data["data"]) == { "some_feature1": 0, "some_feature2": 1 } assert response_data[0]["speakerId"] == speaker_id # clean up server requests.delete("{}/speakers/{}".format(config.get("server"), speaker_id)) requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_get_speakers(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # testing data means = [numpy.array([1, 2]), numpy.array([2, 3]), numpy.array([3, 4])] covariances = [ numpy.array([[1, 2], [3, 4]]), numpy.array([[0, 0], [0, 0]]), numpy.array([[2, 3], [4, 5]]) ] # register speakers ids = [] for mean, covariance in zip(means, covariances): ids.append(transmission.register_speaker(config, mean, covariance)) # call get speakers result = transmission.get_speakers(config) # check that proper result was returned assert set(result.keys()) == set(ids) for i in range(len(ids)): assert (result[ids[i]]["mean"] == means[i]).all() assert (result[ids[i]]["covariance"] == covariances[i]).all() assert result[ids[i]]["count"] == 1 assert type(result[ids[i]]["last_seen"]) == datetime.datetime # clean up server for speaker_id in ids: requests.delete("{}/speakers/{}".format(config.get("server"), speaker_id)) requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_register_speaker(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # testing data audio_mean = numpy.array([1, 2]) audio_covariance = numpy.array([[1, 2], [3, 4]]) # call speaker registration result = transmission.register_speaker(config, audio_mean, audio_covariance) # check that valid speaker ID is returned assert type(result) == str # clean up server requests.delete("{}/speakers/{}".format(config.get("server"), result)) requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
def test_delete_speaker(monkeypatch): # set up config config = settings.Config() monkeypatch.setattr(settings, "FILENAME", "test_temp.ini") # register a device with the server transmission.register_device(config) # testing data mean = numpy.array([1, 2]) covariance = numpy.array([[1, 2], [3, 4]]) # register speaker speaker_id = transmission.register_speaker(config, mean, covariance) # check that speaker was registered response = requests.get("{}/speakers/{}".format(config.get("server"), speaker_id)) assert response.status_code == 200 # call delete speaker transmission.delete_speaker(config, speaker_id) # check that speaker is not returned in get_speakers speakers = transmission.get_speakers(config) assert speaker_id not in speakers # check that speaker still exists but is marked as inactive response = requests.get("{}/speakers/{}".format(config.get("server"), speaker_id)) response_data = response.json() assert json.loads(response_data["data"])["active"] == False # clean up server requests.delete("{}/devices/{}".format(config.get("server"), config.get("device_id")))
# Automatically generates the testing audio files # Note that this functionality requires Internet acess from os import path, remove from pydub import AudioSegment from pydub.generators import WhiteNoise from gtts import gTTS from CAT import settings settings = settings.Config() # conglomerate all relevant settings here, so they can be hard-coded or overridden for tests more easily NUM_BYTES = settings.get("num_bytes") RATE = settings.get("rate") MILLISECONDS_PER_SECOND = settings.get("milliseconds_per_second") MIN_SAMPLE_LENGTH = settings.get("min_sample_length") MAX_SAMPLE_LENGTH = settings.get("max_sample_length") MAX_SILENCE_LENGTH = settings.get("max_silence_length") BITS_PER_BYTE = 8 MARGIN = 2 * settings.get("vad_frame_ms") # in milliseconds LARGER_MARGIN = settings.get("periodic_sample_rate") * MILLISECONDS_PER_SECOND def generate_speech(text, lang, file_path): speech_file = gTTS(text, lang=lang) speech_file.save("{}.mp3".format(file_path)) speech = AudioSegment.from_mp3("{}.mp3".format(file_path)) remove("{}.mp3".format(file_path)) return speech
def test_settings_to_string(): config = settings.Config() assert type(config.to_string()) == str
def test_save_settings(monkeypatch): # initialize config config = settings.Config() # mock filename so it saves in a different file to examine monkeypatch.setattr(settings, "FILENAME", os.path.join("CAT", "test_temp.ini")) # add a settings ID settings_dictionary = {"old_settings_id": config} config.set("settings_id", "old_settings_id") # parameters new_settings = { "vadLevel": 0, "vadSampleRate": 1 / 9, "speakerDiarizationEnabled": True, "daysToForgetSpeaker": 500, } semaphore = multiprocessing.Semaphore(config.get("max_number_of_speakers")) event = multiprocessing.Event() event.set() lock = multiprocessing.Lock() # call function settings.update_settings(config, settings_dictionary, new_settings, "some new settings id", semaphore, event, lock) # test that the event is reset assert event.is_set() # test that all semaphores were released for _ in range(config.get("max_number_of_speakers")): semaphore.acquire() # test that the lock was released lock.acquire() # test that the settings are updated in program assert config.get("vad_level") == 0 assert config.get("periodic_sample_rate") == 1 / 9 assert config.get("speaker_diarization") == True assert config.get("speaker_forget_interval") == datetime.timedelta( days=500) assert config.get("settings_id") == "some new settings id" # test file created assert "test_temp.ini" in os.listdir(get_package_dir()) # test that the setting appears in the file file = open(os.path.join(get_package_dir(), "test_temp.ini"), 'r') file_contents = file.read() assert "vad_level = 0" in file_contents assert "{} = {}".format("periodic_sample_rate", 1 / 9) in file_contents assert "speaker_diarization = True" in file_contents assert "speaker_forget_interval = 500" in file_contents assert "settings_id = some new settings id" in file_contents # check that new setting reads properly config2 = settings.Config() assert config2.get("vad_level") == 0 assert config2.get("periodic_sample_rate") == 1 / 9 assert config2.get("speaker_diarization") == True assert config2.get("speaker_forget_interval") == datetime.timedelta( days=500) assert config2.get("settings_id") == "some new settings id"
def test_save_settings_manage_processes(mock_stream, monkeypatch): # make new config file that can be edited testing_config_file = os.path.join(get_test_recording_dir(), "test_temp.ini") copyfile(settings.FILENAME, testing_config_file) monkeypatch.setattr(settings, "FILENAME", testing_config_file) # set mocks analysis_calls = multiprocessing.Queue() def analyze_mock(file_queue, speaker_dictionary, speaker_dictionary_lock, configs): analysis_calls.put(config.get("max_number_of_speakers")) monkeypatch.setattr(scheduling, "analyze_audio_file", analyze_mock) transmission_check_calls = multiprocessing.Queue() def transmission_check_mock(config, settings_dictionary, threads_ready_to_update, settings_update_event, settings_update_lock): transmission_check_calls.put(config.get("max_number_of_speakers")) monkeypatch.setattr(scheduling.transmission, "check_for_updates", transmission_check_mock) queue_calls = multiprocessing.Queue() original_function = record.queue_audio_buffer def queue_mock(audio_buffer, file_queue, config): queue_calls.put(config.get("max_number_of_speakers")) original_function(audio_buffer, file_queue, config) with mock.patch("CAT.record.queue_audio_buffer", wraps=queue_mock): # initialize config BaseManager.register('Config', settings.Config) config_manager = BaseManager() config_manager.start() config = config_manager.Config() # multiprocess shared parameters process_manager = multiprocessing.Manager() speaker_dictionary = process_manager.dict() settings_update_event = multiprocessing.Event() settings_update_event.set() threads_ready_to_update = multiprocessing.Semaphore( config.get("num_cores") - 1) settings_update_lock = multiprocessing.Lock() speaker_dictionary_lock = multiprocessing.Lock() file_queue = multiprocessing.Queue() # thread-safe FIFO queue settings_dictionary = process_manager.dict( {config.get("settings_id"): config}) # start the process (just one of each) recording_process = multiprocessing.Process( target=record.record, args=(file_queue, config, threads_ready_to_update, settings_update_event)) recording_process.start() analysis_process = multiprocessing.Process( target=scheduling.analyze_audio_files, args=(file_queue, speaker_dictionary, speaker_dictionary_lock, config, settings_dictionary, threads_ready_to_update, settings_update_event, settings_update_lock)) analysis_process.start() # start updating a setting process = multiprocessing.Process( target=settings.update_settings, args=(config, settings_dictionary, { "maxSpeakers": 20 }, "some_new_setting_id", threads_ready_to_update, settings_update_event, settings_update_lock)) process.start() # give the Processes time to run time.sleep(2) # terminate all processes process.terminate() recording_process.terminate() analysis_process.terminate() # check that the setting was updated assert config.get("max_number_of_speakers") == 20 # test that the setting appears in the file file = open(testing_config_file, 'r') assert "max_number_of_speakers = 20" in file.read() # check that new setting reads properly config2 = settings.Config() assert config2.get("max_number_of_speakers") == 20 # clean up os.remove(testing_config_file) config_manager.shutdown()
def config(): return settings.Config()
def test_get_settings(): config = settings.Config() for setting in config.settings: assert config.get(setting) == config.settings[setting]