diff --git a/include/model/CRuleCondition.h b/include/model/CRuleCondition.h index ff8af4ac57..af538c0420 100644 --- a/include/model/CRuleCondition.h +++ b/include/model/CRuleCondition.h @@ -43,7 +43,7 @@ class MODEL_EXPORT CRuleCondition { using TPatternSetCRef = boost::reference_wrapper; public: - enum ERuleConditionType { E_Categorical, E_NumericalActual, E_NumericalTypical, E_NumericalDiffAbs, E_Time }; + enum ERuleConditionType { E_CategoricalMatch, E_CategoricalComplement, E_NumericalActual, E_NumericalTypical, E_NumericalDiffAbs, E_Time }; enum EConditionOperator { E_LT, E_LTE, E_GT, E_GTE }; @@ -76,6 +76,8 @@ class MODEL_EXPORT CRuleCondition { void valueFilter(const core::CPatternSet& valueFilter); //! Is the condition categorical? + //! Categorical conditions are pattern match conditions i.e. + //! E_CategoricalMatch and E_CategoricalComplement bool isCategorical() const; //! Is the condition numerical? diff --git a/lib/api/CDetectionRulesJsonParser.cc b/lib/api/CDetectionRulesJsonParser.cc index 52277434f5..d6ee228f1d 100644 --- a/lib/api/CDetectionRulesJsonParser.cc +++ b/lib/api/CDetectionRulesJsonParser.cc @@ -32,6 +32,8 @@ const std::string TARGET_FIELD_NAME("target_field_name"); const std::string TARGET_FIELD_VALUE("target_field_value"); const std::string TYPE("type"); const std::string CATEGORICAL("categorical"); +const std::string CATEGORICAL_MATCH("categorical_match"); +const std::string CATEGORICAL_COMPLEMENT("categorical_complement"); const std::string NUMERICAL_ACTUAL("numerical_actual"); const std::string NUMERICAL_TYPICAL("numerical_typical"); const std::string NUMERICAL_DIFF_ABS("numerical_diff_abs"); @@ -237,8 +239,10 @@ bool CDetectionRulesJsonParser::parseRuleConditionType(const rapidjson::Value& r } const std::string& type = ruleConditionObject[TYPE.c_str()].GetString(); - if (type == CATEGORICAL) { - ruleCondition.type(model::CRuleCondition::E_Categorical); + if (type == CATEGORICAL_MATCH || type == CATEGORICAL){ + ruleCondition.type(model::CRuleCondition::E_CategoricalMatch); + } else if (type == CATEGORICAL_COMPLEMENT) { + ruleCondition.type(model::CRuleCondition::E_CategoricalComplement); } else if (type == NUMERICAL_ACTUAL) { ruleCondition.type(model::CRuleCondition::E_NumericalActual); } else if (type == NUMERICAL_TYPICAL) { diff --git a/lib/api/unittest/CDetectionRulesJsonParserTest.cc b/lib/api/unittest/CDetectionRulesJsonParserTest.cc index 62c4701040..205514b723 100644 --- a/lib/api/unittest/CDetectionRulesJsonParserTest.cc +++ b/lib/api/unittest/CDetectionRulesJsonParserTest.cc @@ -76,13 +76,19 @@ CppUnit::Test* CDetectionRulesJsonParserTest::suite() { new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenMultipleRules", &CDetectionRulesJsonParserTest::testParseRulesGivenMultipleRules)); suiteOfTests->addTest( - new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalRule", - &CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalRule)); + new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalMatchRule", + &CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalMatchRule)); + suiteOfTests->addTest( + new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalComplementRule", + &CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalComplementRule)); suiteOfTests->addTest(new CppUnit::TestCaller( "CDetectionRulesJsonParserTest::testParseRulesGivenTimeRule", &CDetectionRulesJsonParserTest::testParseRulesGivenTimeRule)); suiteOfTests->addTest( new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenDifferentActions", &CDetectionRulesJsonParserTest::testParseRulesGivenDifferentActions)); + suiteOfTests->addTest( + new CppUnit::TestCaller("CDetectionRulesJsonParserTest::testParseRulesGivenOldStyleCategoricalRule", + &CDetectionRulesJsonParserTest::testParseRulesGivenOldStyleCategoricalRule)); return suiteOfTests; } @@ -356,8 +362,37 @@ void CDetectionRulesJsonParserTest::testParseRulesGivenMultipleRules() { CPPUNIT_ASSERT_EQUAL(std::string("SKIP_SAMPLING (id:42) IF ACTUAL < 2.000000"), rules[1].print()); } -void CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalRule() { - LOG_DEBUG("*** testParseRulesGivenCategoricalRule ***"); +void CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalMatchRule() { + LOG_DEBUG("*** testParseRulesGivenCategoricalMatchRule ***"); + + TStrPatternSetUMap filtersById; + core::CPatternSet filter; + filter.initFromJson("[\"b\", \"a\"]"); + filtersById["filter1"] = filter; + + CDetectionRulesJsonParser parser(filtersById); + CDetectionRulesJsonParser::TDetectionRuleVec rules; + std::string rulesJson = "["; + rulesJson += "{"; + rulesJson += " \"actions\":[\"filter_results\"],"; + rulesJson += " \"conditions_connective\":\"or\","; + rulesJson += " \"conditions\": ["; + rulesJson += " {\"type\":\"categorical_match\", \"field_name\":\"foo\", \"filter_id\":\"filter1\"}"; + rulesJson += " ]"; + rulesJson += "}"; + rulesJson += "]"; + + CPPUNIT_ASSERT(parser.parseRules(rulesJson, rules)); + + CPPUNIT_ASSERT_EQUAL(std::size_t(1), rules.size()); + CPPUNIT_ASSERT_EQUAL(std::string("FILTER_RESULTS IF (foo) IN FILTER"), rules[0].print()); +} + +void CDetectionRulesJsonParserTest::testParseRulesGivenOldStyleCategoricalRule() { + LOG_DEBUG("*** testParseRulesGivenOldStyleCategoricalRule ***"); + + // Tests that the rule type can be parsed as categorical_match + // when the type is categorical TStrPatternSetUMap filtersById; core::CPatternSet filter; @@ -382,6 +417,32 @@ void CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalRule() { CPPUNIT_ASSERT_EQUAL(std::string("FILTER_RESULTS IF (foo) IN FILTER"), rules[0].print()); } +void CDetectionRulesJsonParserTest::testParseRulesGivenCategoricalComplementRule() { + LOG_DEBUG("*** testParseRulesGivenCategoricalComplementRule ***"); + + TStrPatternSetUMap filtersById; + core::CPatternSet filter; + filter.initFromJson("[\"b\", \"a\"]"); + filtersById["filter1"] = filter; + + CDetectionRulesJsonParser parser(filtersById); + CDetectionRulesJsonParser::TDetectionRuleVec rules; + std::string rulesJson = "["; + rulesJson += "{"; + rulesJson += " \"actions\":[\"filter_results\"],"; + rulesJson += " \"conditions_connective\":\"or\","; + rulesJson += " \"conditions\": ["; + rulesJson += " {\"type\":\"categorical_complement\", \"field_name\":\"foo\", \"filter_id\":\"filter1\"}"; + rulesJson += " ]"; + rulesJson += "}"; + rulesJson += "]"; + + CPPUNIT_ASSERT(parser.parseRules(rulesJson, rules)); + + CPPUNIT_ASSERT_EQUAL(std::size_t(1), rules.size()); + CPPUNIT_ASSERT_EQUAL(std::string("FILTER_RESULTS IF (foo) NOT IN FILTER"), rules[0].print()); +} + void CDetectionRulesJsonParserTest::testParseRulesGivenTimeRule() { LOG_DEBUG("*** testParseRulesGivenTimeRule ***"); diff --git a/lib/api/unittest/CDetectionRulesJsonParserTest.h b/lib/api/unittest/CDetectionRulesJsonParserTest.h index 0a3fee428f..f13f1a84b5 100644 --- a/lib/api/unittest/CDetectionRulesJsonParserTest.h +++ b/lib/api/unittest/CDetectionRulesJsonParserTest.h @@ -34,9 +34,11 @@ class CDetectionRulesJsonParserTest : public CppUnit::TestFixture { void testParseRulesGivenNumericalActualRuleWithConnectiveOr(); void testParseRulesGivenNumericalTypicalAndDiffAbsRuleWithConnectiveAnd(); void testParseRulesGivenMultipleRules(); - void testParseRulesGivenCategoricalRule(); + void testParseRulesGivenCategoricalMatchRule(); + void testParseRulesGivenCategoricalComplementRule(); void testParseRulesGivenTimeRule(); void testParseRulesGivenDifferentActions(); + void testParseRulesGivenOldStyleCategoricalRule(); static CppUnit::Test* suite(); }; diff --git a/lib/model/CRuleCondition.cc b/lib/model/CRuleCondition.cc index 4ae912ded6..fd4022a508 100644 --- a/lib/model/CRuleCondition.cc +++ b/lib/model/CRuleCondition.cc @@ -73,7 +73,7 @@ void CRuleCondition::valueFilter(const core::CPatternSet& valueFilter) { } bool CRuleCondition::isCategorical() const { - return m_Type == E_Categorical; + return m_Type == E_CategoricalMatch || m_Type == E_CategoricalComplement; } bool CRuleCondition::isNumerical() const { @@ -90,16 +90,19 @@ bool CRuleCondition::test(const CAnomalyDetectorModel& model, const CDataGatherer& gatherer = model.dataGatherer(); if (this->isCategorical()) { + bool containsValue{false}; if (m_FieldName == gatherer.partitionFieldName()) { - return m_ValueFilter.get().contains(gatherer.partitionFieldValue()); + containsValue = m_ValueFilter.get().contains(gatherer.partitionFieldValue()); } else if (m_FieldName == gatherer.personFieldName()) { - return m_ValueFilter.get().contains(gatherer.personName(pid)); + containsValue = m_ValueFilter.get().contains(gatherer.personName(pid)); } else if (m_FieldName == gatherer.attributeFieldName()) { - return m_ValueFilter.get().contains(gatherer.attributeName(cid)); + containsValue = m_ValueFilter.get().contains(gatherer.attributeName(cid)); } else { LOG_ERROR("Unexpected fieldName = " << m_FieldName); return false; } + + return (m_Type == E_CategoricalComplement) ? !containsValue : containsValue; } else { if (m_FieldValue.empty() == false) { if (isScoped) { @@ -138,7 +141,8 @@ bool CRuleCondition::checkCondition(const CAnomalyDetectorModel& model, core_t::TTime time) const { TDouble1Vec value; switch (m_Type) { - case E_Categorical: { + case E_CategoricalMatch: + case E_CategoricalComplement: { LOG_ERROR("Should never check numerical condition for categorical rule condition"); return false; } @@ -198,7 +202,11 @@ std::string CRuleCondition::print() const { result += ")"; } result += " "; + if (this->isCategorical()) { + if (m_Type == E_CategoricalComplement) { + result += "NOT "; + } result += "IN FILTER"; } else { result += this->print(m_Condition.s_Op) + " " + core::CStringUtils::typeToString(m_Condition.s_Threshold); @@ -208,7 +216,8 @@ std::string CRuleCondition::print() const { std::string CRuleCondition::print(ERuleConditionType type) const { switch (type) { - case E_Categorical: + case E_CategoricalMatch: + case E_CategoricalComplement: return ""; case E_NumericalActual: return "ACTUAL"; diff --git a/lib/model/unittest/CDetectionRuleTest.cc b/lib/model/unittest/CDetectionRuleTest.cc index a6ebf29969..0f14c1da5d 100644 --- a/lib/model/unittest/CDetectionRuleTest.cc +++ b/lib/model/unittest/CDetectionRuleTest.cc @@ -131,179 +131,186 @@ void CDetectionRuleTest::testApplyGivenCategoricalCondition() { model.mockAddBucketValue(model_t::E_PopulationMeanByPersonAndAttribute, 1, 2, 100, actual); model.mockAddBucketValue(model_t::E_PopulationMeanByPersonAndAttribute, 1, 3, 100, actual); + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"a1_1\",\"a2_2\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(attributeFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100)); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"a1*\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(attributeFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == - false); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) != isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"*2\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(attributeFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100)); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"*1*\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(attributeFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == - false); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) != isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"p2\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(personFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100)); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"par_1\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(partitionFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100)); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100)); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == isCategoricalMatch); } + + for (auto conditionType : {CRuleCondition::E_CategoricalMatch, CRuleCondition::E_CategoricalComplement}) { std::string filterJson("[\"par_2\"]"); core::CPatternSet valueFilter; valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(conditionType); condition.fieldName(partitionFieldName); condition.valueFilter(valueFilter); CDetectionRule rule; rule.addCondition(condition); + bool isCategoricalMatch = CRuleCondition::E_CategoricalMatch == conditionType; model_t::CResultType resultType(model_t::CResultType::E_Final); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) == - false); - CPPUNIT_ASSERT( - rule.apply(CDetectionRule::E_FilterResults, model, model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) == - false); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 0, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 0, 1, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 2, 100) != isCategoricalMatch); + CPPUNIT_ASSERT(rule.apply(CDetectionRule::E_FilterResults, model, + model_t::E_PopulationMeanByPersonAndAttribute, resultType, 1, 3, 100) != isCategoricalMatch); } } diff --git a/lib/model/unittest/CEventRatePopulationModelTest.cc b/lib/model/unittest/CEventRatePopulationModelTest.cc index 5cd6d3624f..1c3d72ba0e 100644 --- a/lib/model/unittest/CEventRatePopulationModelTest.cc +++ b/lib/model/unittest/CEventRatePopulationModelTest.cc @@ -1290,7 +1290,7 @@ void CEventRatePopulationModelTest::testIgnoreSamplingGivenDetectionRules() { valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(CRuleCondition::E_CategoricalMatch); condition.valueFilter(valueFilter); CDetectionRule rule; rule.action(CDetectionRule::E_SkipSampling); diff --git a/lib/model/unittest/CMetricPopulationModelTest.cc b/lib/model/unittest/CMetricPopulationModelTest.cc index 144e48c4ef..9019180657 100644 --- a/lib/model/unittest/CMetricPopulationModelTest.cc +++ b/lib/model/unittest/CMetricPopulationModelTest.cc @@ -1346,7 +1346,7 @@ void CMetricPopulationModelTest::testIgnoreSamplingGivenDetectionRules() { valueFilter.initFromJson(filterJson); CRuleCondition condition; - condition.type(CRuleCondition::E_Categorical); + condition.type(CRuleCondition::E_CategoricalMatch); condition.valueFilter(valueFilter); CDetectionRule rule; rule.action(CDetectionRule::E_SkipSampling);