Files
MuditaOS/module-audio/Audio/test/unittest_audio.cpp
Hubert Chrzaniuk bb76fe4080 [EGD-5713] Change volume hardware buttons behaviour
The volume button policy has been redesigned to be less
ambiguous for the user.
2021-03-01 13:54:39 +01:00

477 lines
20 KiB
C++

// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include "Audio/decoder/Decoder.hpp"
#include "Audio/decoder/decoderMP3.hpp"
#include "Audio/decoder/decoderFLAC.hpp"
#include "Audio/decoder/decoderWAV.hpp"
#include "Audio/AudioCommon.hpp"
#include "Audio/AudioMux.hpp"
#include "Audio/Audio.hpp"
#include "Audio/Operation/Operation.hpp"
using namespace audio;
TEST_CASE("Test audio tags")
{
SECTION(" Encoder tests ")
{
std::vector<std::string> testExtensions = {"flac", "wav", "mp3"};
for (auto ext : testExtensions) {
auto dec = audio::Decoder::Create(("testfiles/audio." + ext).c_str());
REQUIRE(dec);
auto tags = dec->fetchTags();
REQUIRE(tags);
REQUIRE(tags->title == ext + " Test track title");
REQUIRE(tags->artist == ext + " Test artist name");
REQUIRE(tags->album == ext + " Test album title");
REQUIRE(tags->year == "2020");
}
}
}
TEST_CASE("Audio settings string creation")
{
SECTION("Create volume string for playback loudspeaker, multimedia")
{
const auto str = audio::dbPath(sys::phone_modes::PhoneMode::Connected,
audio::Setting::Volume,
audio::PlaybackType::Multimedia,
audio::Profile::Type::PlaybackLoudspeaker);
REQUIRE_FALSE(str.empty());
REQUIRE(str == "audio/Connected/PlaybackLoudspeaker/Multimedia/Volume");
}
SECTION("Create volume string for routing speakerphone")
{
const auto str = audio::dbPath(sys::phone_modes::PhoneMode::Offline,
audio::Setting::Volume,
audio::PlaybackType::None,
audio::Profile::Type::RoutingLoudspeaker);
REQUIRE_FALSE(str.empty());
REQUIRE(str == "audio/Offline/RoutingLoudspeaker/Volume");
}
SECTION("Create gain string for recording built-in microphone")
{
const auto str = audio::dbPath(sys::phone_modes::PhoneMode::DoNotDisturb,
audio::Setting::Gain,
audio::PlaybackType::None,
audio::Profile::Type::RecordingBuiltInMic);
REQUIRE_FALSE(str.empty());
REQUIRE(str == "audio/DoNotDisturb/RecordingBuiltInMic/Gain");
}
SECTION("Create empty volume string when Idle")
{
const auto str = audio::dbPath(sys::phone_modes::PhoneMode::Offline,
audio::Setting::Volume,
audio::PlaybackType::None,
audio::Profile::Type::Idle);
REQUIRE(str.empty());
}
SECTION("System settings change")
{
struct TestCase
{
PlaybackType playbackType;
Setting setting;
std::string path;
};
std::vector<TestCase> testCases = {
// system volume
{PlaybackType::System, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
{PlaybackType::Meditation, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
{PlaybackType::CallRingtone, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
{PlaybackType::KeypadSound, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
{PlaybackType::TextMessageRingtone,
Setting::Volume,
"audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
{PlaybackType::Notifications, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Notifications/Volume"},
// other types volume
{PlaybackType::Alarm, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Alarm/Volume"},
{PlaybackType::Multimedia, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Multimedia/Volume"},
{PlaybackType::None, Setting::Volume, "audio/Offline/RecordingBuiltInMic/Volume"},
// EnableSound
{PlaybackType::System, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/Notifications/EnableSound"},
{PlaybackType::Meditation,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/Meditation/EnableSound"},
{PlaybackType::CallRingtone,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/CallRingtone/EnableSound"},
{PlaybackType::KeypadSound,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/KeypadSound/EnableSound"},
{PlaybackType::TextMessageRingtone,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/TextMessageRingtone/EnableSound"},
{PlaybackType::Notifications,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/Notifications/EnableSound"},
{PlaybackType::Alarm, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/Alarm/EnableSound"},
{PlaybackType::Multimedia,
Setting::EnableSound,
"audio/Offline/RecordingBuiltInMic/Multimedia/EnableSound"},
{PlaybackType::None, Setting::EnableSound, "audio/Offline/RecordingBuiltInMic/EnableSound"},
};
for (auto &testCase : testCases) {
const auto str = audio::dbPath(sys::phone_modes::PhoneMode::Offline,
testCase.setting,
testCase.playbackType,
audio::Profile::Type::RecordingBuiltInMic);
REQUIRE_FALSE(str.empty());
REQUIRE(str == testCase.path);
}
}
}
class MockAudio : public audio::Audio
{
public:
MockAudio(audio::Audio::State state, audio::PlaybackType plbckType, audio::Operation::State opState)
: audio::Audio([](const sys::Message *e) { return std::optional<std::string>(); }), state(state),
plbckType(plbckType), opState(opState)
{}
audio::PlaybackType GetCurrentOperationPlaybackType() const override
{
return plbckType;
}
audio::Operation::State GetCurrentOperationState() const override
{
return opState;
}
State GetCurrentState() const override
{
return state;
}
State state = State::Idle;
audio::PlaybackType plbckType;
audio::Operation::State opState;
};
TEST_CASE("Test AudioMux")
{
using namespace audio;
int16_t tkId = 0;
auto insertAudio = [](std::vector<AudioMux::Input> &inputs,
Audio::State state,
PlaybackType plbckType,
Operation::State opState,
int16_t t) {
inputs.emplace_back(AudioMux::Input(std::make_unique<MockAudio>(state, plbckType, opState), Token(t)));
return t;
};
SECTION("Check Audio::Mux GetInput by Token")
{
int16_t tokenIdx = 1;
std::vector<AudioMux::Input> audioInputs;
AudioMux aMux(audioInputs);
GIVEN("One Input")
{
WHEN("Input exists")
{
tkId =
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token(tkId)) != std::nullopt);
}
WHEN("Input does not exists")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token(tokenIdx + 1)) == std::nullopt);
}
WHEN("Token passed is not valid")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token::MakeBadToken()) == std::nullopt);
}
WHEN("Token passed is uninitialized")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token()) == std::nullopt);
}
}
GIVEN("N Inputs")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
tkId = insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
WHEN("Input exists")
{
REQUIRE(aMux.GetInput(Token(tkId)) != std::nullopt);
}
WHEN("Input does not exists")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token(tokenIdx + 1)) == std::nullopt);
}
}
}
SECTION("Check Audio::Mux GetInput by State")
{
int16_t tokenIdx = 1;
std::vector<AudioMux::Input> audioInputs;
AudioMux aMux(audioInputs);
auto testState1 = Audio::State::Playback;
auto testState2 = Audio::State::Recording;
auto testStateExcuded = Audio::State::Routing;
GIVEN("One Input")
{
WHEN("Input with given State exists")
{
tkId = insertAudio(audioInputs, testState1, PlaybackType::None, Operation::State::Idle, tokenIdx);
WHEN("One State in vector")
{
auto retInput = aMux.GetInput({testState1});
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Multiple State enums in vector")
{
auto retInput = aMux.GetInput({testState2, testState1});
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
}
WHEN("Input with given State does not exists")
{
insertAudio(audioInputs, testStateExcuded, PlaybackType::None, Operation::State::Idle, tokenIdx);
REQUIRE(aMux.GetInput(Token(tokenIdx + 1)) == std::nullopt);
}
}
GIVEN("N Inputs")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx++);
tkId = insertAudio(audioInputs, testState1, PlaybackType::None, Operation::State::Idle, tokenIdx++);
insertAudio(audioInputs, testState2, PlaybackType::None, Operation::State::Idle, tokenIdx++);
WHEN("Input with given State exists")
{
WHEN("One State in vector")
{
auto retInput = aMux.GetInput({testState1});
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Multiple State enums in vector")
{
auto retInput = aMux.GetInput({testState1, testState2});
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Multiple State enums in vector (changed order in vector)")
{
auto retInput = aMux.GetInput({testState2, testState1});
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
}
}
}
SECTION("Check Audio::Mux GetAvailableInput")
{
int16_t tokenIdx = 1;
std::vector<AudioMux::Input> audioInputs;
AudioMux aMux(audioInputs);
auto testPlaybackTypeLowPrio = PlaybackType::Alarm;
auto testPlaybackTypeMidPrio = PlaybackType::Multimedia;
auto testPlaybackTypeHighPrio = PlaybackType::CallRingtone;
auto mergableType = PlaybackType::Notifications;
GIVEN("One Input")
{
WHEN("When idle input available")
{
tkId =
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeLowPrio);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Should reject due to higher priority PlaybackType active")
{
tkId = insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeMidPrio, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeLowPrio);
REQUIRE(retInput == std::nullopt);
}
WHEN("Should take over due to lower priority PlaybackType active")
{
tkId = insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeLowPrio, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeMidPrio);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Should merge due to same mergable type active")
{
tkId = insertAudio(audioInputs, Audio::State::Playback, mergableType, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(mergableType);
REQUIRE(retInput == std::nullopt);
}
}
GIVEN("N Inputs")
{
insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeLowPrio, Operation::State::Idle, tokenIdx++);
tkId = insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeLowPrio, Operation::State::Idle, tokenIdx++);
WHEN("When idle input available")
{
tkId =
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeLowPrio);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Should reject due to higher priority PlaybackType active")
{
insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeHighPrio, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeLowPrio);
REQUIRE(retInput == std::nullopt);
}
WHEN("Should reject due to higher priority PlaybackType active even if Idle available")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeHighPrio, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeLowPrio);
REQUIRE(retInput == std::nullopt);
}
WHEN("Should take over due to lower priority PlaybackType active")
{
auto retInput = aMux.GetAvailableInput(testPlaybackTypeMidPrio);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Should merge due to same mergable type active")
{
insertAudio(audioInputs, Audio::State::Playback, mergableType, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(mergableType);
REQUIRE(retInput == std::nullopt);
}
WHEN("Should merge due to same mergable type active even if Idle available")
{
insertAudio(audioInputs, Audio::State::Idle, PlaybackType::None, Operation::State::Idle, tokenIdx);
insertAudio(
audioInputs, Audio::State::Playback, testPlaybackTypeMidPrio, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetAvailableInput(testPlaybackTypeMidPrio);
REQUIRE(retInput != std::nullopt);
}
}
}
SECTION("Check Audio::Mux GetPlaybackInput")
{
int16_t tokenIdx = 1;
std::vector<AudioMux::Input> audioInputs;
AudioMux aMux(audioInputs);
auto testPlaybackTypeLowPrio = PlaybackType::Multimedia;
auto testPlaybackTypeHighPrio = PlaybackType::CallRingtone;
GIVEN("One Input")
{
WHEN("When free inputs available")
{
tkId = insertAudio(
audioInputs, Audio::State::Playback, PlaybackType::Multimedia, Operation::State::Active, tokenIdx);
auto retInput = aMux.GetPlaybackInput(testPlaybackTypeLowPrio);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("Should reject due to Recording active")
{
tkId = insertAudio(
audioInputs, Audio::State::Recording, PlaybackType::None, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetPlaybackInput(testPlaybackTypeHighPrio);
REQUIRE(retInput == std::nullopt);
}
WHEN("Should reject due to Routing active")
{
tkId = insertAudio(
audioInputs, Audio::State::Routing, PlaybackType::None, Operation::State::Idle, tokenIdx);
auto retInput = aMux.GetPlaybackInput(testPlaybackTypeHighPrio);
REQUIRE(retInput == std::nullopt);
}
}
}
SECTION("Check Audio::Mux GetRoutingInput")
{
int16_t tokenIdx = 1;
std::vector<AudioMux::Input> audioInputs;
AudioMux aMux(audioInputs);
GIVEN("One Input")
{
WHEN("When Routing input active")
{
tkId = insertAudio(
audioInputs, Audio::State::Routing, PlaybackType::None, Operation::State::Active, tokenIdx);
auto retInput = aMux.GetRoutingInput();
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
WHEN("When active Routing not available without force")
{
tkId = insertAudio(audioInputs,
Audio::State::Playback,
PlaybackType::CallRingtone,
Operation::State::Active,
tokenIdx);
auto retInput = aMux.GetRoutingInput();
REQUIRE(retInput == std::nullopt);
}
WHEN("When active Routing not available with force")
{
tkId = insertAudio(audioInputs,
Audio::State::Playback,
PlaybackType::CallRingtone,
Operation::State::Active,
tokenIdx);
auto retInput = aMux.GetRoutingInput(true);
REQUIRE(retInput != std::nullopt);
REQUIRE((*retInput)->token == Token(tkId));
}
}
}
}