#include #include #include #include namespace smo { namespace intrin { namespace { using ParamList = std::vector>; ParamList makeParams(std::initializer_list> entries) { ParamList params; for (const auto& entry : entries) { params.emplace_back(entry.first, entry.second); } return params; } TEST(IntrinThresholdArrayContainsTest, MatchesCanonicalNames) { EXPECT_TRUE(arrayContains(kIntrinInterestPcNames, "interest-pc")); EXPECT_TRUE(arrayContains(kIntrinInterestThrNames, "interest-thr")); EXPECT_FALSE(arrayContains(kForbiddenUnitlessIntrinStems, "interest-pc")); } TEST(IntrinThresholdIsKnownParamNameTest, RecognizesAllCanonicalFamilies) { EXPECT_TRUE(isKnownIntrinThresholdParamName("interest-pc")); EXPECT_TRUE(isKnownIntrinThresholdParamName("interest-threshold")); EXPECT_TRUE(isKnownIntrinThresholdParamName("distraction-percentage")); EXPECT_TRUE(isKnownIntrinThresholdParamName("distraction-thr")); EXPECT_TRUE(isKnownIntrinThresholdParamName("stupefying-pc")); EXPECT_TRUE(isKnownIntrinThresholdParamName("stupefaction-thresh")); EXPECT_TRUE(isKnownIntrinThresholdParamName("intolerable-pc")); EXPECT_TRUE(isKnownIntrinThresholdParamName("intolerable-threshold")); EXPECT_FALSE(isKnownIntrinThresholdParamName("interest")); EXPECT_FALSE(isKnownIntrinThresholdParamName("passband-count-gt-val")); } TEST(IntrinThresholdValidateNoIntrinParamsOnQualeIfaceTest, RejectsKnownIntrinNames) { const ParamList params = makeParams({{"interest-pc", "85"}}); try { validateNoIntrinParamsOnQualeIface("pcloudLightAmbience", params); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "not valid on qualeIfaceApi"); } } TEST(IntrinThresholdValidateNoIntrinParamsOnQualeIfaceTest, RejectsUnitlessStems) { const ParamList params = makeParams({{"distraction", "10"}}); try { validateNoIntrinParamsOnQualeIface("mesh", params); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "invalid without a unit suffix"); } } TEST(IntrinThresholdValidateNoIntrinParamsOnQualeIfaceTest, RejectsFromStimbuffMarker) { const ParamList params = makeParams({{"from-stimbuff", ""}}); try { validateNoIntrinParamsOnQualeIface("mesh", params); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "from-stimbuff"); } } TEST(IntrinThresholdValidateNoIntrinParamsOnQualeIfaceTest, AllowsSensoryParams) { const ParamList params = makeParams({ {"passband-count-gt-val", "12"}, {"histbuff-ms", "30000"}, }); EXPECT_NO_THROW(validateNoIntrinParamsOnQualeIface( "pcloudLightAmbience", params)); } TEST(IntrinThresholdValidateIntrinSegmentParamsTest, RejectsUnitlessStems) { const ParamList params = makeParams({{"stupefaction", "5"}}); try { validateIntrinSegmentParams("postrin", params); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "invalid without a unit suffix"); } } TEST(IntrinThresholdValidateIntrinSegmentParamsTest, AllowsSuffixedNames) { const ParamList params = makeParams({ {"interest-pc", "85"}, {"distraction-thr", "12"}, }); EXPECT_NO_THROW(validateIntrinSegmentParams("negtrin", params)); } TEST(IntrinThresholdParseOptionalThresholdParamTest, ReturnsDefaultWhenAbsent) { const ParamList params = makeParams({{"other-param", "1"}}); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, /*defaultValue=*/7, /*defaultUnit=*/ThresholdUnit::Absolute); EXPECT_FALSE(parsed.wasSpecified); EXPECT_EQ(parsed.value, 7); EXPECT_EQ(parsed.unit, ThresholdUnit::Absolute); EXPECT_TRUE(parsed.matchedName.empty()); } TEST(IntrinThresholdParseOptionalThresholdParamTest, ParsesPercentageParam) { const ParamList params = makeParams({{"interest-percentage", "50"}}); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Absolute); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.value, 50); EXPECT_EQ(parsed.unit, ThresholdUnit::Percentage); EXPECT_EQ(parsed.matchedName, "interest-percentage"); } TEST(IntrinThresholdParseOptionalThresholdParamTest, ParsesAbsoluteParam) { const ParamList params = makeParams({{"interest-threshold", "42"}}); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Percentage); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.value, 42); EXPECT_EQ(parsed.unit, ThresholdUnit::Absolute); EXPECT_EQ(parsed.matchedName, "interest-threshold"); } TEST(IntrinThresholdParseOptionalThresholdParamTest, LattermostMatchingParamWins) { const ParamList params = makeParams({ {"interest-pc", "10"}, {"interest-threshold", "99"}, }); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Percentage); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.value, 99); EXPECT_EQ(parsed.unit, ThresholdUnit::Absolute); EXPECT_EQ(parsed.matchedName, "interest-threshold"); } TEST(IntrinThresholdParseOptionalThresholdParamTest, LattermostPercentageWinsOverEarlierAbsolute) { const ParamList params = makeParams({ {"interest-thr", "12"}, {"interest-pc", "75"}, }); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Absolute); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.value, 75); EXPECT_EQ(parsed.unit, ThresholdUnit::Percentage); EXPECT_EQ(parsed.matchedName, "interest-pc"); } TEST(IntrinThresholdParseOptionalThresholdParamTest, InvalidIntegerThrows) { const ParamList params = makeParams({{"interest-pc", "not-int"}}); try { parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Absolute); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "Failed to parse 'interest-pc'"); } } TEST(IntrinThresholdResolveThresholdValueTest, PercentageUsesBase) { const ParsedThresholdParam percentageParam = { .value = 50, .unit = ThresholdUnit::Percentage, .matchedName = "interest-pc", .wasSpecified = true, }; EXPECT_EQ(resolveThresholdValue(percentageParam, 84), 42u); } TEST(IntrinThresholdResolveThresholdValueTest, AbsolutePassesThrough) { const ParsedThresholdParam absoluteParam = { .value = 17, .unit = ThresholdUnit::Absolute, .matchedName = "interest-thr", .wasSpecified = true, }; EXPECT_EQ(resolveThresholdValue(absoluteParam, 84), 17u); } TEST(IntrinThresholdResolveThresholdValueTest, PercentageTruncatesTowardZero) { const ParsedThresholdParam percentageParam = { .value = 33, .unit = ThresholdUnit::Percentage, .matchedName = "interest-pc", .wasSpecified = true, }; EXPECT_EQ(resolveThresholdValue(percentageParam, 100), 33u); EXPECT_EQ(resolveThresholdValue(percentageParam, 10), 3u); } TEST(IntrinThresholdSynonymCoverageTest, AllThrSynonymsParseAsAbsolute) { const std::array thrNames = { "interest-threshold", "interest-thresh", "interest-thr", }; for (const std::string_view thrName : thrNames) { const ParamList params = makeParams({ {thrName.data(), "11"}, }); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Percentage); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.unit, ThresholdUnit::Absolute); EXPECT_EQ(parsed.value, 11); EXPECT_EQ(parsed.matchedName, thrName); } } TEST(IntrinThresholdSynonymCoverageTest, AllPcSynonymsParseAsPercentage) { const std::array pcNames = { "interest-percentage", "interest-pc", }; for (const std::string_view pcName : pcNames) { const ParamList params = makeParams({ {pcName.data(), "25"}, }); const ParsedThresholdParam parsed = parseOptionalThresholdParam( params, kIntrinInterestPcNames, kIntrinInterestThrNames, 0, ThresholdUnit::Absolute); EXPECT_TRUE(parsed.wasSpecified); EXPECT_EQ(parsed.unit, ThresholdUnit::Percentage); EXPECT_EQ(parsed.value, 25); EXPECT_EQ(parsed.matchedName, pcName); } } } // namespace } // namespace intrin } // namespace smo