-
Notifications
You must be signed in to change notification settings - Fork 18
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. When those operations have done so they will reference the values extracted:
-
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
.
Finally, the Regex Operation and Custom operations may take arguments
- A
List<string>
calledArguments
Performs regular expression matching using the provided Data.
-
Data
-List<string>
of Regular Expressions to match -
Arguments
-List<string>
of regex options.
-
i
setsRegexOptions.IgnoreCase
-
m
setsRegexOptions.MultiLine
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 == "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
CaptureClause
where the object states represent the object states passed to the Clause for operating.
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
- The Label matching a Custom Operation Delegate you've set
true
if
- A custom operation delegate with a matching
CustomOperation
label is found. a. That delegate returns true.
- May return a capture as anything that inherits from
ClauseCapture
.
You can create a Delegate for custom operations. From the Walkthrough we have the overweight operation, which looks like this:
public OperationResult OverweightOperationDelegate(Clause clause, object? state1, object? state2, IEnumerable<ClauseCapture>? captures)
{
if (state1 is Vehicle vehicle)
{
var res = vehicle.Weight > vehicle.Capacity;
if ((res && !clause.Invert) || (clause.Invert && !res))
{
// The rule applies and is true and the capture is available if capture is enabled
return new OperationResult(true, clause.Capture ? new TypedClauseCapture<int>(clause, vehicle.Weight, state1, state2) : null);
}
}
return new OperationResult(false, null);
}
The Overweight rule also has a validation delegate
public IEnumerable<Violation> OverweightOperationValidationDelegate(Rule r, Clause c)
{
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 violations;
}
Now we can instantiate our operation
var analyzer = new Analyzer();
OatOperation OverweightOperation = new OatOperation(Operation.Custom, analyzer)
{
CustomOperation = "OVERWEIGHT",
OperationDelegate = OverweightOperationDelegate,
ValidationDelegate = OverweightOperationValidationDelegate
};
analyzer.SetOperation(OverWeightOperation);
With our operation set we can test it.
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 issues = analyzer.EnumerateRuleIssues(rules).ToList();
Assert.IsFalse(issues.Any());
Assert.IsTrue(analyzer.Analyze(rules,overweightTruck).Any());
Indicates an Operation with a provided script
-
Script
- A ScriptData object containing a Script to execute -
Data
- A List -
DictData
- A List<KeyValuePair<string,string>>
true
if
- The provided
ScriptData
compiles - The
Script
returnstrue
when passed the Data and States.
- May return a capture as anything that inherits from
ClauseCapture
.
Instead of using a Delegate you can provide a Script inline to run.
var overweightTruck = new Vehicle()
{
Weight = 30000,
Capacity = 20000,
};
new Rule("Overweight Script")
{
Expression = "Overweight",
Target = "Vehicle",
Clauses = new List<Clause>()
{
new Clause(Operation.Script)
{
Label = "Overweight",
Script = new ScriptData(code: @"
if (State1 is Vehicle vehicle)
{
var res = vehicle.Weight > vehicle.Capacity;
if ((res && !Clause.Invert) || (Clause.Invert && !res))
{
// The rule applies and is true and the capture is available if capture is enabled
return new OperationResult(true, Clause.Capture ? new TypedClauseCapture<int>(Clause, vehicle.Weight, State1, State2) : null);
}
}
return new OperationResult(false, null);",
imports: new List<string>() {"System", "Microsoft.CST.OAT.VehicleDemo"},
references: new List<string>(){ "VehicleDemo" }),
Capture = true
}
}
};
var analyzer = new Analyzer(new AnalyzerOptions(true));
var ruleIssues = analyzer.EnumerateRuleIssues(rule);
Assert.IsFalse(ruleIssues.Any());
Assert.IsTrue(analyzer.Analyze(new Rule[]{ rule },overweightTruck).Any());