Files
MuditaOS/module-services/service-cellular/tests/unittest_volteHandler.cpp
Marcin Zieliński 357ae2d51c [MOS-825] VoLTE steering according to IMSI
Steering the GUI and the modem according to
whether VoLTE is permitted for the active
SIM card's operator. This is done based on
the SIM card's IMSI string.

Additionally, made logging more consistent.
2022-11-30 08:29:39 +01:00

249 lines
8.4 KiB
C++

// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <catch2/catch.hpp>
#include <module-cellular/test/mock/AtCommon_channel.hpp>
#include <module-services/service-cellular/src/VolteHandler.hpp>
#include <queue>
#include <regex>
#include <utility>
using namespace at;
using namespace cellular::service;
using CommandAndResponse = std::pair<AT, Result>;
namespace
{
struct DummyModemResponseParser
{
template <typename... Args>
bool operator()(Args &&...args) const
{
FAIL("test depends on parser, but shouldn't");
return false;
}
};
struct QcfgImsThrowingParser
{
bool operator()(QcfgImsResult const &, bool) const
{
throw "IMS";
}
};
template <bool ReturnValue>
struct QcfgImsDummyParser
{
bool operator()(QcfgImsResult const &, bool) const
{
return ReturnValue;
}
};
std::string extractCommandMnemonic(std::string const &input)
{
std::regex const extractor("AT\\+([A-Z_]+).*");
std::smatch pieces;
if (!std::regex_match(input, pieces, extractor) || pieces.size() != 2) {
FAIL("bad command string given");
return "";
}
return pieces[1].str();
}
template <typename Container, typename... Item>
void push(Container &container, Item &&...items)
{
(container.push(items), ...);
}
} // namespace
namespace at
{
struct DummyOkResult : Result
{
DummyOkResult() : Result(Result::Code::OK, {})
{}
};
struct DummyErrorResult : Result
{
DummyErrorResult() : Result(Result::Code::ERROR, {})
{}
};
struct CommandSequentialChannel : ChannelMock
{
CommandSequentialChannel(std::queue<CommandAndResponse> &&chain) : chain_{chain}
{}
Result cmd(Cmd const &at)
{
if (chain_.empty()) {
FAIL("all commands for channel already done");
}
auto const &currentCommand = chain_.front();
auto currentCommandMnemonic =
(currentCommand.first == AT::QCFG_IMS) ? "QCFG" : magic_enum::enum_name(currentCommand.first);
if (currentCommandMnemonic != extractCommandMnemonic(at.getCmd())) {
FAIL("unexpected command in sequence");
}
auto ret = currentCommand.second;
chain_.pop();
return ret;
}
protected:
std::queue<CommandAndResponse> chain_;
};
} // namespace at
TEST_CASE("VoLTE handler test")
{
SECTION("ThrowsAboutVoiceDomainFailure_When_TryingToEnableVolte_And_VoiceDomainCommandRespondsNOK")
{
std::queue<CommandAndResponse> chain;
push(chain, std::make_pair(AT::QNVFW, DummyErrorResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, DummyModemResponseParser> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, true), Catch::Contains("voice domain"));
}
SECTION("ThrowsAboutMbnAutoselectFailure_When_TryingToEnableVolte_And_AutoselectCommandRespondsNOK")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QMBNCFG, DummyErrorResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, DummyModemResponseParser> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, true), Catch::Contains("MBN"));
}
SECTION("ThrowsAboutImsCheckingFailure_When_TryingToEnableVolte_And_ImsConfigurationQueryRespondsNOK")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QMBNCFG, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyErrorResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, DummyModemResponseParser> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, true), Catch::Contains("IMS"));
}
SECTION("ThrowsAboutImsCheckingFailure_When_TryingToEnableVolte_And_CantParseImsConfigurationQuery")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QMBNCFG, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsThrowingParser> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, true), Catch::Contains("IMS"));
}
SECTION("ReturnsOk_When_TryingToEnableVolte_And_AlreadyEnabled")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QMBNCFG, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<true>> handler;
REQUIRE(handler.switchVolte(channel, true, true));
}
SECTION("ReturnsOk_When_TryingToDisableVolte_And_AlreadyDisabled")
{
std::queue<CommandAndResponse> chain;
push(chain, std::make_pair(AT::QNVFW, DummyOkResult{}), std::make_pair(AT::QCFG_IMS, DummyOkResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<true>> handler;
REQUIRE(handler.switchVolte(channel, true, false));
}
SECTION("ReturnsFalse_When_TryingToDisableVolte_And_WasEnabledBefore")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<false>> handler;
REQUIRE_FALSE(handler.switchVolte(channel, true, false));
}
SECTION("ReturnsFalse_When_TryingToEnableVolte_And_WasEnabledBefore")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<false>> handler;
REQUIRE_FALSE(handler.switchVolte(channel, true, false));
}
SECTION("ThrowsAboutImsFailure_When_DisablingVolte_And_ImsSteeringCommandFailed")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyErrorResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<false>> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, false),
Catch::Contains("fail") && Catch::Contains("IMS"));
}
SECTION("ThrowsAboutImsFailure_When_EnablingVolte_And_ImsSteeringCommandFailed")
{
std::queue<CommandAndResponse> chain;
push(chain,
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QNVFW, DummyOkResult{}),
std::make_pair(AT::QMBNCFG, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyOkResult{}),
std::make_pair(AT::QCFG_IMS, DummyErrorResult{}));
CommandSequentialChannel channel(std::move(chain));
VolteHandler<CommandSequentialChannel, QcfgImsDummyParser<false>> handler;
REQUIRE_THROWS_WITH(handler.switchVolte(channel, true, true),
Catch::Contains("fail") && Catch::Contains("IMS"));
}
}