Skip to content

Commit

Permalink
Merge pull request #24 from NatVanG/drillthroughjsonpointer
Browse files Browse the repository at this point in the history
Drillthrough jsonpointer in rules data mapping part.
NatVanG authored Nov 19, 2023
2 parents a6caee9 + cf63b62 commit d6dd501
Showing 11 changed files with 941 additions and 137 deletions.
86 changes: 86 additions & 0 deletions PBIXInspectorLibrary/CustomRules/SetDifferenceRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Json.Logic;
using Json.More;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;


namespace PBIXInspectorLibrary.CustomRules;

/// <summary>
/// Handles the `diff` operation.
/// </summary>
[Operator("diff")]
[JsonConverter(typeof(SetDifferenceRuleJsonConverter))]
public class SetDifferenceRule : Json.Logic.Rule
{
internal Json.Logic.Rule Set1 { get; }
internal Json.Logic.Rule Set2 { get; }

public SetDifferenceRule(Json.Logic.Rule set1, Json.Logic.Rule set2)
{
Set1 = set1;
Set2 = set2;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var set1 = Set1.Apply(data, contextData);
var set2 = Set2.Apply(data, contextData);

if (set1 is not JsonArray || set2 is not JsonArray)
return new JsonArray();

var arr1 = (JsonArray)set1;
var arr2 = (JsonArray)set2;

var difference = new JsonArray();

foreach (var item in arr1)
{
if (!arr2.Any(x => item.IsEquivalentTo(x)))
{
var copy = item.Copy();
if (!difference.Any(x => x.IsEquivalentTo(copy)))
{
difference.Add(copy);
}
}
}

return difference;
}
}

internal class SetDifferenceRuleJsonConverter : JsonConverter<SetDifferenceRule>
{
public override SetDifferenceRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Json.Logic.Rule[]>(ref reader, options);

if (parameters is not { Length: 2 })
throw new JsonException("The difference rule needs an array with 2 parameters.");

return new SetDifferenceRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, SetDifferenceRule value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("difference");
writer.WriteStartArray();
writer.WriteRule(value.Set1, options);
writer.WriteRule(value.Set2, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
90 changes: 90 additions & 0 deletions PBIXInspectorLibrary/CustomRules/SetEqualRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Json.Logic;
using Json.More;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;


namespace PBIXInspectorLibrary.CustomRules;

/// <summary>
/// Handles the `equalsets` operation.
/// </summary>
[Operator("equalsets")]
[JsonConverter(typeof(SetEqualRuleJsonConverter))]
public class SetEqualRule : Json.Logic.Rule
{
internal Json.Logic.Rule Set1 { get; }
internal Json.Logic.Rule Set2 { get; }

public SetEqualRule(Json.Logic.Rule set1, Json.Logic.Rule set2)
{
Set1 = set1;
Set2 = set2;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var set1 = Set1.Apply(data, contextData);
var set2 = Set2.Apply(data, contextData);

if (set1 is not JsonArray || set2 is not JsonArray)
return new JsonArray();

var arr1 = (JsonArray)set1;
var arr2 = (JsonArray)set2;

var symmetricDifference = new JsonArray();

foreach (var item in arr1)
{
if (!arr2.Any(x => item.IsEquivalentTo(x)))
{
return false;
}
}

foreach (var item in arr2)
{
if (!arr1.Any(x => item.IsEquivalentTo(x)))
{
return false;
}
}

return true;
}
}

internal class SetEqualRuleJsonConverter : JsonConverter<SetEqualRule>
{
public override SetEqualRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Json.Logic.Rule[]>(ref reader, options);

if (parameters is not { Length: 2 })
throw new JsonException("The symdiff rule needs an array with 2 parameters.");

return new SetEqualRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, SetEqualRule value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("symdiff");
writer.WriteStartArray();
writer.WriteRule(value.Set1, options);
writer.WriteRule(value.Set2, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
86 changes: 86 additions & 0 deletions PBIXInspectorLibrary/CustomRules/SetIntersectionRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Json.Logic;
using Json.More;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;


namespace PBIXInspectorLibrary.CustomRules;

/// <summary>
/// Handles the `intersection` operation.
/// </summary>
[Operator("intersection")]
[JsonConverter(typeof(SetIntersectionRuleJsonConverter))]
public class SetIntersectionRule : Json.Logic.Rule
{
internal Json.Logic.Rule Set1 { get; }
internal Json.Logic.Rule Set2 { get; }

public SetIntersectionRule(Json.Logic.Rule set1, Json.Logic.Rule set2)
{
Set1 = set1;
Set2 = set2;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var set1 = Set1.Apply(data, contextData);
var set2 = Set2.Apply(data, contextData);

if (set1 is not JsonArray || set2 is not JsonArray)
return new JsonArray();

var arr1 = (JsonArray)set1;
var arr2 = (JsonArray)set2;

var intersection = new JsonArray();

foreach (var item in arr1)
{
if (arr2.Any(x => item.IsEquivalentTo(x)))
{
var copy = item.Copy();
if (!intersection.Any(x => x.IsEquivalentTo(copy)))
{
intersection.Add(copy);
}
}
}

return intersection;
}
}

internal class SetIntersectionRuleJsonConverter : JsonConverter<SetIntersectionRule>
{
public override SetIntersectionRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Json.Logic.Rule[]>(ref reader, options);

if (parameters is not { Length: 2 })
throw new JsonException("The intersection rule needs an array with 2 parameters.");

return new SetIntersectionRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, SetIntersectionRule value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("intersection");
writer.WriteStartArray();
writer.WriteRule(value.Set1, options);
writer.WriteRule(value.Set2, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
98 changes: 98 additions & 0 deletions PBIXInspectorLibrary/CustomRules/SetSymmetricDifferenceRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using Json.Logic;
using Json.More;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;


namespace PBIXInspectorLibrary.CustomRules;

/// <summary>
/// Handles the `symdiff` operation.
/// </summary>
[Operator("symdiff")]
[JsonConverter(typeof(SetSymmetricDifferenceRuleJsonConverter))]
public class SetSymmetricDifferenceRule : Json.Logic.Rule
{
internal Json.Logic.Rule Set1 { get; }
internal Json.Logic.Rule Set2 { get; }

public SetSymmetricDifferenceRule(Json.Logic.Rule set1, Json.Logic.Rule set2)
{
Set1 = set1;
Set2 = set2;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var set1 = Set1.Apply(data, contextData);
var set2 = Set2.Apply(data, contextData);

if (set1 is not JsonArray || set2 is not JsonArray)
return new JsonArray();

var arr1 = (JsonArray)set1;
var arr2 = (JsonArray)set2;

var symmetricDifference = new JsonArray();

foreach (var item in arr1)
{
if (!arr2.Any(x => item.IsEquivalentTo(x)))
{
var copy = item.Copy();
if (!symmetricDifference.Any(x => x.IsEquivalentTo(copy)))
{
symmetricDifference.Add(copy);
}
}
}

foreach (var item in arr2)
{
if (!arr1.Any(x => item.IsEquivalentTo(x)))
{
var copy = item.Copy();
if (!symmetricDifference.Any(x => x.IsEquivalentTo(copy)))
{
symmetricDifference.Add(copy);
}
}
}

return symmetricDifference;
}
}

internal class SetSymmetricDifferenceRuleJsonConverter : JsonConverter<SetSymmetricDifferenceRule>
{
public override SetSymmetricDifferenceRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Json.Logic.Rule[]>(ref reader, options);

if (parameters is not { Length: 2 })
throw new JsonException("The symdiff rule needs an array with 2 parameters.");

return new SetSymmetricDifferenceRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, SetSymmetricDifferenceRule value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("symdiff");
writer.WriteStartArray();
writer.WriteRule(value.Set1, options);
writer.WriteRule(value.Set2, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
92 changes: 92 additions & 0 deletions PBIXInspectorLibrary/CustomRules/SetUnionRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Json.Logic;
using Json.More;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;


namespace PBIXInspectorLibrary.CustomRules;

/// <summary>
/// Handles the `union` operation.
/// </summary>
[Operator("union")]
[JsonConverter(typeof(SetUnionRuleJsonConverter))]
public class SetUnionRule : Json.Logic.Rule
{
internal Json.Logic.Rule Set1 { get; }
internal Json.Logic.Rule Set2 { get; }

public SetUnionRule(Json.Logic.Rule set1, Json.Logic.Rule set2)
{
Set1 = set1;
Set2 = set2;
}

/// <summary>
/// Applies the rule to the input data.
/// </summary>
/// <param name="data">The input data.</param>
/// <param name="contextData">
/// Optional secondary data. Used by a few operators to pass a secondary
/// data context to inner operators.
/// </param>
/// <returns>The result of the rule.</returns>
public override JsonNode? Apply(JsonNode? data, JsonNode? contextData = null)
{
var set1 = Set1.Apply(data, contextData);
var set2 = Set2.Apply(data, contextData);

if (set1 is not JsonArray || set2 is not JsonArray)
return new JsonArray();

var arr1 = (JsonArray)set1;
var arr2 = (JsonArray)set2;

var union = new JsonArray();

foreach (var item in arr1)
{
var copy = item.Copy();
if (!union.Any(x => x.IsEquivalentTo(copy)))
{
union.Add(copy);
}
}

foreach (var item in arr2)
{
var copy = item.Copy();
if (!union.Any(x => x.IsEquivalentTo(copy)))
{
union.Add(copy);
}
}

return union;
}
}

internal class SetUnionRuleJsonConverter : JsonConverter<SetUnionRule>
{
public override SetUnionRule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var parameters = JsonSerializer.Deserialize<Json.Logic.Rule[]>(ref reader, options);

if (parameters is not { Length: 2 })
throw new JsonException("The union rule needs an array with 2 parameters.");

return new SetUnionRule(parameters[0], parameters[1]);
}

public override void Write(Utf8JsonWriter writer, SetUnionRule value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("union");
writer.WriteStartArray();
writer.WriteRule(value.Set1, options);
writer.WriteRule(value.Set2, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
158 changes: 62 additions & 96 deletions PBIXInspectorLibrary/Inspector.cs
Original file line number Diff line number Diff line change
@@ -19,9 +19,9 @@ public class Inspector : InspectorBase
private const string FILTEREXPRESSIONMARKER = "?";
private const string JSONPOINTERSTART = "/";
private const string CONTEXTARRAY = ".";
internal const char DRILLCHAR = '>';

private string _pbiFilePath, _rulesFilePath;
//private PbiFile _pbiFile;
private InspectionRules? _inspectionRules;

public event EventHandler<MessageIssuedEventArgs>? MessageIssued;
@@ -75,12 +75,6 @@ public Inspector(string pbiFilePath, string rulesFilePath) : base(pbiFilePath, r

private PbiFile InitPbiFile(string pbiFilePath)
{
//if (!File.Exists(pbiFilePath))
//{
// throw new PBIXInspectorException(string.Format("The PBI file with path \"{0}\" does not exist.", pbiFilePath));
//}
//else
//{
switch (PbiFile.PBIFileType(pbiFilePath))
{
case PbiFile.PBIFileTypeEnum.PBIX:
@@ -94,7 +88,6 @@ private PbiFile InitPbiFile(string pbiFilePath)
default:
throw new PBIXInspectorException(string.Format("Could not determine the extension of PBI file with path \"{0}\".", pbiFilePath));
}
//}
}

private void AddCustomRulesToRegistry()
@@ -117,7 +110,11 @@ private void AddCustomRulesToRegistry()
Json.Logic.RuleRegistry.AddRule<CustomRules.ToRecordRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.DrillVariableRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.RectOverlapRule>();

Json.Logic.RuleRegistry.AddRule<CustomRules.SetIntersectionRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.SetUnionRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.SetDifferenceRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.SetSymmetricDifferenceRule>();
Json.Logic.RuleRegistry.AddRule<CustomRules.SetEqualRule>();
}

/// <summary>
@@ -219,7 +216,9 @@ public IEnumerable<TestResult> Inspect()

bool result = false;

var newdata = MapRuleDataPointersToValues(contextNodeArray, rule, contextNodeArray);
//HACK: checking if the rule's intention is to return an array or a single object
var node = rule.Path.Contains("*") || rule.Path.Contains("?") ? contextNodeArray : (contextNodeArray != null ? contextNodeArray.FirstOrDefault() : null);
var newdata = MapRuleDataPointersToValues(node, rule, contextNodeArray);

//TODO: the following commented line does not work with the variableRule implementation with context array passed in.
//var jruleresult = jrule.Apply(newdata, contextNodeArray);
@@ -398,81 +397,6 @@ private JsonArray ConvertToJsonArray(List<JToken>? tokens)
return tokens;
}

/*
private JToken? ExecuteTokenPath(JToken jo, string ruleName, string rulePath, bool rulePathErrorWhenNoMatch)
{
string parentPath, queryPath = string.Empty;
JToken? token = new JToken();
//TODO: a regex match to extract the substring would be better
if (rulePath.Contains(SUBJPATHSTART)) //check for subpath syntax
{
if (rulePath.EndsWith(SUBJPATHEND))
{
//TODO: currently subpath is assumed to be the last path (i.e. the whole string end in "})" but we should be able to resolve inner subpath and return to parent path
var index = rulePath.IndexOf(SUBJPATHSTART);
parentPath = rulePath.Substring(0, index);
queryPath = rulePath.Substring(index + SUBJPATHSTART.Length, rulePath.Length - (index + SUBJPATHSTART.Length) - 1);
var parentTokens = SelectTokens(jo, ruleName, parentPath, rulePathErrorWhenNoMatch);
if (parentTokens == null || parentTokens.Count() == 0) { return token; }
if (parentPath.Contains(FILTEREXPRESSIONMARKER))
{
JArray ja = new JArray();
foreach (var t in parentTokens)
{
//HACK: why do I have to parse a token into a token to make the subsequent SelectTokens call work?
var jt = JToken.Parse(t.ToString());
ja.Add(jt);
}
token = ja.SelectTokens(queryPath, rulePathErrorWhenNoMatch);
}
else
{
foreach (var t in parentTokens)
{
//var childtokens = SelectTokens((JObject?)t, rule.Name, childPath, rule.PathErrorWhenNoMatch); //TODO: this seems better but throws InvalidCastException
var childtokens = SelectTokens(((JObject)JToken.Parse(t.ToString())), ruleName, queryPath, rulePathErrorWhenNoMatch);
//only return children tokens, the reference to parent tokens is lost.
if (childtokens != null) tokens.AddRange(childtokens.ToList());
}
}
}
else
{
throw new PBIXInspectorException(string.Format("Path \"{0}\" needs to end with \"{1}\" as it contains a subpath.", rulePath, "}"));
}
}
else
{
tokens = SelectTokens(jo, ruleName, rulePath, rulePathErrorWhenNoMatch)?.ToList();
}
return tokens;
}
*/

/*
private IEnumerable<JToken>? SelectTokens(JObject? jo, string ruleName, string rulePath, bool rulePathErrorWhenNoMatch)
{
IEnumerable<JToken>? tokens;
//TODO: for some reason I can't catch Newtonsoft.Json.JsonException when rule.PathErrorWhenNoMatch is true
tokens = jo.SelectTokens(rulePath, false);
if (tokens == null || tokens.Count() == 0)
{
var msgType = rulePathErrorWhenNoMatch ? MessageTypeEnum.Error : MessageTypeEnum.Information;
OnMessageIssued(msgType, string.Format("Rule \"{0}\" with JPath \"{1}\" did not return any tokens.", ruleName, rulePath));
}
return tokens;
}
*/

private IEnumerable<JToken>? SelectTokens(JToken? jo, string ruleName, string rulePath, bool rulePathErrorWhenNoMatch)
{
IEnumerable<JToken>? tokens;
@@ -514,21 +438,14 @@ private JsonArray ConvertToJsonArray(List<JToken>? tokens)
{
if (item.Value is JsonValue)
{

var value = item.Value.AsValue().Stringify();
//TODO: enable navigation to parent path
//while (value.StartsWith("."))
//{
// target = target.Parent;
// value = value.Substring(1, value.Length - 1);
//}

if (value.StartsWith(JSONPOINTERSTART)) //check for JsonPointer syntax
{
try
{
var pointer = JsonPointer.Parse(value);
var evalsuccess = pointer.TryEvaluate(target, out var newval);
//var pointer = JsonPointer.Parse(value);
//var evalsuccess = pointer.TryEvaluate(target, out var newval);
var evalsuccess = EvalPath(value, target, out var newval);
if (evalsuccess)
{
if (newval != null)
@@ -566,7 +483,7 @@ private JsonArray ConvertToJsonArray(List<JToken>? tokens)
}
}
}
else if (value.StartsWith(CONTEXTARRAY))
else if (value.Equals(CONTEXTARRAY))
{
//context array token was used so pass in the parent array
newdata.Add(new KeyValuePair<string, JsonNode?>(item.Key, contextNodeArray.Copy()));
@@ -595,6 +512,55 @@ private JsonArray ConvertToJsonArray(List<JToken>? tokens)
return newdata;
}

internal bool EvalPath(string pathString, JsonNode? data, out JsonNode? result)
{
if (pathString.Contains(DRILLCHAR))
{
var leftString = pathString.Substring(0, pathString.IndexOf(DRILLCHAR));
var rightString = pathString.Substring(pathString.IndexOf(DRILLCHAR) + 1);

var leftStringPath = string.Concat(leftString.StartsWith(JSONPOINTERSTART) ? string.Empty : JSONPOINTERSTART, leftString.Replace('.', '/'));
var pointer = JsonPointer.Parse(leftStringPath);
if (pointer.TryEvaluate(data, out result))
{
if (result is JsonValue val)
{
//remove single quotes from beginning and end of string if any.
string strVal;
if (val.ToString()!.StartsWith("'") && val.ToString()!.EndsWith("'"))
{
strVal = val.ToString()!.Substring(1, val.ToString()!.Length - 2);
}
else
{
strVal = val.ToString()!;
}

var pathEvalNode = JsonNode.Parse(strVal);
return EvalPath(rightString, pathEvalNode, out result);
}
else
{
return EvalPath(rightString, result, out result);
}
}
}
else if (pathString.Trim().Equals(CONTEXTARRAY))
{
result = data;
return true;
}
else
{
var pathStringPath = string.Concat(pathString.StartsWith(JSONPOINTERSTART) ? string.Empty : JSONPOINTERSTART, pathString.Replace('.', '/'));
var pointer = JsonPointer.Parse(pathStringPath);
return pointer.TryEvaluate(data, out result);
}

result = null;
return false;
}


private Encoding GetEncodingFromCodePage(int codePage)
{
179 changes: 178 additions & 1 deletion PBIXInspectorTests/CustomRulesTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PBIXInspectorLibrary.CustomRules;
using Json.More;
using PBIXInspectorLibrary.CustomRules;
using System.Text.Json.Nodes;

namespace PBIXInspectorTests
@@ -75,6 +76,182 @@ public void StringContainsNoMatchTest()
var result = rule.Apply(null);
Assert.That((int)result.AsValue(), Is.EqualTo(0));
}

[Test]
public void SetIntersectionTest1()
{
var arr1 = "[\"a\",\"b\"]";
var arr2 = "[\"b\",\"c\"]";

var expected = "[\"b\"]";

var rule = new SetIntersectionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetIntersectionTest2()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"b\",\"c\",\"d\"]";

var expected = "[\"b\", \"c\"]";

var rule = new SetIntersectionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetIntersectionTest3()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"d\",\"e\",\"f\"]";

var expected = "[]";

var rule = new SetIntersectionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetIntersectionTest4()
{
var arr1 = "[\"d\",\"e\",\"f\"]";
var arr2 = "[\"a\",\"b\",\"c\"]";

var expected = "[]";

var rule = new SetIntersectionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetUnionTest1()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"d\",\"e\",\"f\"]";

var expected = "[\"a\",\"b\",\"c\",\"d\",\"e\",\"f\"]";

var rule = new SetUnionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetUnionTest2()
{
var arr1 = "[]";
var arr2 = "[\"d\",\"e\",\"f\"]";

var expected = "[\"d\",\"e\",\"f\"]";

var rule = new SetUnionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetUnionTest3()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[]";

var expected = "[\"a\",\"b\",\"c\"]";

var rule = new SetUnionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetUnionTest4()
{
var arr1 = "[\"a\",\"b\",\"c\",\"a\"]";
var arr2 = "[]";

var expected = "[\"a\",\"b\",\"c\"]";

var rule = new SetUnionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetUnionTest5()
{
var arr1 = "[\"a\",\"b\",\"c\",\"d\"]";
var arr2 = "[\"d\",\"e\",\"f\"]";

var expected = "[\"a\",\"b\",\"c\",\"d\",\"e\",\"f\"]";

var rule = new SetUnionRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetDifferenceTest1()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"c\",\"d\",\"e\"]";

var expected = "[\"a\",\"b\"]";

var rule = new SetDifferenceRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetSymmetricDifferenceTest1()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"c\",\"d\",\"e\"]";

var expected = "[\"a\",\"b\",\"d\",\"e\"]";

var rule = new SetSymmetricDifferenceRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That(result.IsEquivalentTo(JsonNode.Parse(expected)));
}

[Test]
public void SetEqualTest1()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"c\",\"d\",\"e\"]";

var rule = new SetEqualRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That((bool)result.AsValue(), Is.False);
}

[Test]
public void SetEqualTest2()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"a\",\"b\",\"c\"]";

var rule = new SetEqualRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That((bool)result.AsValue(), Is.True);
}

[Test]
public void SetEqualTest3()
{
var arr1 = "[\"a\",\"b\",\"c\"]";
var arr2 = "[\"c\",\"b\",\"a\"]";

var rule = new SetEqualRule(JsonNode.Parse(arr1), JsonNode.Parse(arr2));
var result = rule.Apply(null);
Assert.That((bool)result.AsValue(), Is.True);
}
#pragma warning restore CS8602
}
}
Binary file modified PBIXInspectorTests/Files/Inventory sample - fails.pbix
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -12,11 +12,11 @@
},
{
"name": "PBIDesktopVersion",
"value": "2.121.903.0 (23.09)"
"value": "2.123.684.0 (23.11)"
},
{
"name": "PBI_ProTooling",
"value": "[\"DevMode\"]"
"value": "[\"DevMode\",\"DaxQueryView_Desktop\"]"
}
],
"culture": "fr-FR",

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion PBIXInspectorWinForm/MainForm.Designer.cs

0 comments on commit d6dd501

Please sign in to comment.