Files
salmanoff/commonLibs/attachmentSupport/tests/deviceAttachmentSpecParams_tests.cpp
T
hayodea b198f6a42b Add shared DeviceAttachmentSpec param parsing helpers.
Centralize DAP param lookup, parsing, and validation primitives so stimBuff
and threshold modules can share one implementation instead of duplicating
parse logic per plugin.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 11:00:22 -04:00

414 lines
12 KiB
C++

#include <array>
#include <gtest/gtest.h>
#include <support/exceptionAssertions.h>
#include <user/deviceAttachmentSpec.h>
#include <vector>
namespace smo {
namespace device {
namespace {
using ParamList = std::vector<std::pair<std::string, std::string>>;
ParamList makeParams(std::initializer_list<std::pair<const char*, const char*>> entries)
{
ParamList params;
for (const auto& entry : entries)
{
params.emplace_back(entry.first, entry.second);
}
return params;
}
TEST(DeviceAttachmentSpecNamesContainTest, MatchesVectorAndArraySynonyms)
{
const std::vector<std::string> widthSynonyms = {
"frame-width",
"dim-w",
};
EXPECT_TRUE(DeviceAttachmentSpec::namesContain(widthSynonyms, "dim-w"));
EXPECT_FALSE(DeviceAttachmentSpec::namesContain(widthSynonyms, "dim-h"));
const std::array<std::string_view, 2> heightSynonyms = {
"frame-height",
"dim-h",
};
EXPECT_TRUE(DeviceAttachmentSpec::namesContain(heightSynonyms, "frame-height"));
EXPECT_FALSE(DeviceAttachmentSpec::namesContain(heightSynonyms, "frame-width"));
}
TEST(DeviceAttachmentSpecParamsContainTest, DetectsExactParamName)
{
const ParamList params = makeParams({
{"display", "0"},
{"screen", "1"},
});
EXPECT_TRUE(DeviceAttachmentSpec::paramsContain(params, "display"));
EXPECT_TRUE(DeviceAttachmentSpec::paramsContain(params, "screen"));
EXPECT_FALSE(DeviceAttachmentSpec::paramsContain(params, "width"));
}
TEST(DeviceAttachmentSpecParamNameMatchesAnySynonymGroupTest, MatchesAcrossGroups)
{
const std::vector<std::string> widthSynonyms = {"dim-w", "frame-w"};
const std::vector<std::string> heightSynonyms = {"dim-h", "frame-h"};
EXPECT_TRUE(DeviceAttachmentSpec::paramNameMatchesAnySynonymGroup(
"dim-w", widthSynonyms, heightSynonyms));
EXPECT_TRUE(DeviceAttachmentSpec::paramNameMatchesAnySynonymGroup(
"frame-h", widthSynonyms, heightSynonyms));
EXPECT_FALSE(DeviceAttachmentSpec::paramNameMatchesAnySynonymGroup(
"colour-space", widthSynonyms, heightSynonyms));
}
TEST(DeviceAttachmentSpecNormalizeParamTokenTest, TrimsAndLowercases)
{
EXPECT_EQ(DeviceAttachmentSpec::normalizeParamToken(" TRUE "), "true");
EXPECT_EQ(DeviceAttachmentSpec::normalizeParamToken("YUV"), "yuv");
EXPECT_EQ(DeviceAttachmentSpec::normalizeParamToken("480p"), "480p");
EXPECT_EQ(DeviceAttachmentSpec::normalizeParamToken(""), "");
}
TEST(DeviceAttachmentSpecFindOptionalParamTest, LattermostExactNameWins)
{
const ParamList params = makeParams({
{"display", "0"},
{"other", "ignored"},
{"display", "2"},
});
const std::optional<std::pair<std::string, std::string>> matched =
DeviceAttachmentSpec::findOptionalParam(params, "display");
ASSERT_TRUE(matched.has_value());
EXPECT_EQ(matched->first, "display");
EXPECT_EQ(matched->second, "2");
}
TEST(DeviceAttachmentSpecFindOptionalParamWithSynonymsTest, LattermostSynonymWins)
{
const std::vector<std::string> synonyms = {
"cmd-timeout-ms",
"command-timeout-ms",
};
const ParamList params = makeParams({
{"cmd-timeout-ms", "5"},
{"command-timeout-ms", "25"},
});
const std::optional<std::pair<std::string, std::string>> matched =
DeviceAttachmentSpec::findOptionalParamWithSynonyms(params, synonyms);
ASSERT_TRUE(matched.has_value());
EXPECT_EQ(matched->first, "command-timeout-ms");
EXPECT_EQ(matched->second, "25");
}
TEST(DeviceAttachmentSpecFindOptionalParamWithSynonymsTest, AbsentReturnsNullopt)
{
const std::vector<std::string> synonyms = {"data-port"};
const ParamList params = makeParams({{"cmd-port", "56001"}});
EXPECT_FALSE(DeviceAttachmentSpec::findOptionalParamWithSynonyms(
params, synonyms).has_value());
}
TEST(DeviceAttachmentSpecParseParamValueAsIntTest, ParsesValidInteger)
{
EXPECT_EQ(
DeviceAttachmentSpec::parseParamValueAsInt("width", "1280"),
1280);
EXPECT_EQ(
DeviceAttachmentSpec::parseParamValueAsInt("width", "-42"),
-42);
}
TEST(DeviceAttachmentSpecParseParamValueAsIntTest, InvalidIntegerThrows)
{
try {
DeviceAttachmentSpec::parseParamValueAsInt("width", "not-a-number");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "Failed to parse 'width'");
}
}
TEST(DeviceAttachmentSpecParseParamValueAsIntTest, OutOfRangeThrows)
{
try {
DeviceAttachmentSpec::parseParamValueAsInt(
"width",
"999999999999999999999");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "out of range");
}
}
TEST(DeviceAttachmentSpecParseParamValueAsPositiveIntTest, RejectsNonPositive)
{
EXPECT_EQ(
DeviceAttachmentSpec::parseParamValueAsPositiveInt("width", "640"),
640);
try {
DeviceAttachmentSpec::parseParamValueAsPositiveInt("width", "0");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "must be positive");
}
try {
DeviceAttachmentSpec::parseParamValueAsPositiveInt("width", "-10");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "must be positive");
}
}
TEST(DeviceAttachmentSpecParseParamValueAsBoolTest, ParsesRecognizedValues)
{
EXPECT_TRUE(DeviceAttachmentSpec::parseParamValueAsBool(""));
EXPECT_TRUE(DeviceAttachmentSpec::parseParamValueAsBool("true"));
EXPECT_TRUE(DeviceAttachmentSpec::parseParamValueAsBool(" YES "));
EXPECT_TRUE(DeviceAttachmentSpec::parseParamValueAsBool("1"));
EXPECT_FALSE(DeviceAttachmentSpec::parseParamValueAsBool("false"));
EXPECT_FALSE(DeviceAttachmentSpec::parseParamValueAsBool("0"));
EXPECT_FALSE(DeviceAttachmentSpec::parseParamValueAsBool("no"));
EXPECT_FALSE(DeviceAttachmentSpec::parseParamValueAsBool(
"", /*emptyMeansTrue=*/false));
}
TEST(DeviceAttachmentSpecParseParamValueAsBoolTest, UnknownValueThrows)
{
EXPECT_THROW(
DeviceAttachmentSpec::parseParamValueAsBool("maybe"),
std::runtime_error);
}
TEST(DeviceAttachmentSpecParseRequiredParamAsIntTest, MissingParamThrows)
{
const ParamList params = makeParams({{"screen", "1"}});
try {
DeviceAttachmentSpec::parseRequiredParamAsInt(params, "display");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "No display specified in params");
}
}
TEST(DeviceAttachmentSpecParseRequiredParamAsIntTest, LattermostValueWins)
{
const ParamList params = makeParams({
{"display", "0"},
{"display", "3"},
});
EXPECT_EQ(
DeviceAttachmentSpec::parseRequiredParamAsInt(params, "display"),
3);
}
TEST(DeviceAttachmentSpecParseOptionalParamAsIntTest, ReturnsDefaultWhenAbsent)
{
const ParamList params = makeParams({{"screen", "1"}});
EXPECT_EQ(
DeviceAttachmentSpec::parseOptionalParamAsInt(params, "display", 42),
42);
}
TEST(DeviceAttachmentSpecParseOptionalParamAsIntWithSynonymsTest, SynonymLattermostWins)
{
const std::vector<std::string> synonyms = {
"n-dgrams-per-frame",
"num-dgrams-per-frame",
};
const ParamList params = makeParams({
{"n-dgrams-per-frame", "10"},
{"num-dgrams-per-frame", "99"},
});
EXPECT_EQ(
DeviceAttachmentSpec::parseOptionalParamAsIntWithSynonyms(
params, synonyms, 84),
99);
}
TEST(DeviceAttachmentSpecParseRequiredParamAsIntWithSynonymsTest, ParsesMatchedSynonym)
{
const std::vector<std::string> synonyms = {
"cmd-timeout-ms",
"command-timeout-ms",
};
const ParamList params = makeParams({
{"command-timeout-ms", "15"},
});
EXPECT_EQ(
DeviceAttachmentSpec::parseRequiredParamAsIntWithSynonyms(
params,
synonyms,
"missing timeout"),
15);
}
TEST(DeviceAttachmentSpecParseRequiredParamValueWithSynonymsTest, RequiresNonEmptyValue)
{
const std::vector<std::string> synonyms = {"colour-space"};
const ParamList missing = makeParams({{"width", "640"}});
try {
DeviceAttachmentSpec::parseRequiredParamValueWithSynonyms(
missing, synonyms, "colour-space required");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "colour-space required");
}
const ParamList emptyValue = makeParams({{"colour-space", ""}});
try {
DeviceAttachmentSpec::parseRequiredParamValueWithSynonyms(
emptyValue, synonyms, "colour-space required");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "colour-space required");
}
const ParamList present = makeParams({{"colour-space", "yuv"}});
EXPECT_EQ(
DeviceAttachmentSpec::parseRequiredParamValueWithSynonyms(
present, synonyms, "colour-space required"),
"yuv");
}
TEST(DeviceAttachmentSpecParseOptionalParamValueWithSynonymsTest, OptionalStringValue)
{
const std::vector<std::string> synonyms = {"colour-space"};
const ParamList absent = makeParams({{"width", "640"}});
EXPECT_FALSE(DeviceAttachmentSpec::parseOptionalParamValueWithSynonyms(
absent, synonyms, "empty colour-space").has_value());
const ParamList emptyValue = makeParams({{"colour-space", ""}});
try {
DeviceAttachmentSpec::parseOptionalParamValueWithSynonyms(
emptyValue, synonyms, "empty colour-space");
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "empty colour-space");
}
const ParamList present = makeParams({{"colour-space", "yuv"}});
const std::optional<std::string> parsed =
DeviceAttachmentSpec::parseOptionalParamValueWithSynonyms(
present, synonyms, "empty colour-space");
ASSERT_TRUE(parsed.has_value());
EXPECT_EQ(*parsed, "yuv");
}
TEST(DeviceAttachmentSpecParseOptionalParamAsBoolWithSynonymsTest, ParsesPresenceAndValues)
{
const std::vector<std::string> synonyms = {
"full-planar-is-optional",
"opt-planar",
};
const ParamList absent = makeParams({{"width", "640"}});
EXPECT_FALSE(DeviceAttachmentSpec::parseOptionalParamAsBoolWithSynonyms(
absent, synonyms, false));
const ParamList presentEmpty = makeParams({{"opt-planar", ""}});
EXPECT_TRUE(DeviceAttachmentSpec::parseOptionalParamAsBoolWithSynonyms(
presentEmpty, synonyms, false));
const ParamList presentFalse = makeParams({{"opt-planar", "false"}});
EXPECT_FALSE(DeviceAttachmentSpec::parseOptionalParamAsBoolWithSynonyms(
presentFalse, synonyms, true));
const ParamList lattermostWins = makeParams({
{"opt-planar", "true"},
{"full-planar-is-optional", "false"},
});
EXPECT_FALSE(DeviceAttachmentSpec::parseOptionalParamAsBoolWithSynonyms(
lattermostWins, synonyms, true));
}
TEST(DeviceAttachmentSpecRejectUnknownParamsTest, RejectsUnknownNames)
{
const std::vector<std::string> knownA = {"width", "dim-w"};
const std::vector<std::string> knownB = {"height", "dim-h"};
const ParamList params = makeParams({
{"dim-w", "640"},
{"unknown-param", "1"},
});
try {
DeviceAttachmentSpec::rejectUnknownParams(
params,
"unknown param '",
knownA,
knownB);
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception, "unknown-param");
}
}
TEST(DeviceAttachmentSpecRejectUnknownParamsTest, AcceptsAllKnownNames)
{
const std::vector<std::string> knownA = {"width", "dim-w"};
const std::vector<std::string> knownB = {"height", "dim-h"};
const ParamList params = makeParams({
{"dim-w", "640"},
{"dim-h", "480"},
});
EXPECT_NO_THROW(DeviceAttachmentSpec::rejectUnknownParams(
params,
"unknown param '",
knownA,
knownB));
}
} // namespace
} // namespace device
} // namespace smo