-
Notifications
You must be signed in to change notification settings - Fork 19
Operations
Operations are defined in the OPERATION enum.
All Operations by default have access to some data:
-
state1
The first object state provided. -
state2
The second object state provided.
In addition some operations call ObjectToValues
which attempts to convert the targeted field of the object into string representations.
-
valsToCheck
aList<string>
-
dictToCheck
aList<KeyValuePair<string,string>>
In addition, many operations take argument data either in the form of:
- A
List<string>
calledData
- A
List<KeyValuePair<string,string>>
calledDictData
.
Performs regular expression matching using the provided Data.
Data
- List<strings>
of Regular Expressions to match
true
when any of the Regexes in Data match any of the valsToCheck
values extracted from the Field
A TypedClauseCapture<Match>
containing the Match
resulting from the regex call.
[TestMethod]
public void VerifyRegexOperator()
{
var falseRegexObject = "TestPathHere";
var trueRegexObject = "Directory/File";
var regexRule = new Rule("Regex Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.REGEX)
{
Data = new List<string>()
{
".+\\/.+"
}
}
}
};
var regexAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { regexRule }; ;
Assert.IsTrue(regexAnalyzer.Analyze(ruleList, trueRegexObject).Any());
Assert.IsFalse(regexAnalyzer.Analyze(ruleList, falseRegexObject).Any());
}
Simple equals operation.
Data
- List<string>
to check equality
true
when any of the strings in Data
are in valsToCheck
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings
public void VerifyEqOperator()
{
var assertTrueObject = new TestObject()
{
StringField = "Magic",
BoolField = true,
IntField = 700
};
var assertFalseObject = new TestObject()
{
StringField = "NotMagic",
BoolField = false,
IntField = 701
};
var stringEquals = new Rule("String Equals Rule")
{
Target = "TestObject",
Clauses = new List<Clause>()
{
new Clause(OPERATION.EQ, "StringField")
{
Data = new List<string>()
{
"Magic"
}
}
}
};
var analyzer = new Analyzer();
var ruleList = new List<Rule>() { stringEquals };
var trueObjectResults = analyzer.Analyze(ruleList, assertTrueObject);
var falseObjectResults = analyzer.Analyze(ruleList, assertFalseObject);
Assert.IsTrue(trueObjectResults.Any(x => x.Name == "Bool Equals Rule"));
Assert.IsTrue(trueObjectResults.Any(x => x.Name == "Int Equals Rule"));
Assert.IsTrue(trueObjectResults.Any(x => x.Name == "String Equals Rule"));
Assert.IsFalse(falseObjectResults.Any(x => x.Name == "String Equals Rule"));
}
Simple Not equals operation.
Data
- List<string>
to check equality
true
when none of the strings in Data
are in valsToCheck
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings
public void VerifyNeqOperator()
{
var assertFalseObject = new TestObject()
{
StringField = "Magic",
BoolField = true,
IntField = 700
};
var assertTrueObject = new TestObject()
{
StringField = "NotMagic",
BoolField = false,
IntField = 701
};
var intNotEquals = new Rule("Int Not Equals Rule")
{
Target = "TestObject",
Clauses = new List<Clause>()
{
new Clause(OPERATION.NEQ, "IntField")
{
Data = new List<string>()
{
"700"
}
}
}
};
var analyzer = new Analyzer();
var ruleList = new List<Rule>() { intNotEquals};
var trueObjectResults = analyzer.Analyze(ruleList, assertTrueObject);
var falseObjectResults = analyzer.Analyze(ruleList, assertFalseObject);
Assert.IsTrue(trueObjectResults.Any(x => x == intNotEquals));
Assert.IsFalse(falseObjectResults.Any(x => x == intNotEquals));
}
Checks if the ALL of the provided values are in the extracted values.
-
DictData
- AList<KVP<string,string>>
-
Data
- AList<string>
These rules are processed in this order, once a rule has matched its first clause here, it won't attempt to match the numbered rules. Each rule is true
if:
-
dictToCheck
is not empty, and all of the KVP inDictData
have a Key and Value match indictToCheck
- The Target is a
List<string>
, and all of the strings inData
are contained invalsToCheck
- The Target is a
string
,and all of the strings inData
are contained in the string - The Target is an
Enum
with the Flags attribute and all of the Flags specified inData
are set in the Enum
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings -
TypedClauseCapture<List<KeyValuePair<string,string>>>
when the captured target is key value pairs -
TypedClauseCapture<Enum>
when the captured target is an enum
public void VerifyContainsOperator()
{
var trueStringObject = new TestObject()
{
StringField = "ThisStringContainsMagic"
};
var falseStringObject = new TestObject()
{
StringField = "ThisStringDoesNot"
};
var stringContains = new Rule("String Contains Rule")
{
Target = "TestObject",
Clauses = new List<Clause>()
{
new Clause(OPERATION.CONTAINS, "StringField")
{
Data = new List<string>()
{
"Magic",
"String"
}
}
}
};
var stringAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { stringContains }; ;
Assert.IsTrue(stringAnalyzer.Analyze(ruleList, trueStringObject).Any());
Assert.IsFalse(stringAnalyzer.Analyze(ruleList, falseStringObject).Any());
}
Checks if the ANY of the provided values are in the extracted values.
-
DictData
- AList<KVP<string,string>>
-
Data
- AList<string>
These rules are processed in this order, once a rule has matched its first clause here, it won't attempt to match the numbered rules. Each rule is true
if:
-
dictToCheck
is not empty, and any of the KVP inDictData
have a Key and Value match indictToCheck
- The Target is a
List<string>
, and any of the strings inData
are contained invalsToCheck
- The Target is a
string
,and any of the strings inData
are contained in the string - The Target is an
Enum
with the Flags attribute and any of the Flags specified inData
are set in the Enum
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings -
TypedClauseCapture<List<KeyValuePair<string,string>>>
when the captured target is key value pairs -
TypedClauseCapture<Enum>
when the captured target is an enum
public void VerifyContainsAnyOperator()
{
var enumFlagsContains = new Rule("Enum Flags Contains Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.CONTAINS_ANY)
{
Data = new List<string>(){"Magic", "Normal"}
}
}
};
var enumAnalyzer = new Analyzer();
ruleList = new List<Rule>() { enumFlagsContains };
Assert.IsTrue(enumAnalyzer.Analyze(ruleList, Words.Magic).Any());
Assert.IsTrue(enumAnalyzer.Analyze(ruleList, Words.Normal).Any());
Assert.IsTrue(enumAnalyzer.Analyze(ruleList, Words.Magic | Words.Normal).Any());
Assert.IsFalse(enumAnalyzer.Analyze(ruleList, Words.Shazam).Any());
}
Checks if any of the provided ints are Greater Than the extracted values
Data
- A Lists representing ints
true
when any of the Data
when parsed as ints is greater than any of the values in valsToCheck
parsed as ints.
TypedClauseCapture<int>
the captured int
public void VerifyGtOperator()
{
var gtRule = new Rule("Gt Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.GT)
{
Data = new List<string>()
{
"9000"
}
}
}
};
var gtAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { gtRule }; ;
Assert.IsTrue(gtAnalyzer.Analyze(ruleList, 500).Any());
Assert.IsFalse(gtAnalyzer.Analyze(ruleList, 9001).Any());
}
Checks if any of the provided ints are Less Than the extracted values
Data
- A List representing ints
true
when any of the Data
when parsed as ints is less than any of the values in valsToCheck
parsed as ints.
TypedClauseCapture<int>
the captured int
public void VerifyLtOperator()
{
var ltRule = new Rule("Lt Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.LT)
{
Data = new List<string>()
{
"20000"
}
}
}
};
var ltAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { ltRule }; ;
Assert.IsTrue(ltAnalyzer.Analyze(ruleList, 10).Any());
Assert.IsFalse(ltAnalyzer.Analyze(ruleList, 30000).Any());
}
Checks if the two object states provided differ
No Arguments
true
if the two object states appear to be different.
TypedClauseCapture<ComparisonResult>
whose Result is a KellermanSoftware.CompareNetObjects.ComparisonResult
, the result of the comparison between the two states.
public void VerifyWasModifiedOperator()
{
var firstObject = new TestObject()
{
StringDictField = new Dictionary<string, string>() { { "Magic Word", "Please" }, { "Another Key", "Another Value" } }
};
var secondObject = new TestObject()
{
StringDictField = new Dictionary<string, string>() { { "Magic Word", "Abra Kadabra" }, { "Another Key", "A Different Value" } }
};
var wasModifiedRule = new Rule("Was Modified Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.WAS_MODIFIED)
}
};
var analyzer = new Analyzer();
var ruleList = new List<Rule>() { wasModifiedRule }; ;
Assert.IsTrue(analyzer.Analyze(ruleList, true, false).Any());
Assert.IsTrue(analyzer.Analyze(ruleList, "A String", "Another string").Any());
Assert.IsTrue(analyzer.Analyze(ruleList, 3, 4).Any());
Assert.IsTrue(analyzer.Analyze(ruleList, new List<string>() { "One", "Two" }, new List<string>() { "Three", "Four" }).Any());
Assert.IsTrue(analyzer.Analyze(ruleList, firstObject, secondObject).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, true, true).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, "A String", "A String").Any());
Assert.IsFalse(analyzer.Analyze(ruleList, 3, 3).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, new List<string>() { "One", "Two" }, new List<string>() { "One", "Two" }).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, firstObject, firstObject).Any());
}
Checks if any of the strings extracted end with the provided data.
Data
- A List<string>
of potential endings
true
if any of the strings in valsToCheck
end with any of the strings in Data
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings
public void VerifyEndsWithOperator()
{
var trueEndsWithObject = "ThisStringEndsWithMagic";
var falseEndsWithObject = "ThisStringHasMagicButDoesn't";
var endsWithRule = new Rule("Ends With Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.ENDS_WITH)
{
Data = new List<string>()
{
"Magic"
}
}
}
};
var endsWithAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { endsWithRule }; ;
Assert.IsTrue(endsWithAnalyzer.Analyze(ruleList, trueEndsWithObject).Any());
Assert.IsFalse(endsWithAnalyzer.Analyze(ruleList, falseEndsWithObject).Any());
}
Checks if any of the strings extracted start with the provided data.
Data
- A List<string>
of potential starts
true
if any of the strings in valsToCheck
start with any of the strings in Data
-
TypedClauseCapture<string>
when the captured target is a single string -
TypedClauseCapture<List<string>>
when the captured target is a list of strings
public void VerifyStartsWithOperator()
{
var trueEndsWithObject = "MagicStartsThisStringOff";
var falseEndsWithObject = "ThisStringHasMagicButLater";
var startsWithRule = new Rule("Starts With Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.STARTS_WITH)
{
Data = new List<string>()
{
"Magic"
}
}
}
};
var analyzer = new Analyzer();
var ruleList = new List<Rule>() { startsWithRule }; ;
Assert.IsTrue(analyzer.Analyze(ruleList, trueEndsWithObject).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, falseEndsWithObject).Any());
}
Checks if both object states are null
No Arguments
true
if both object states are null
TypedClauseCapture<object?>
where Result is null.
public void VerifyIsNullOperator()
{
var isNullRule = new Rule("Is Null Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.IS_NULL)
}
};
var isNullAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { isNullRule }; ;
Assert.IsTrue(isNullAnalyzer.Analyze(ruleList, null).Any());
Assert.IsFalse(isNullAnalyzer.Analyze(ruleList, "Not Null").Any());
}
Checks if the object state is a true bool
No Arguments
true
if either object state is true
TypedClauseCapture<bool>
the target bool
public void VerifyIsTrueOperator()
{
var isTrueRule = new Rule("Is True Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.IS_TRUE)
}
};
var isTrueAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { isTrueRule }; ;
Assert.IsTrue(isTrueAnalyzer.Analyze(ruleList, true).Any());
Assert.IsFalse(isTrueAnalyzer.Analyze(ruleList, false).Any());
}
Checks if the object state is a DateTime and is before any of the times specified in Data
Data
- A List of DateTime.Parse compatible DateTime strings
true
if either object state is a date time and any of the provided DateTimes in Data
are after it
TypedClauseCapture<DateTime>
the targeted time
public void VerifyIsBeforeOperator()
{
var falseIsBeforeObject = DateTime.MaxValue;
var falseIsAfterObject = DateTime.MinValue;
var isBeforeRule = new Rule("Is Before Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.IS_BEFORE)
{
Data = new List<string>()
{
DateTime.Now.ToString()
}
}
}
};
var analyzer = new Analyzer();
var ruleList = new List<Rule>() { isBeforeRule }; ;
Assert.IsTrue(analyzer.Analyze(ruleList, falseIsAfterObject).Any());
Assert.IsFalse(analyzer.Analyze(ruleList, falseIsBeforeObject).Any());
}
Checks if the object state is a DateTime and is after any of the times specified in Data
Data
- A List of DateTime.Parse compatible DateTime strings
-
true
if either object state is a date time and any of the provided DateTimes inData
are before it
TypedClauseCapture<DateTime>
the targeted time
public void VerifyIsAfterOperator()
{
var falseIsAfterObject = DateTime.MinValue;
var trueIsAfterObject = DateTime.MaxValue;
var isAfterRule = new Rule("Is After Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.IS_AFTER)
{
Data = new List<string>()
{
DateTime.Now.ToString()
}
}
}
};
var isAfterAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { isAfterRule }; ;
Assert.IsTrue(isAfterAnalyzer.Analyze(ruleList, trueIsAfterObject).Any());
Assert.IsFalse(isAfterAnalyzer.Analyze(ruleList, falseIsAfterObject).Any());
}
Checks if the object state is a DateTime and is after DateTime.Now when executed
No Arguments
-
true
if either object state is a date time and after DateTime.Now
TypedClauseCapture<DateTime>
the targeted time
public void VerifyIsExpiredOperation()
{
var falseIsExpiredObject = DateTime.MaxValue;
var trueIsExpiredObject = DateTime.MinValue;
var isExpiredRule = new Rule("Is Expired Rule")
{
Clauses = new List<Clause>()
{
new Clause(OPERATION.IS_EXPIRED)
}
};
var isAfterAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { isExpiredRule }; ;
Assert.IsTrue(isAfterAnalyzer.Analyze(ruleList, trueIsExpiredObject).Any());
Assert.IsFalse(isAfterAnalyzer.Analyze(ruleList, falseIsExpiredObject).Any());
}
If dictionary values were extracted if any key in that dictionary matches the provided Data
-
Data
- A List of dictionary keys to check
-
true
if any of theData
strings are present indictToCheck.Keys
-
TypedClauseCapture<List<string>>
of the found keys
public void VerifyContainsKeyOperator()
{
var trueAlgDict = new TestObject()
{
StringDictField = new Dictionary<string, string>()
{
{ "Magic", "Anything" }
}
};
var falseAlgDict = new TestObject()
{
StringDictField = new Dictionary<string, string>()
{
{ "No Magic", "Anything" }
}
};
var algDictContains = new Rule("Alg Dict Changed PCR 1")
{
Target = "TestObject",
Clauses = new List<Clause>()
{
new Clause(OPERATION.CONTAINS_KEY, "StringDictField")
{
Data = new List<string>()
{
"Magic"
}
}
}
};
var algDictAnalyzer = new Analyzer();
var ruleList = new List<Rule>() { algDictContains }; ;
Assert.IsTrue(algDictAnalyzer.Analyze(ruleList, trueAlgDict).Any());
Assert.IsFalse(algDictAnalyzer.Analyze(ruleList, falseAlgDict).Any());
}
Indicates a custom operation
-
Data
- A List -
DictData
- A List<KeyValuePair<string,string>> -
CustomOperation
- A string identifying the custom operation
true
if:
- A matching custom operation delegate is found
- That delegate returns true.
- Anything that inherits from
ClauseCapture
.
# First we have to set up our Operation's Delegate
public (bool Applies, bool Result) OverweightOperationDelegate(Clause clause,
IEnumerable<string>? valsToCheck, IEnumerable<KeyValuePair<string, string>> dictToCheck, object? state1, object? state2)
{
if (clause.CustomOperation == "OVERWEIGHT")
{
if (state1 is Vehicle vehicle)
{
if (vehicle.Weight > vehicle.Capacity)
{
return (true, true);
}
}
return (true, false);
}
return (false, false);
}
# And the Delegate to ensure we can validate rules with our new operation
public (bool Applies, IEnumerable<Violation> Violations) OverweightOperationValidationDelegate(Rule r, Clause c)
{
if (c.CustomOperation == "OVERWEIGHT")
{
var violations = new List<Violation>();
if (r.Target != "Vehicle")
{
violations.Add(new Violation("Overweight operation requires a Vehicle object", r, c));
}
if (c.Data != null || c.DictData != null)
{
violations.Add(new Violation("Overweight operation takes no data.", r, c));
}
return (true, violations);
}
else
{
return (false, Array.Empty<Violation>());
}
}
var overweightTruck = new Vehicle()
{
Weight = 30000,
Capacity = 20000,
};
var rules = new Rule[] {
new Rule("Overweight")
{
Target = "Vehicle",
Clauses = new List<Clause>()
{
new Clause(OPERATION.CUSTOM)
{
CustomOperation = "OVERWEIGHT"
}
}
},
};
var analyzer = new Analyzer();
analyzer.CustomOperationDelegates.Add(OverweightOperationDelegate);
analyzer.CustomOperationValidationDelegates.Add(OverweightOperationValidationDelegate);
var issues = analyzer.EnumerateRuleIssues(rules).ToList();
Assert.IsFalse(issues.Any());
Assert.IsTrue(analyzer.Analyze(rules,overweightTruck).Any());