diff --git a/src/ESLint.Formatter/sarif.js b/src/ESLint.Formatter/sarif.js
index ddfe98149..b313d37dd 100644
--- a/src/ESLint.Formatter/sarif.js
+++ b/src/ESLint.Formatter/sarif.js
@@ -132,9 +132,11 @@ module.exports = function (results, data) {
console.log(err);
}
}
- if (result.messages.length > 0) {
- result.messages.forEach(message => {
+ const messages = result.suppressedMessages ? result.messages.concat(result.suppressedMessages) : result.messages;
+
+ if (messages.length > 0) {
+ messages.forEach(message => {
const sarifRepresentation = {
level: getResultLevel(message),
@@ -208,7 +210,7 @@ module.exports = function (results, data) {
}
if (message.column > 0) {
sarifRepresentation.locations[0].physicalLocation.region.startColumn = message.column;
- };
+ }
}
if (message.source) {
@@ -220,6 +222,17 @@ module.exports = function (results, data) {
};
}
+ if (message.suppressions) {
+ sarifRepresentation.suppressions = message.suppressions.map(suppression => {
+ return {
+ kind: suppression.kind === "directive" ? "inSource" : "external",
+ justification: suppression.justification
+ }
+ });
+ } else {
+ sarifRepresentation.suppressions = [];
+ }
+
if (message.ruleId) {
sarifResults.push(sarifRepresentation);
} else {
diff --git a/src/ESLint.Formatter/test/sarif.js b/src/ESLint.Formatter/test/sarif.js
index 666e69c88..fb9bb10c7 100644
--- a/src/ESLint.Formatter/test/sarif.js
+++ b/src/ESLint.Formatter/test/sarif.js
@@ -87,7 +87,8 @@ describe("formatter:sarif", () => {
describe("when passed no messages", () => {
const code = [{
filePath: sourceFilePath1,
- messages: []
+ messages: [],
+ suppressedMessages: []
}];
it("should return a log with files, but no results", () => {
@@ -103,6 +104,94 @@ describe("formatter:sarif", () => {
describe("formatter:sarif", () => {
describe("when passed one message", () => {
+ const code = [{
+ filePath: sourceFilePath1,
+ messages: [{
+ message: "Unexpected value.",
+ severity: 2,
+ ruleId: testRuleId
+ }],
+ suppressedMessages: []
+ }];
+
+ it("should return a log with one file and one result", () => {
+ const log = JSON.parse(formatter(code, { rulesMeta: rules }));
+
+ assert(log.runs[0].artifacts[0].location.uri.startsWith(uriPrefix));
+ assert(log.runs[0].artifacts[0].location.uri, "/" + sourceFilePath1);
+ assert.isDefined(log.runs[0].results);
+ assert.lengthOf(log.runs[0].results, 1);
+ assert.strictEqual(log.runs[0].results[0].level, "error");
+ assert.isDefined(log.runs[0].results[0].message);
+ assert.strictEqual(log.runs[0].results[0].message.text, code[0].messages[0].message);
+ assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.startsWith(uriPrefix));
+ assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.endsWith("/" + sourceFilePath1));
+ assert.strictEqual(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.index, 0);
+ assert.isDefined(log.runs[0].results[0].suppressions);
+ assert.lengthOf(log.runs[0].results[0].suppressions, 0);
+ });
+ });
+
+ describe("when passed one suppressedMessage", () => {
+ const code = [{
+ filePath: sourceFilePath1,
+ messages: [],
+ suppressedMessages: [{
+ message: "Unexpected value.",
+ severity: 2,
+ ruleId: testRuleId,
+ suppressions: [{ kind: "directive", justification: "foo" }]
+ }]
+ }];
+
+ it("should return a log with one file and one result", () => {
+ const log = JSON.parse(formatter(code, { rulesMeta: rules }));
+
+ assert(log.runs[0].artifacts[0].location.uri.startsWith(uriPrefix));
+ assert(log.runs[0].artifacts[0].location.uri, "/" + sourceFilePath1);
+ assert.isDefined(log.runs[0].results);
+ assert.lengthOf(log.runs[0].results, 1);
+ assert.strictEqual(log.runs[0].results[0].level, "error");
+ assert.isDefined(log.runs[0].results[0].message);
+ assert.strictEqual(log.runs[0].results[0].message.text, code[0].suppressedMessages[0].message);
+ assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.startsWith(uriPrefix));
+ assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.endsWith("/" + sourceFilePath1));
+ assert.strictEqual(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.index, 0);
+ assert.lengthOf(log.runs[0].results[0].suppressions, 1);
+ assert.strictEqual(log.runs[0].results[0].suppressions[0].kind, "inSource");
+ assert.strictEqual(log.runs[0].results[0].suppressions[0].justification, code[0].suppressedMessages[0].suppressions[0].justification);
+ });
+ });
+
+ describe("when passed one suppressedMessage with multiple suppressions", () => {
+ const code = [{
+ filePath: sourceFilePath1,
+ messages: [],
+ suppressedMessages: [{
+ message: "Unexpected value.",
+ severity: 2,
+ ruleId: testRuleId,
+ suppressions: [
+ { kind: "directive", justification: "foo" },
+ { kind: "directive", justification: "bar" }
+ ]
+ }]
+ }];
+
+ it("should return a log with one file and one result", () => {
+ const log = JSON.parse(formatter(code, { rulesMeta: rules }));
+
+ assert.isDefined(log.runs[0].results[0].message);
+ assert.strictEqual(log.runs[0].results[0].message.text, code[0].suppressedMessages[0].message);
+ assert.lengthOf(log.runs[0].results[0].suppressions, 2);
+ assert.strictEqual(log.runs[0].results[0].suppressions[0].kind, "inSource");
+ assert.strictEqual(log.runs[0].results[0].suppressions[0].justification, code[0].suppressedMessages[0].suppressions[0].justification);
+ assert.strictEqual(log.runs[0].results[0].suppressions[1].kind, "inSource");
+ assert.strictEqual(log.runs[0].results[0].suppressions[1].justification, code[0].suppressedMessages[0].suppressions[1].justification);
+ });
+ });
+
+ describe("when passed one message and no suppressedMessages array", () => {
const code = [{
filePath: sourceFilePath1,
messages: [{
@@ -114,7 +203,7 @@ describe("formatter:sarif", () => {
it("should return a log with one file and one result", () => {
const log = JSON.parse(formatter(code, { rulesMeta: rules }));
-
+
assert(log.runs[0].artifacts[0].location.uri.startsWith(uriPrefix));
assert(log.runs[0].artifacts[0].location.uri, "/" + sourceFilePath1);
assert.isDefined(log.runs[0].results);
@@ -125,6 +214,8 @@ describe("formatter:sarif", () => {
assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.startsWith(uriPrefix));
assert(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.uri.endsWith("/" + sourceFilePath1));
assert.strictEqual(log.runs[0].results[0].locations[0].physicalLocation.artifactLocation.index, 0);
+ assert.isDefined(log.runs[0].results[0].suppressions);
+ assert.lengthOf(log.runs[0].results[0].suppressions, 0);
});
});
});
@@ -138,7 +229,8 @@ describe("formatter:sarif", () => {
message: "Unexpected value.",
ruleId: ruleid,
source: "getValue()"
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one rule", () => {
@@ -162,7 +254,8 @@ describe("formatter:sarif", () => {
message: "Unexpected value.",
ruleId: testRuleId,
line: 10
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one result whose location region has only a startLine", () => {
@@ -184,7 +277,8 @@ describe("formatter:sarif", () => {
ruleId: testRuleId,
line: 10,
column: 0
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one result whose location contains a region with line # and no column #", () => {
@@ -206,7 +300,8 @@ describe("formatter:sarif", () => {
ruleId: testRuleId,
line: 10,
column: 5
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one result whose location contains a region with line and column #s", () => {
@@ -229,7 +324,8 @@ describe("formatter:sarif", () => {
line: 10,
column: 5,
source: "getValue()"
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one result whose location contains a region with line and column #s", () => {
@@ -251,7 +347,8 @@ describe("formatter:sarif", () => {
severity: 2,
ruleId: testRuleId,
source: "getValue()"
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one result whose location contains a region with line and column #s", () => {
@@ -264,6 +361,39 @@ describe("formatter:sarif", () => {
});
});
+
+
+describe("formatter:sarif", () => {
+ describe("when passed one message and one suppressedMessage", () => {
+ const code = [{
+ filePath: sourceFilePath1,
+ messages: [{
+ message: "Unexpected value.",
+ severity: 2,
+ ruleId: testRuleId,
+ source: "getValue()"
+ }],
+ suppressedMessages: [{
+ message: "Unexpected value.",
+ severity: 2,
+ ruleId: testRuleId,
+ source: "getValue()",
+ suppressions: [{ kind: "directive", justification: "foo" }]
+ }]
+ }];
+
+ it("should return a log with two results, one of which has suppressions", () => {
+ const log = JSON.parse(formatter(code, rules));
+
+ assert.lengthOf(log.runs[0].results, 2)
+ assert.lengthOf(log.runs[0].results[0].suppressions, 0);
+ assert.lengthOf(log.runs[0].results[1].suppressions, 1);
+ assert.strictEqual(log.runs[0].results[1].suppressions[0].kind, "inSource");
+ assert.strictEqual(log.runs[0].results[1].suppressions[0].justification, code[0].suppressedMessages[0].suppressions[0].justification);
+ });
+ });
+});
+
describe("formatter:sarif", () => {
describe("when passed two results with two messages each", () => {
const ruleid1 = "no-unused-vars";
@@ -291,7 +421,8 @@ describe("formatter:sarif", () => {
line: 10,
column: 5,
source: "doSomething(thingId)"
- }]
+ }],
+ suppressedMessages: []
},
{
filePath: sourceFilePath2,
@@ -305,7 +436,8 @@ describe("formatter:sarif", () => {
message: "Custom error.",
ruleId: ruleid3,
line: 42
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with two files, three rules, and four results", () => {
@@ -385,6 +517,11 @@ describe("formatter:sarif", () => {
assert.strictEqual(log.runs[0].results[2].locations[0].physicalLocation.region.startLine, 18);
assert.isUndefined(log.runs[0].results[2].locations[0].physicalLocation.region.startColumn);
assert.isUndefined(log.runs[0].results[2].locations[0].physicalLocation.region.snippet);
+
+ assert.lengthOf(log.runs[0].results[0].suppressions, 0);
+ assert.lengthOf(log.runs[0].results[1].suppressions, 0);
+ assert.lengthOf(log.runs[0].results[2].suppressions, 0);
+ assert.lengthOf(log.runs[0].results[3].suppressions, 0);
});
});
});
@@ -404,7 +541,8 @@ describe("formatter:sarif", () => {
};
const code = [{
filePath: sourceFilePath1,
- messages: [ ]
+ messages: [],
+ suppressedMessages: []
},
{
filePath: sourceFilePath2,
@@ -418,7 +556,8 @@ describe("formatter:sarif", () => {
message: "Custom error.",
ruleId: ruleid3,
line: 42
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with two files, two rules, and two results", () => {
@@ -464,6 +603,9 @@ describe("formatter:sarif", () => {
assert.strictEqual(log.runs[0].results[0].locations[0].physicalLocation.region.startLine, 18);
assert.isUndefined(log.runs[0].results[0].locations[0].physicalLocation.region.startColumn);
assert.isUndefined(log.runs[0].results[0].locations[0].physicalLocation.region.snippet);
+
+ assert.lengthOf(log.runs[0].results[0].suppressions, 0);
+ assert.lengthOf(log.runs[0].results[1].suppressions, 0);
});
});
});
@@ -476,7 +618,8 @@ describe("formatter:sarif", () => {
message: "Internal error.",
severity: 2,
// no ruleId property
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with no rules, no results, and a tool execution notification", () => {
@@ -526,7 +669,8 @@ describe("formatter:sarif", () => {
message: "Custom error.",
ruleId: ruleid,
line: 42
- }]
+ }],
+ suppressedMessages: []
}];
it("should return a log with one file, one rule, and one result", () => {
const log = JSON.parse(formatter(code, { rulesMeta: rules }));
diff --git a/src/ReleaseHistory.md b/src/ReleaseHistory.md
index 3441bbe16..1811a5a09 100644
--- a/src/ReleaseHistory.md
+++ b/src/ReleaseHistory.md
@@ -7,6 +7,7 @@
* BREAKING: Fix `InvalidOperationException` when using PropertiesDictionary in a multithreaded application, and remove `[Serializable]` from it. Now use of BinaryFormatter on it will result in `SerializationException`: Type `PropertiesDictionary` is not marked as serializable. [#2415](https://github.com/microsoft/sarif-sdk/pull/2415)
* BREAKING: `SarifLogger` now emits an artifacts table entry if `artifactLocation` is not null for tool configuration and tool execution notifications. [#2437](https://github.com/microsoft/sarif-sdk/pull/2437)
* BUGFIX: Fix `ArgumentException` when `--recurse` is enabled and two file target specifiers generates the same file path. [#2438](https://github.com/microsoft/sarif-sdk/pull/2438)
+* FEATURE: Add `--sort-results` argument to `rewrite` command to get sorted SARIF results. [#2422](https://github.com/microsoft/sarif-sdk/pull/2422)
## **v2.4.12** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.4.12) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.4.12) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.4.12) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.4.12) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.4.12)
diff --git a/src/Sarif/Comparers/ArtifactSortingComparer.cs b/src/Sarif/Comparers/ArtifactComparer.cs
similarity index 51%
rename from src/Sarif/Comparers/ArtifactSortingComparer.cs
rename to src/Sarif/Comparers/ArtifactComparer.cs
index a4db5c069..36fdd5c68 100644
--- a/src/Sarif/Comparers/ArtifactSortingComparer.cs
+++ b/src/Sarif/Comparers/ArtifactComparer.cs
@@ -2,137 +2,110 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
-using System.Linq;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class ArtifactSortingComparer : IComparer
+ internal class ArtifactComparer : IComparer
{
- internal static readonly ArtifactSortingComparer Instance = new ArtifactSortingComparer();
+ internal static readonly ArtifactComparer Instance = new ArtifactComparer();
public int Compare(Artifact left, Artifact right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
+ return compareResult;
}
- if (right == null)
- {
- return 1;
- }
+ compareResult = MessageComparer.Instance.Compare(left.Description, right.Description);
- int compareResult = 0;
- compareResult = MessageSortingComparer.Instance.Compare(left.Description, right.Description);
if (compareResult != 0)
{
return compareResult;
}
- compareResult = ArtifactLocationSortingComparer.Instance.Compare(left.Location, right.Location);
+ compareResult = ArtifactLocationComparer.Instance.Compare(left.Location, right.Location);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.ParentIndex.CompareTo(right.ParentIndex);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Offset.CompareTo(right.Offset);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Length.CompareTo(right.Length);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Roles.CompareTo(right.Roles);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.MimeType, right.MimeType);
+
if (compareResult != 0)
{
return compareResult;
}
- compareResult = ArtifactContentSortingComparer.Instance.Compare(left.Contents, right.Contents);
+ compareResult = ArtifactContentComparer.Instance.Compare(left.Contents, right.Contents);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.Encoding, right.Encoding);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.SourceLanguage, right.SourceLanguage);
+
if (compareResult != 0)
{
return compareResult;
}
- if (!object.ReferenceEquals(left.Hashes, right.Hashes))
+ compareResult = ComparerHelper.CompareDictionary(left.Hashes, right.Hashes);
+
+ if (compareResult != 0)
{
- if (left.Hashes == null)
- {
- return -1;
- }
-
- if (right.Hashes == null)
- {
- return 1;
- }
-
- compareResult = left.Hashes.Count.CompareTo(right.Hashes.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Hashes.Count; i++)
- {
- compareResult = string.Compare(left.Hashes.ElementAt(i).Key, right.Hashes.ElementAt(i).Key);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.Hashes.ElementAt(i).Value, right.Hashes.ElementAt(i).Value);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
+ return compareResult;
}
compareResult = left.LastModifiedTimeUtc.CompareTo(right.LastModifiedTimeUtc);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/ArtifactContentSortingComparer.cs b/src/Sarif/Comparers/ArtifactContentComparer.cs
similarity index 53%
rename from src/Sarif/Comparers/ArtifactContentSortingComparer.cs
rename to src/Sarif/Comparers/ArtifactContentComparer.cs
index e0c0d2896..1a1709a4e 100644
--- a/src/Sarif/Comparers/ArtifactContentSortingComparer.cs
+++ b/src/Sarif/Comparers/ArtifactContentComparer.cs
@@ -4,52 +4,45 @@
using System.Collections.Generic;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class ArtifactContentSortingComparer : IComparer
+ internal class ArtifactContentComparer : IComparer
{
- internal static readonly ArtifactContentSortingComparer Instance = new ArtifactContentSortingComparer();
+ internal static readonly ArtifactContentComparer Instance = new ArtifactContentComparer();
public int Compare(ArtifactContent left, ArtifactContent right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
+ return compareResult;
}
- int compareResult = 0;
compareResult = string.Compare(left.Text, right.Text);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.Binary, right.Binary);
+
if (compareResult != 0)
{
return compareResult;
}
- compareResult = MultiformatMessageStringSortingComparer.Instance.Compare(left.Rendered, right.Rendered);
+ compareResult = MultiformatMessageStringComparer.Instance.Compare(left.Rendered, right.Rendered);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/ArtifactLocationComparer.cs b/src/Sarif/Comparers/ArtifactLocationComparer.cs
new file mode 100644
index 000000000..9751aa98f
--- /dev/null
+++ b/src/Sarif/Comparers/ArtifactLocationComparer.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ArtifactLocationComparer : IComparer
+ {
+ internal static readonly ArtifactLocationComparer Instance = new ArtifactLocationComparer();
+
+ public int Compare(ArtifactLocation left, ArtifactLocation right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareUri(left.Uri, right.Uri);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.UriBaseId, right.UriBaseId);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Index.CompareTo(right.Index);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MessageComparer.Instance.Compare(left.Description, right.Description);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/ArtifactLocationSortingComparer.cs b/src/Sarif/Comparers/ArtifactLocationSortingComparer.cs
deleted file mode 100644
index aac3fa1be..000000000
--- a/src/Sarif/Comparers/ArtifactLocationSortingComparer.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class ArtifactLocationSortingComparer : IComparer
- {
- internal static readonly ArtifactLocationSortingComparer Instance = new ArtifactLocationSortingComparer();
-
- public int Compare(ArtifactLocation left, ArtifactLocation right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- if (ReferenceEquals(left.Uri, right.Uri))
- {
- return 0;
- }
-
- if (left.Uri == null)
- {
- return -1;
- }
-
- if (right.Uri == null)
- {
- return 1;
- }
-
- compareResult = string.Compare(left.Uri.OriginalString, right.Uri.OriginalString);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.UriBaseId, right.UriBaseId);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.Index.CompareTo(right.Index);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = MessageSortingComparer.Instance.Compare(left.Description, right.Description);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/CodeFlowComparer.cs b/src/Sarif/Comparers/CodeFlowComparer.cs
new file mode 100644
index 000000000..5823deacb
--- /dev/null
+++ b/src/Sarif/Comparers/CodeFlowComparer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class CodeFlowComparer : IComparer
+ {
+ internal static readonly CodeFlowComparer Instance = new CodeFlowComparer();
+
+ public int Compare(CodeFlow left, CodeFlow right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = MessageComparer.Instance.Compare(left.Message, right.Message);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.ThreadFlows, right.ThreadFlows, ThreadFlowComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/CodeFlowSortingComparer.cs b/src/Sarif/Comparers/CodeFlowSortingComparer.cs
deleted file mode 100644
index d28f7b9d9..000000000
--- a/src/Sarif/Comparers/CodeFlowSortingComparer.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class CodeFlowSortingComparer : IComparer
- {
- internal static readonly CodeFlowSortingComparer Instance = new CodeFlowSortingComparer();
-
- public int Compare(CodeFlow left, CodeFlow right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
-
- compareResult = MessageSortingComparer.Instance.Compare(left.Message, right.Message);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.ThreadFlows, right.ThreadFlows))
- {
-
- if (left.ThreadFlows == null)
- {
- return -1;
- }
-
- if (right.ThreadFlows == null)
- {
- return 1;
- }
-
- compareResult = left.ThreadFlows.Count.CompareTo(right.ThreadFlows.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int index_1 = 0; index_1 < left.ThreadFlows.Count; ++index_1)
- {
- compareResult = ThreadFlowSortingComparer.Instance.Compare(left.ThreadFlows[index_1], right.ThreadFlows[index_1]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/ComparerHelpers.cs b/src/Sarif/Comparers/ComparerHelpers.cs
new file mode 100644
index 000000000..2b6300e27
--- /dev/null
+++ b/src/Sarif/Comparers/ComparerHelpers.cs
@@ -0,0 +1,206 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ComparerHelper
+ {
+ ///
+ /// Compare 2 object refences. Return value false presents need to
+ /// compare objects values to get final result.
+ ///
+ /// The first object to compare.
+ /// The second object to compare.
+ ///
+ /// 0 if both objects are same or both are null.
+ /// 1 if the first object is null and the second object is not null.
+ /// 2 if the first object is not null and the second object is null.
+ ///
+ /// Return true if can get a definite result, otherwise return false.
+ public static bool CompareReference(object left, object right, out int result)
+ {
+ result = 0;
+
+ // ReferenceEquals returns true if both are null
+ if (object.ReferenceEquals(left, right))
+ {
+ result = 0;
+ return true;
+ }
+
+ if (left == null)
+ {
+ result = -1;
+ return true;
+ }
+
+ if (right == null)
+ {
+ result = 1;
+ return true;
+ }
+
+ // Cannot determine the comparison result based on reference
+ // need further comparing objects properties.
+ return false;
+ }
+
+ ///
+ /// Compare 2 lists of type T derived from IComparable.
+ ///
+ /// type derived from IComparable
+ ///
+ ///
+ ///
+ public static int CompareList(IList left, IList right) where T : IComparable
+ {
+ return CompareListHelper(left, right, (a, b) => a.CompareTo(b));
+ }
+
+ ///
+ /// Compare 2 lists of type T using a Comparer.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int CompareList(IList left, IList right, IComparer comparer)
+ {
+ if (comparer == null)
+ {
+ throw new ArgumentNullException(nameof(comparer));
+ }
+
+ return CompareListHelper(left, right, comparer.Compare);
+ }
+
+ /// Main function for comparing 2 lists.
+ private static int CompareListHelper(IList left, IList right, Func compareFunc)
+ {
+ if (compareFunc == null)
+ {
+ throw new ArgumentNullException(nameof(compareFunc));
+ }
+
+ if (CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Count.CompareTo(right.Count);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ for (int i = 0; i < left.Count; ++i)
+ {
+ if (CompareReference(left[i], right[i], out compareResult) && compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = compareFunc(left[i], right[i]);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+ }
+
+ return compareResult;
+ }
+
+ public static int CompareDictionary(IDictionary left, IDictionary right) where T : IComparable
+ {
+ return CompareDictionaryHelper(left, right, (a, b) => a.CompareTo(b));
+ }
+
+ public static int CompareDictionary(IDictionary left, IDictionary right, IComparer comparer)
+ {
+ if (comparer == null)
+ {
+ throw new ArgumentNullException(nameof(comparer));
+ }
+
+ return CompareDictionaryHelper(left, right, comparer.Compare);
+ }
+
+ /// Main function for comparing 2 dictionaries.
+ private static int CompareDictionaryHelper(IDictionary left, IDictionary right, Func compareFunc)
+ {
+ if (compareFunc == null)
+ {
+ throw new ArgumentNullException(nameof(compareFunc));
+ }
+
+ if (CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Count.CompareTo(right.Count);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ IList leftKeys = left.Keys.OrderBy(k => k).ToList();
+ IList rightKeys = right.Keys.OrderBy(k => k).ToList();
+
+ for (int i = 0; i < leftKeys.Count; ++i)
+ {
+ compareResult = leftKeys[i].CompareTo(rightKeys[i]);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = compareFunc(left[leftKeys[i]], right[rightKeys[i]]);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+ }
+
+ return compareResult;
+ }
+
+ ///
+ /// Compare 2 Uri objects using same parameters.
+ ///
+ ///
+ ///
+ ///
+ public static int CompareUri(Uri left, Uri right)
+ {
+ int result = Uri.Compare(
+ left,
+ right,
+ UriComponents.Path,
+ UriFormat.SafeUnescaped,
+ StringComparison.Ordinal);
+
+ // Uri.Compare returns int value indicates lexical relationship between
+ // 2 Uris, can be any number. Just return 3 options 0/1/-1.
+ return result switch
+ {
+ var x when x == 0 => 0,
+ var x when x > 0 => 1,
+ var x when x < 0 => -1,
+ _ => result
+ };
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/LocationComparer.cs b/src/Sarif/Comparers/LocationComparer.cs
new file mode 100644
index 000000000..893225c68
--- /dev/null
+++ b/src/Sarif/Comparers/LocationComparer.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class LocationComparer : IComparer
+ {
+ internal static readonly LocationComparer Instance = new LocationComparer();
+
+ public int Compare(Location left, Location right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Id.CompareTo(right.Id);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = PhysicalLocationComparer.Instance.Compare(left.PhysicalLocation, right.PhysicalLocation);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.LogicalLocations, right.LogicalLocations, LogicalLocationComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/LocationSortingComparer.cs b/src/Sarif/Comparers/LocationSortingComparer.cs
deleted file mode 100644
index de91361b8..000000000
--- a/src/Sarif/Comparers/LocationSortingComparer.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class LocationSortingComparer : IComparer
- {
- internal static readonly LocationSortingComparer Instance = new LocationSortingComparer();
-
- public int Compare(Location left, Location right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- compareResult = left.Id.CompareTo(right.Id);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = PhysicalLocationSortingComparer.Instance.Compare(left.PhysicalLocation, right.PhysicalLocation);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.LogicalLocations, right.LogicalLocations))
- {
- if (left.LogicalLocations == null)
- {
- return -1;
- }
-
- if (right.LogicalLocations == null)
- {
- return 1;
- }
-
- compareResult = left.LogicalLocations.Count.CompareTo(right.LogicalLocations.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.LogicalLocations.Count; ++i)
- {
- compareResult = LogicalLocationSortingComparer.Instance.Compare(left.LogicalLocations[i], right.LogicalLocations[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/LogicalLocationSortingComparer.cs b/src/Sarif/Comparers/LogicalLocationComparer.cs
similarity index 69%
rename from src/Sarif/Comparers/LogicalLocationSortingComparer.cs
rename to src/Sarif/Comparers/LogicalLocationComparer.cs
index 59e295f82..9a7bab897 100644
--- a/src/Sarif/Comparers/LogicalLocationSortingComparer.cs
+++ b/src/Sarif/Comparers/LogicalLocationComparer.cs
@@ -4,70 +4,66 @@
using System.Collections.Generic;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class LogicalLocationSortingComparer : IComparer
+ internal class LogicalLocationComparer : IComparer
{
- internal static readonly LogicalLocationSortingComparer Instance = new LogicalLocationSortingComparer();
+ internal static readonly LogicalLocationComparer Instance = new LogicalLocationComparer();
public int Compare(LogicalLocation left, LogicalLocation right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
+ return compareResult;
}
- int compareResult = 0;
compareResult = string.Compare(left.Name, right.Name);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Index.CompareTo(right.Index);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.FullyQualifiedName, right.FullyQualifiedName);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.DecoratedName, right.DecoratedName);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.ParentIndex.CompareTo(right.ParentIndex);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.Kind, right.Kind);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/MessageComparer.cs b/src/Sarif/Comparers/MessageComparer.cs
new file mode 100644
index 000000000..de951435e
--- /dev/null
+++ b/src/Sarif/Comparers/MessageComparer.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class MessageComparer : IComparer
+ {
+ internal static readonly MessageComparer Instance = new MessageComparer();
+
+ public int Compare(Message left, Message right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Text, right.Text);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Markdown, right.Markdown);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Id, right.Id);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Arguments, right.Arguments);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/MessageSortingComparer.cs b/src/Sarif/Comparers/MessageSortingComparer.cs
deleted file mode 100644
index 465621e7d..000000000
--- a/src/Sarif/Comparers/MessageSortingComparer.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class MessageSortingComparer : IComparer
- {
- internal static readonly MessageSortingComparer Instance = new MessageSortingComparer();
-
- public int Compare(Message left, Message right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- compareResult = string.Compare(left.Text, right.Text);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.Markdown, right.Markdown);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.Id, right.Id);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.Arguments, right.Arguments))
- {
- if (left.Arguments == null)
- {
- return -1;
- }
-
- if (right.Arguments == null)
- {
- return 1;
- }
-
- compareResult = left.Arguments.Count.CompareTo(right.Arguments.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Arguments.Count; ++i)
- {
- compareResult = string.Compare(left.Arguments[i], right.Arguments[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/MultiformatMessageStringSortingComparer.cs b/src/Sarif/Comparers/MultiformatMessageStringComparer.cs
similarity index 54%
rename from src/Sarif/Comparers/MultiformatMessageStringSortingComparer.cs
rename to src/Sarif/Comparers/MultiformatMessageStringComparer.cs
index 62f70413a..14306c2e4 100644
--- a/src/Sarif/Comparers/MultiformatMessageStringSortingComparer.cs
+++ b/src/Sarif/Comparers/MultiformatMessageStringComparer.cs
@@ -4,46 +4,38 @@
using System.Collections.Generic;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class MultiformatMessageStringSortingComparer : IComparer
+ internal class MultiformatMessageStringComparer : IComparer
{
- internal static readonly MultiformatMessageStringSortingComparer Instance = new MultiformatMessageStringSortingComparer();
+ internal static readonly MultiformatMessageStringComparer Instance = new MultiformatMessageStringComparer();
public int Compare(MultiformatMessageString left, MultiformatMessageString right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
+ return compareResult;
}
- int compareResult = 0;
compareResult = string.Compare(left.Text, right.Text);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = string.Compare(left.Markdown, right.Markdown);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/PhysicalLocationComparer.cs b/src/Sarif/Comparers/PhysicalLocationComparer.cs
new file mode 100644
index 000000000..99694ec63
--- /dev/null
+++ b/src/Sarif/Comparers/PhysicalLocationComparer.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class PhysicalLocationComparer : IComparer
+ {
+ internal static readonly PhysicalLocationComparer Instance = new PhysicalLocationComparer();
+
+ public int Compare(PhysicalLocation left, PhysicalLocation right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = ArtifactLocationComparer.Instance.Compare(left.ArtifactLocation, right.ArtifactLocation);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = RegionComparer.Instance.Compare(left.Region, right.Region);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = RegionComparer.Instance.Compare(left.ContextRegion, right.ContextRegion);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/PhysicalLocationSortingComparer.cs b/src/Sarif/Comparers/PhysicalLocationSortingComparer.cs
deleted file mode 100644
index d1e8ac37a..000000000
--- a/src/Sarif/Comparers/PhysicalLocationSortingComparer.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class PhysicalLocationSortingComparer : IComparer
- {
- internal static readonly PhysicalLocationSortingComparer Instance = new PhysicalLocationSortingComparer();
-
- public int Compare(PhysicalLocation left, PhysicalLocation right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
-
- compareResult = ArtifactLocationSortingComparer.Instance.Compare(left.ArtifactLocation, right.ArtifactLocation);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = RegionSortingComparer.Instance.Compare(left.Region, right.Region);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = RegionSortingComparer.Instance.Compare(left.ContextRegion, right.ContextRegion);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/RegionSortingComparer.cs b/src/Sarif/Comparers/RegionComparer.cs
similarity index 74%
rename from src/Sarif/Comparers/RegionSortingComparer.cs
rename to src/Sarif/Comparers/RegionComparer.cs
index 57eb3b15b..94d522eba 100644
--- a/src/Sarif/Comparers/RegionSortingComparer.cs
+++ b/src/Sarif/Comparers/RegionComparer.cs
@@ -4,82 +4,80 @@
using System.Collections.Generic;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class RegionSortingComparer : IComparer
+ internal class RegionComparer : IComparer
{
- internal static readonly RegionSortingComparer Instance = new RegionSortingComparer();
+ internal static readonly RegionComparer Instance = new RegionComparer();
public int Compare(Region left, Region right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
+ return compareResult;
}
- int compareResult = 0;
compareResult = left.StartLine.CompareTo(right.StartLine);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.StartColumn.CompareTo(right.StartColumn);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.EndLine.CompareTo(right.EndLine);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.EndColumn.CompareTo(right.EndColumn);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.CharOffset.CompareTo(right.CharOffset);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.CharLength.CompareTo(right.CharLength);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.ByteOffset.CompareTo(right.ByteOffset);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.ByteLength.CompareTo(right.ByteLength);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/ReportingConfigurationSortingComparer.cs b/src/Sarif/Comparers/ReportingConfigurationComparer.cs
similarity index 58%
rename from src/Sarif/Comparers/ReportingConfigurationSortingComparer.cs
rename to src/Sarif/Comparers/ReportingConfigurationComparer.cs
index c8ca7452f..3ea985309 100644
--- a/src/Sarif/Comparers/ReportingConfigurationSortingComparer.cs
+++ b/src/Sarif/Comparers/ReportingConfigurationComparer.cs
@@ -4,52 +4,45 @@
using System.Collections.Generic;
///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
///
-namespace Microsoft.CodeAnalysis.Sarif
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
{
- internal class ReportingConfigurationSortingComparer : IComparer
+ internal class ReportingConfigurationComparer : IComparer
{
- internal static readonly ReportingConfigurationSortingComparer Instance = new ReportingConfigurationSortingComparer();
+ internal static readonly ReportingConfigurationComparer Instance = new ReportingConfigurationComparer();
public int Compare(ReportingConfiguration left, ReportingConfiguration right)
{
- if (ReferenceEquals(left, right))
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
{
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
+ return compareResult;
}
- int compareResult = 0;
compareResult = left.Enabled.CompareTo(right.Enabled);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Level.CompareTo(right.Level);
+
if (compareResult != 0)
{
return compareResult;
}
compareResult = left.Rank.CompareTo(right.Rank);
+
if (compareResult != 0)
{
return compareResult;
}
+ // Warning there may be properties are not compared.
return compareResult;
}
}
diff --git a/src/Sarif/Comparers/ReportingDescriptorComparer.cs b/src/Sarif/Comparers/ReportingDescriptorComparer.cs
new file mode 100644
index 000000000..c61f3adb1
--- /dev/null
+++ b/src/Sarif/Comparers/ReportingDescriptorComparer.cs
@@ -0,0 +1,105 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ReportingDescriptorComparer : IComparer
+ {
+ internal static readonly ReportingDescriptorComparer Instance = new ReportingDescriptorComparer();
+
+ public int Compare(ReportingDescriptor left, ReportingDescriptor right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Id, right.Id);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.DeprecatedIds, right.DeprecatedIds);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Guid, right.Guid);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.DeprecatedGuids, right.DeprecatedGuids);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Name, right.Name);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.DeprecatedNames, right.DeprecatedNames);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MultiformatMessageStringComparer.Instance.Compare(left.ShortDescription, right.ShortDescription);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MultiformatMessageStringComparer.Instance.Compare(left.FullDescription, right.FullDescription);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ReportingConfigurationComparer.Instance.Compare(left.DefaultConfiguration, right.DefaultConfiguration);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareUri(left.HelpUri, right.HelpUri);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MultiformatMessageStringComparer.Instance.Compare(left.Help, right.Help);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/ReportingDescriptorSortingComparer.cs b/src/Sarif/Comparers/ReportingDescriptorSortingComparer.cs
deleted file mode 100644
index 3941229a4..000000000
--- a/src/Sarif/Comparers/ReportingDescriptorSortingComparer.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class ReportingDescriptorSortingComparer : IComparer
- {
- internal static readonly ReportingDescriptorSortingComparer Instance = new ReportingDescriptorSortingComparer();
-
- public int Compare(ReportingDescriptor left, ReportingDescriptor right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- compareResult = string.Compare(left.Id, right.Id);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.DeprecatedIds, right.DeprecatedIds))
- {
- if (left.DeprecatedIds == null)
- {
- return -1;
- }
-
- if (right.DeprecatedIds == null)
- {
- return 1;
- }
-
- compareResult = left.DeprecatedIds.Count.CompareTo(right.DeprecatedIds.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.DeprecatedIds.Count; ++i)
- {
- compareResult = string.Compare(left.DeprecatedIds[i], right.DeprecatedIds[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = string.Compare(left.Guid, right.Guid);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.DeprecatedGuids, right.DeprecatedGuids))
- {
- if (left.DeprecatedGuids == null)
- {
- return -1;
- }
-
- if (right.DeprecatedGuids == null)
- {
- return 1;
- }
-
- compareResult = left.DeprecatedGuids.Count.CompareTo(right.DeprecatedGuids.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.DeprecatedGuids.Count; ++i)
- {
- compareResult = string.Compare(left.DeprecatedGuids[i], right.DeprecatedGuids[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = string.Compare(left.Name, right.Name);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.DeprecatedNames, right.DeprecatedNames))
- {
- if (left.DeprecatedNames == null)
- {
- return -1;
- }
-
- if (right.DeprecatedNames == null)
- {
- return 1;
- }
-
- compareResult = left.DeprecatedNames.Count.CompareTo(right.DeprecatedNames.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.DeprecatedNames.Count; ++i)
- {
- compareResult = string.Compare(left.DeprecatedNames[i], right.DeprecatedNames[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = MultiformatMessageStringSortingComparer.Instance.Compare(left.ShortDescription, right.ShortDescription);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = MultiformatMessageStringSortingComparer.Instance.Compare(left.FullDescription, right.FullDescription);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = ReportingConfigurationSortingComparer.Instance.Compare(left.DefaultConfiguration, right.DefaultConfiguration);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.HelpUri.OriginalString, right.HelpUri.OriginalString);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = MultiformatMessageStringSortingComparer.Instance.Compare(left.Help, right.Help);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/ResultComparer.cs b/src/Sarif/Comparers/ResultComparer.cs
new file mode 100644
index 000000000..579a46684
--- /dev/null
+++ b/src/Sarif/Comparers/ResultComparer.cs
@@ -0,0 +1,119 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ResultComparer : IComparer
+ {
+ internal static readonly ResultComparer Instance = new ResultComparer();
+
+ public int Compare(Result left, Result right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.RuleId, right.RuleId);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.RuleIndex.CompareTo(right.RuleIndex);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Level.CompareTo(right.Level);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Kind.CompareTo(right.Kind);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MessageComparer.Instance.Compare(left.Message, right.Message);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ArtifactLocationComparer.Instance.Compare(left.AnalysisTarget, right.AnalysisTarget);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Locations, right.Locations, LocationComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Guid, right.Guid);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.CorrelationGuid, right.CorrelationGuid);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.OccurrenceCount.CompareTo(right.OccurrenceCount);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.CodeFlows, right.CodeFlows, CodeFlowComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.BaselineState.CompareTo(right.BaselineState);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Rank.CompareTo(right.Rank);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/ResultSortingComparer.cs b/src/Sarif/Comparers/ResultSortingComparer.cs
deleted file mode 100644
index 4ff39b64a..000000000
--- a/src/Sarif/Comparers/ResultSortingComparer.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class ResultSortingComparer : IComparer
- {
- internal static readonly ResultSortingComparer Instance = new ResultSortingComparer();
-
- public int Compare(Result left, Result right)
- {
- // both reference to same object, or both are null
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
-
- compareResult = string.Compare(left.RuleId, right.RuleId);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.RuleIndex.CompareTo(right.RuleIndex);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.Level.CompareTo(right.Level);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.Kind.CompareTo(right.Kind);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = MessageSortingComparer.Instance.Compare(left.Message, right.Message);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = ArtifactLocationSortingComparer.Instance.Compare(left.AnalysisTarget, right.AnalysisTarget);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.Locations, right.Locations))
- {
- if (left.Locations == null)
- {
- return -1;
- }
-
- if (right.Locations == null)
- {
- return 1;
- }
-
- compareResult = left.Locations.Count.CompareTo(right.Locations.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Locations.Count; ++i)
- {
- compareResult = LocationSortingComparer.Instance.Compare(left.Locations[i], right.Locations[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = string.Compare(left.Guid, right.Guid);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = string.Compare(left.CorrelationGuid, right.CorrelationGuid);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.OccurrenceCount.CompareTo(right.OccurrenceCount);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.CodeFlows, right.CodeFlows))
- {
- if (left.CodeFlows == null)
- {
- return -1;
- }
-
- if (right.CodeFlows == null)
- {
- return 1;
- }
-
- compareResult = left.CodeFlows.Count.CompareTo(right.CodeFlows.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.CodeFlows.Count; ++i)
- {
- compareResult = CodeFlowSortingComparer.Instance.Compare(left.CodeFlows[i], right.CodeFlows[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = left.BaselineState.CompareTo(right.BaselineState);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.Rank.CompareTo(right.Rank);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/RunComparer.cs b/src/Sarif/Comparers/RunComparer.cs
new file mode 100644
index 000000000..7e674c2ca
--- /dev/null
+++ b/src/Sarif/Comparers/RunComparer.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class RunComparer : IComparer
+ {
+ internal static readonly RunComparer Instance = new RunComparer();
+
+ public int Compare(Run left, Run right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Artifacts, right.Artifacts, ArtifactComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ToolComponentComparer.Instance.Compare(left?.Tool?.Driver, right?.Tool?.Driver);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Results, right.Results, ResultComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/RunSortingComparer.cs b/src/Sarif/Comparers/RunSortingComparer.cs
deleted file mode 100644
index c666437df..000000000
--- a/src/Sarif/Comparers/RunSortingComparer.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class RunSortingComparer : IComparer
- {
- internal static readonly RunSortingComparer Instance = new RunSortingComparer();
-
- public int Compare(Run left, Run right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
-
- if (!object.ReferenceEquals(left.Tool.Driver.Rules, right.Tool.Driver.Rules))
- {
- if (left.Tool.Driver.Rules == null)
- {
- return -1;
- }
-
- if (right.Tool.Driver.Rules == null)
- {
- return 1;
- }
-
- compareResult = left.Tool.Driver.Rules.Count.CompareTo(right.Tool.Driver.Rules.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Tool.Driver.Rules.Count; ++i)
- {
- compareResult = ReportingDescriptorSortingComparer.Instance.Compare(left.Tool.Driver.Rules[i], right.Tool.Driver.Rules[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/ThreadFlowComparer.cs b/src/Sarif/Comparers/ThreadFlowComparer.cs
new file mode 100644
index 000000000..512ce96da
--- /dev/null
+++ b/src/Sarif/Comparers/ThreadFlowComparer.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ThreadFlowComparer : IComparer
+ {
+ internal static readonly ThreadFlowComparer Instance = new ThreadFlowComparer();
+
+ public int Compare(ThreadFlow left, ThreadFlow right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Id, right.Id);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = MessageComparer.Instance.Compare(left.Message, right.Message);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Locations, right.Locations, ThreadFlowLocationComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/ThreadFlowLocationComparer.cs b/src/Sarif/Comparers/ThreadFlowLocationComparer.cs
new file mode 100644
index 000000000..621ec30dd
--- /dev/null
+++ b/src/Sarif/Comparers/ThreadFlowLocationComparer.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ThreadFlowLocationComparer : IComparer
+ {
+ internal static readonly ThreadFlowLocationComparer Instance = new ThreadFlowLocationComparer();
+
+ public int Compare(ThreadFlowLocation left, ThreadFlowLocation right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Index.CompareTo(right.Index);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = LocationComparer.Instance.Compare(left.Location, right.Location);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Kinds, right.Kinds);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.NestingLevel.CompareTo(right.NestingLevel);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.ExecutionOrder.CompareTo(right.ExecutionOrder);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.ExecutionTimeUtc.CompareTo(right.ExecutionTimeUtc);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = left.Importance.CompareTo(right.Importance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Comparers/ThreadFlowLocationSortingComparer.cs b/src/Sarif/Comparers/ThreadFlowLocationSortingComparer.cs
deleted file mode 100644
index c8e9c75fc..000000000
--- a/src/Sarif/Comparers/ThreadFlowLocationSortingComparer.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class ThreadFlowLocationSortingComparer : IComparer
- {
- internal static readonly ThreadFlowLocationSortingComparer Instance = new ThreadFlowLocationSortingComparer();
-
- public int Compare(ThreadFlowLocation left, ThreadFlowLocation right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- compareResult = left.Index.CompareTo(right.Index);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = LocationSortingComparer.Instance.Compare(left.Location, right.Location);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.Kinds, right.Kinds))
- {
- if (left.Kinds == null)
- {
- return -1;
- }
-
- if (right.Kinds == null)
- {
- return 1;
- }
-
- compareResult = left.Kinds.Count.CompareTo(right.Kinds.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Kinds.Count; ++i)
- {
- compareResult = string.Compare(left.Kinds[i], right.Kinds[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- compareResult = left.NestingLevel.CompareTo(right.NestingLevel);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.ExecutionOrder.CompareTo(right.ExecutionOrder);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.ExecutionTimeUtc.CompareTo(right.ExecutionTimeUtc);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = left.Importance.CompareTo(right.Importance);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/ThreadFlowSortingComparer.cs b/src/Sarif/Comparers/ThreadFlowSortingComparer.cs
deleted file mode 100644
index db2035917..000000000
--- a/src/Sarif/Comparers/ThreadFlowSortingComparer.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Collections.Generic;
-
-///
-/// All Comparer implementations should be replaced by auto-generated codes by JSchema as
-/// part of EqualityComparer in a planned comprehensive solution.
-/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
-///
-namespace Microsoft.CodeAnalysis.Sarif
-{
- internal class ThreadFlowSortingComparer : IComparer
- {
- internal static readonly ThreadFlowSortingComparer Instance = new ThreadFlowSortingComparer();
-
- public int Compare(ThreadFlow left, ThreadFlow right)
- {
- if (ReferenceEquals(left, right))
- {
- return 0;
- }
-
- if (left == null)
- {
- return -1;
- }
-
- if (right == null)
- {
- return 1;
- }
-
- int compareResult = 0;
- compareResult = string.Compare(left.Id, right.Id);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- compareResult = MessageSortingComparer.Instance.Compare(left.Message, right.Message);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- if (!object.ReferenceEquals(left.Locations, right.Locations))
- {
- if (left.Locations == null)
- {
- return -1;
- }
-
- if (right.Locations == null)
- {
- return 1;
- }
-
- compareResult = left.Locations.Count.CompareTo(right.Locations.Count);
- if (compareResult != 0)
- {
- return compareResult;
- }
-
- for (int i = 0; i < left.Locations.Count; ++i)
- {
- compareResult = ThreadFlowLocationSortingComparer.Instance.Compare(left.Locations[i], right.Locations[i]);
- if (compareResult != 0)
- {
- return compareResult;
- }
- }
- }
-
- return compareResult;
- }
- }
-}
diff --git a/src/Sarif/Comparers/ToolComponentComparer.cs b/src/Sarif/Comparers/ToolComponentComparer.cs
new file mode 100644
index 000000000..0e0f1c548
--- /dev/null
+++ b/src/Sarif/Comparers/ToolComponentComparer.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+
+///
+/// Warning this comparer may not have all properties compared. Will be replaced by comprehensive
+/// comparer generated by JSchema as part of EqualityComparer in a planned comprehensive solution.
+/// Tracking by issue: https://github.com/microsoft/jschema/issues/141
+///
+
+namespace Microsoft.CodeAnalysis.Sarif.Comparers
+{
+ internal class ToolComponentComparer : IComparer
+ {
+ internal static readonly ToolComponentComparer Instance = new ToolComponentComparer();
+
+ public int Compare(ToolComponent left, ToolComponent right)
+ {
+ if (ComparerHelper.CompareReference(left, right, out int compareResult))
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Guid, right.Guid);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Name, right.Name);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Organization, right.Organization);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Product, right.Product);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.FullName, right.FullName);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.Version, right.Version);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.SemanticVersion, right.SemanticVersion);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = string.Compare(left.ReleaseDateUtc, right.ReleaseDateUtc);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareUri(left.DownloadUri, right.DownloadUri);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareUri(left.InformationUri, right.InformationUri);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ compareResult = ComparerHelper.CompareList(left.Rules, right.Rules, ReportingDescriptorComparer.Instance);
+
+ if (compareResult != 0)
+ {
+ return compareResult;
+ }
+
+ // Warning there may be properties are not compared.
+ return compareResult;
+ }
+ }
+}
diff --git a/src/Sarif/Visitors/SortingVisitor.cs b/src/Sarif/Visitors/SortingVisitor.cs
index a0f3c295a..81ad4eb09 100644
--- a/src/Sarif/Visitors/SortingVisitor.cs
+++ b/src/Sarif/Visitors/SortingVisitor.cs
@@ -5,66 +5,51 @@
using System.Collections.Generic;
using System.Linq;
+using Microsoft.CodeAnalysis.Sarif.Comparers;
+
namespace Microsoft.CodeAnalysis.Sarif.Visitors
{
public class SortingVisitor : SarifRewritingVisitor
{
+ // Dictionaries to cache new index to old index mappings.
private readonly IDictionary ruleIndexMap;
private readonly IDictionary artifactIndexMap;
public SortingVisitor()
{
- ruleIndexMap = new ConcurrentDictionary();
- artifactIndexMap = new ConcurrentDictionary();
+ this.ruleIndexMap = new ConcurrentDictionary();
+ this.artifactIndexMap = new ConcurrentDictionary();
}
public override SarifLog VisitSarifLog(SarifLog node)
{
SarifLog current = base.VisitSarifLog(node);
- if (node?.Runs != null)
+
+ if (current?.Runs != null)
{
- current.Runs = current.Runs.OrderBy(r => r, RunSortingComparer.Instance).ToList();
+ current.Runs = current.Runs.OrderBy(r => r, RunComparer.Instance).ToList();
}
+
return current;
}
public override Run VisitRun(Run node)
{
+ // Reset index maps for each run object.
+ this.ruleIndexMap.Clear();
+ this.artifactIndexMap.Clear();
+
if (node?.Artifacts != null)
{
- IDictionary oldIndexes = new Dictionary(capacity: node.Artifacts.Count);
- // save old indexes
- for (int i = 0; i < node.Artifacts.Count; i++)
- {
- if (!oldIndexes.ContainsKey(node.Artifacts[i]))
- {
- oldIndexes.Add(node.Artifacts[i], i);
- }
- }
-
- // sort
- node.Artifacts = node.Artifacts.OrderBy(a => a, ArtifactSortingComparer.Instance).ToList();
-
- // udpate new indexes
- for (int newIndex = 0; newIndex < node.Artifacts.Count; newIndex++)
- {
- if (oldIndexes.TryGetValue(node.Artifacts[newIndex], out int oldIndex)
- && !artifactIndexMap.TryGetValue(oldIndex, out _))
- {
- artifactIndexMap.Add(oldIndex, newIndex);
- }
- }
-
- oldIndexes.Clear();
+ node.Artifacts = this.SortAndBuildIndexMap(node?.Artifacts, ArtifactComparer.Instance, this.artifactIndexMap);
}
- // traverse child nodes first, so the child list properties should be sorted
+ // Traverse child nodes first to make sure the child properties are sorted.
Run current = base.VisitRun(node);
- // then sort properties of current node
if (current?.Results != null)
{
- current.Results = current.Results.OrderBy(r => r, ResultSortingComparer.Instance).ToList();
+ current.Results = current.Results.OrderBy(r => r, ResultComparer.Instance).ToList();
}
return current;
@@ -72,107 +57,133 @@ public override Run VisitRun(Run node)
public override ToolComponent VisitToolComponent(ToolComponent node)
{
- ToolComponent current = base.VisitToolComponent(node);
- if (current?.Rules != null)
+ if (node?.Rules != null)
{
- IDictionary oldIndexes = new Dictionary(capacity: current.Rules.Count);
- // before sort the rules, save old indexes
- for (int i = 0; i < current.Rules.Count; i++)
- {
- if (!oldIndexes.ContainsKey(current.Rules[i]))
- {
- oldIndexes.Add(current.Rules[i], i);
- }
- }
-
- // sort rules
- current.Rules = current.Rules.OrderBy(r => r, ReportingDescriptorSortingComparer.Instance).ToList();
-
- // udpate new indexes
- for (int newIndex = 0; newIndex < current.Rules.Count; newIndex++)
- {
- if (oldIndexes.TryGetValue(current.Rules[newIndex], out int oldIndex)
- && !ruleIndexMap.TryGetValue(oldIndex, out _))
- {
- ruleIndexMap.Add(oldIndex, newIndex);
- }
- }
-
- oldIndexes.Clear();
+ node.Rules = this.SortAndBuildIndexMap(node?.Rules, ReportingDescriptorComparer.Instance, this.ruleIndexMap);
}
- return current;
+
+ return base.VisitToolComponent(node);
}
public override Result VisitResult(Result node)
{
Result current = base.VisitResult(node);
+
if (current != null)
{
- // update old index to new index
- if (current.RuleIndex != -1
- && ruleIndexMap.TryGetValue(current.RuleIndex, out int newIndex))
+ if (current.RuleIndex != -1 && this.ruleIndexMap.TryGetValue(current.RuleIndex, out int newIndex))
{
current.RuleIndex = newIndex;
}
if (current.Locations != null)
{
- current.Locations = current.Locations.OrderBy(r => r, LocationSortingComparer.Instance).ToList();
+ current.Locations = current.Locations.OrderBy(r => r, LocationComparer.Instance).ToList();
}
+
if (current.CodeFlows != null)
{
- current.CodeFlows = current.CodeFlows.OrderBy(r => r, CodeFlowSortingComparer.Instance).ToList();
+ current.CodeFlows = current.CodeFlows.OrderBy(r => r, CodeFlowComparer.Instance).ToList();
}
}
+
return current;
}
public override CodeFlow VisitCodeFlow(CodeFlow node)
{
CodeFlow current = base.VisitCodeFlow(node);
+
if (current?.ThreadFlows != null)
{
- current.ThreadFlows = current.ThreadFlows.OrderBy(t => t, ThreadFlowSortingComparer.Instance).ToList();
+ current.ThreadFlows = current.ThreadFlows.OrderBy(t => t, ThreadFlowComparer.Instance).ToList();
}
+
return current;
}
public override ThreadFlow VisitThreadFlow(ThreadFlow node)
{
ThreadFlow current = base.VisitThreadFlow(node);
+
if (current?.Locations != null)
{
- current.Locations = current.Locations.OrderBy(t => t, ThreadFlowLocationSortingComparer.Instance).ToList();
+ current.Locations = current.Locations.OrderBy(t => t, ThreadFlowLocationComparer.Instance).ToList();
}
- return current;
- }
- public override ThreadFlowLocation VisitThreadFlowLocation(ThreadFlowLocation node)
- {
- return base.VisitThreadFlowLocation(node);
+ return current;
}
public override Location VisitLocation(Location node)
{
Location current = base.VisitLocation(node);
+
if (current?.LogicalLocations != null)
{
- current.LogicalLocations = current.LogicalLocations.OrderBy(t => t, LogicalLocationSortingComparer.Instance).ToList();
+ current.LogicalLocations = current.LogicalLocations.OrderBy(t => t, LogicalLocationComparer.Instance).ToList();
}
+
return current;
}
public override ArtifactLocation VisitArtifactLocation(ArtifactLocation node)
{
ArtifactLocation current = base.VisitArtifactLocation(node);
- // update old index to new index
- if (current.Index != -1
- && artifactIndexMap.TryGetValue(current.Index, out int newIndex))
+
+ if (current.Index != -1 && this.artifactIndexMap.TryGetValue(current.Index, out int newIndex))
{
current.Index = newIndex;
}
return current;
}
+
+ private IList SortAndBuildIndexMap(IList list, IComparer comparer, IDictionary indexMapping)
+ {
+ if (list != null)
+ {
+ IDictionary unsortedIndices = this.CacheListIndices(list);
+
+ list = list.OrderBy(r => r, comparer).ToList();
+
+ this.MapNewIndices(list, unsortedIndices, indexMapping);
+
+ unsortedIndices.Clear();
+ }
+
+ return list;
+ }
+
+ private IDictionary CacheListIndices(IList list)
+ {
+ // Assume each item in the list is unique (has different reference).
+ // According to sarif-2.1.0-rtm.5.json, artifacts array of runs and rules array of toolComponent
+ // are defined as "uniqueItems".
+ var dict = new Dictionary(capacity: list.Count);
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ // If some objects are same (with same reference), keep only 1 object
+ // in the dictionary and set of indices in hash set.
+ if (!dict.ContainsKey(list[i]))
+ {
+ dict.Add(list[i], i);
+ }
+ }
+
+ return dict;
+ }
+
+ private void MapNewIndices(IList newList, IDictionary oldIndices, IDictionary indexMapping)
+ {
+ for (int newIndex = 0; newIndex < newList.Count; newIndex++)
+ {
+ if (oldIndices.TryGetValue(newList[newIndex], out int oldIndex) &&
+ !indexMapping.ContainsKey(oldIndex))
+ {
+ indexMapping.Add(oldIndex, newIndex);
+ }
+ }
+ }
}
}
diff --git a/src/Test.UnitTests.Sarif/Comparers/ComparersTests.cs b/src/Test.UnitTests.Sarif/Comparers/ComparersTests.cs
new file mode 100644
index 000000000..94f5c9b2b
--- /dev/null
+++ b/src/Test.UnitTests.Sarif/Comparers/ComparersTests.cs
@@ -0,0 +1,668 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using FluentAssertions;
+
+using Microsoft.CodeAnalysis.Sarif;
+using Microsoft.CodeAnalysis.Sarif.Comparers;
+using Microsoft.CodeAnalysis.Test.Utilities.Sarif;
+
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.CodeAnalysis.Test.UnitTests.Sarif.Comparers
+{
+ public class ComparersTests
+ {
+ private readonly Random random;
+
+ public ComparersTests(ITestOutputHelper outputHelper)
+ {
+ this.random = RandomSarifLogGenerator.GenerateRandomAndLog(outputHelper);
+ }
+
+ [Fact]
+ public void CompareList__Shuffle_Tests()
+ {
+ IList originalList = Enumerable.Range(-100, 200).ToList();
+
+ IList shuffledList = originalList.ToList().Shuffle(this.random);
+
+ int result = 0, newResult = 0;
+
+ result = ComparerHelper.CompareList(originalList, shuffledList);
+ result.Should().NotBe(0);
+
+ newResult = ComparerHelper.CompareList(shuffledList, originalList);
+ newResult.Should().Be(result * -1);
+
+ IList sortedList = shuffledList.OrderBy(i => i).ToList();
+
+ result = ComparerHelper.CompareList(originalList, sortedList);
+ result.Should().Be(0);
+
+ newResult = ComparerHelper.CompareList(originalList, sortedList);
+ newResult.Should().Be(0);
+ }
+
+ [Fact]
+ public void CompareList_BothNull_Tests()
+ {
+ IList list1 = null;
+ IList list2 = null;
+ ComparerHelper.CompareList(list1, list2).Should().Be(0);
+ ComparerHelper.CompareList(list2, list1).Should().Be(0);
+ }
+
+ [Fact]
+ public void CompareList_CompareNullToNotNull_Tests()
+ {
+ IList list1 = null;
+ IList list2 = Enumerable.Range(-10, 20).ToList();
+ ComparerHelper.CompareList(list1, list2).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1).Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareList_DifferentCount_Tests()
+ {
+ IList list1 = Enumerable.Range(0, 11).ToList();
+ IList list2 = Enumerable.Range(0, 10).ToList();
+ ComparerHelper.CompareList(list1, list2).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1).Should().Be(-1);
+ }
+
+ [Fact]
+ public void CompareList_SameCountDifferentElement_Tests()
+ {
+ IList list1 = Enumerable.Range(0, 10).ToList();
+ IList list2 = Enumerable.Range(1, 10).ToList();
+ ComparerHelper.CompareList(list1, list2).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1).Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareList_WithNullComparer_Tests()
+ {
+ ToolComponent tool = new ToolComponent { Guid = Guid.Empty.ToString() };
+
+ IList runs1 = new[] { new Run { Tool = new Tool { Driver = tool } } };
+ IList runs2 = Array.Empty();
+
+ Action act = () => ComparerHelper.CompareList(runs1, runs2, comparer: null);
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void CompareList_WithComparer_Tests()
+ {
+ IList run1 = new List();
+ run1.Add(null);
+ run1.Add(new Run { Tool = new Tool { Driver = new ToolComponent { Guid = Guid.NewGuid().ToString() } } });
+
+ IList run2 = new List();
+ run2.Add(new Run { Tool = new Tool { Driver = new ToolComponent { Guid = Guid.NewGuid().ToString() } } });
+ run2.Add(null);
+
+ int result = ComparerHelper.CompareList(run1, run2, RunComparer.Instance);
+ result.Should().Be(-1);
+
+ result = ComparerHelper.CompareList(run2, run1, RunComparer.Instance);
+ result.Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareDictionary_Shuffle_Tests()
+ {
+ IDictionary dict1 = new Dictionary();
+ dict1.Add("a", "a");
+ dict1.Add("b", "b");
+ dict1.Add("c", "c");
+
+ IDictionary dict2 = new Dictionary();
+ dict2.Add("b", "b");
+ dict2.Add("c", "c");
+ dict2.Add("a", "a");
+
+ int result = ComparerHelper.CompareDictionary(dict1, dict2);
+ result.Should().Be(0);
+
+ dict2["c"] = "d";
+
+ result = ComparerHelper.CompareDictionary(dict1, dict2);
+ result.Should().Be(-1);
+
+ result = ComparerHelper.CompareDictionary(dict2, dict1);
+ result.Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareDictionary_BothNull_Tests()
+ {
+ IDictionary list1 = null;
+ IDictionary list2 = null;
+ ComparerHelper.CompareDictionary(list1, list2).Should().Be(0);
+ ComparerHelper.CompareDictionary(list2, list1).Should().Be(0);
+ }
+
+ [Fact]
+ public void CompareDictionary_CompareNullToNotNull_Tests()
+ {
+ IDictionary list1 = null;
+ IDictionary list2 = new Dictionary()
+ { { "a", "a" } };
+ ComparerHelper.CompareDictionary(list1, list2).Should().Be(-1);
+ ComparerHelper.CompareDictionary(list2, list1).Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareDictionary_DifferentCount_Tests()
+ {
+ IDictionary list1 = new Dictionary()
+ { { "a", "a" }, { "b", "b" } };
+ IDictionary list2 = new Dictionary()
+ { { "c", "c" } };
+ ComparerHelper.CompareDictionary(list1, list2).Should().Be(1);
+ ComparerHelper.CompareDictionary(list2, list1).Should().Be(-1);
+ }
+
+ [Fact]
+ public void CompareDictionary_SameCountDifferentElement_Tests()
+ {
+ IDictionary list1 = new Dictionary()
+ { { "a", "a" }, { "b", "b" } };
+ IDictionary list2 = new Dictionary()
+ { { "c", "c" }, { "d", "d" } };
+ ComparerHelper.CompareDictionary(list1, list2).Should().Be(-1);
+ ComparerHelper.CompareDictionary(list2, list1).Should().Be(1);
+ }
+
+ [Fact]
+ public void CompareDictionary_WithNullComparer_Tests()
+ {
+ IDictionary loc1 = new Dictionary();
+ IDictionary loc2 = new Dictionary();
+
+ Action act = () => ComparerHelper.CompareDictionary(loc1, loc2, comparer: null);
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void CompareDictionary_WithComparer_Tests()
+ {
+ IDictionary loc1 = new Dictionary();
+ loc1.Add("1", null);
+ loc1.Add("2", new Location { Id = 2 });
+ IDictionary loc2 = new Dictionary();
+ loc2.Add("1", new Location { Id = 1 });
+ loc2.Add("2", new Location { Id = 2 });
+
+ int result = ComparerHelper.CompareDictionary(loc1, loc2, LocationComparer.Instance);
+ result.Should().Be(-1);
+
+ result = ComparerHelper.CompareDictionary(loc2, loc1, LocationComparer.Instance);
+ result.Should().Be(1);
+ }
+
+ [Fact]
+ public void ArtifactContentComparer_Tests()
+ {
+ var list1 = new List();
+ var list2 = new List();
+
+ list1.Add(null);
+ list2.Add(null);
+ ComparerHelper.CompareList(list1, list2, ArtifactContentComparer.Instance).Should().Be(0);
+
+ list1.Add(new ArtifactContent() { Text = "content 1" });
+ list2.Add(new ArtifactContent() { Text = "content 2" });
+
+ ComparerHelper.CompareList(list1, list2, ArtifactContentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ArtifactContentComparer.Instance).Should().Be(1);
+ list1.Clear();
+ list2.Clear();
+
+ list1.Add(new ArtifactContent() { Binary = "WUJDMTIz" });
+ list2.Add(new ArtifactContent() { Binary = "QUJDMTIz" });
+
+ ComparerHelper.CompareList(list1, list2, ArtifactContentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ArtifactContentComparer.Instance).Should().Be(-1);
+ list1.Clear();
+ list2.Clear();
+
+ list1.Add(new ArtifactContent() { Text = "content 1", Rendered = new MultiformatMessageString { Markdown = "`markdown`" } });
+ list2.Add(new ArtifactContent() { Text = "content 1", Rendered = new MultiformatMessageString { Markdown = "title" } });
+ ComparerHelper.CompareList(list1, list2, ArtifactContentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ArtifactContentComparer.Instance).Should().Be(1);
+ }
+
+ [Fact]
+ public void ReportingConfigurationComparer_Tests()
+ {
+ var rules1 = new List();
+ var rules2 = new List();
+
+ rules1.Add(null);
+ rules2.Add(null);
+ ComparerHelper.CompareList(rules1, rules2, ReportingConfigurationComparer.Instance).Should().Be(0);
+
+ rules1.Add(new ReportingConfiguration() { Rank = 26.648d });
+ rules2.Add(new ReportingConfiguration() { Rank = 87.1d });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingConfigurationComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingConfigurationComparer.Instance).Should().Be(1);
+
+
+ rules1.Insert(0, new ReportingConfiguration() { Level = FailureLevel.Error });
+ rules2.Insert(0, new ReportingConfiguration() { Level = FailureLevel.Warning });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingConfigurationComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingConfigurationComparer.Instance).Should().Be(-1);
+
+ rules1.Insert(0, new ReportingConfiguration() { Enabled = false, Rank = 80d });
+ rules2.Insert(0, new ReportingConfiguration() { Enabled = true, Rank = 80d });
+ ComparerHelper.CompareList(rules1, rules2, ReportingConfigurationComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingConfigurationComparer.Instance).Should().Be(1);
+ }
+
+ [Fact]
+ public void ToolComponentComparer_Tests()
+ {
+ var list1 = new List();
+ var list2 = new List();
+
+ list1.Add(null);
+ list2.Add(null);
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(0);
+
+ list1.Insert(0, new ToolComponent() { Guid = Guid.Empty.ToString() });
+ list2.Insert(0, new ToolComponent() { Guid = Guid.NewGuid().ToString() });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(1);
+
+
+ list1.Insert(0, new ToolComponent() { Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { Name = "code scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(-1);
+
+ list1.Insert(0, new ToolComponent() { Organization = "MS", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { Organization = "Microsoft", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(-1);
+
+ list1.Insert(0, new ToolComponent() { Product = "PREfast", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { Product = "prefast", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(-1);
+
+ list1.Insert(0, new ToolComponent() { FullName = "Analysis Linter", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { FullName = "Analysis Linter Tool", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(1);
+
+ list1.Insert(0, new ToolComponent() { Version = "CWR-2022-01", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { Version = "CWR-2021-12", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(-1);
+
+ list1.Insert(0, new ToolComponent() { SemanticVersion = "1.0.1", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { SemanticVersion = "1.0.3", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(1);
+
+ list1.Insert(0, new ToolComponent() { ReleaseDateUtc = "2/8/2022", Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { ReleaseDateUtc = "1/1/2022", Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(-1);
+
+ list1.Insert(0, new ToolComponent() { DownloadUri = new Uri("https://example/download/v1"), Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { DownloadUri = new Uri("https://example/download/v2"), Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(1);
+
+ list1.Insert(0, new ToolComponent() { Rules = new ReportingDescriptor[] { new ReportingDescriptor { Id = "TESTRULE001" } }, Name = "scan tool" });
+ list2.Insert(0, new ToolComponent() { Rules = new ReportingDescriptor[] { new ReportingDescriptor { Id = "TESTRULE002" } }, Name = "scan tool" });
+
+ ComparerHelper.CompareList(list1, list2, ToolComponentComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(list2, list1, ToolComponentComparer.Instance).Should().Be(1);
+ }
+
+ [Fact]
+ public void ReportingDescriptorComparer_Tests()
+ {
+ var rules1 = new List();
+ var rules2 = new List();
+
+ rules1.Add(null);
+ rules2.Add(null);
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(0);
+
+ rules1.Insert(0, new ReportingDescriptor() { Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { Id = "TestRule2" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(1);
+
+
+ rules1.Insert(0, new ReportingDescriptor() { DeprecatedIds = new string[] { "OldRuleId3" }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { DeprecatedIds = new string[] { "OldRuleId2" }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(-1);
+
+ rules1.Insert(0, new ReportingDescriptor() { Guid = Guid.NewGuid().ToString(), Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { Guid = Guid.Empty.ToString(), Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(-1);
+
+ rules1.Insert(0, new ReportingDescriptor() { DeprecatedIds = new string[] { Guid.Empty.ToString() }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { DeprecatedIds = new string[] { Guid.NewGuid().ToString() }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(1);
+
+ rules1.Insert(0, new ReportingDescriptor() { Name = "UnusedVariable", Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { Name = "", Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(-1);
+
+ rules1.Insert(0, new ReportingDescriptor() { ShortDescription = new MultiformatMessageString { Text = "Remove unused variable" }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { ShortDescription = new MultiformatMessageString { Text = "Wrong description" }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(1);
+
+ rules1.Insert(0, new ReportingDescriptor() { FullDescription = new MultiformatMessageString { Text = "Remove unused variable" }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { FullDescription = new MultiformatMessageString { Text = "Wrong description" }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(1);
+
+ rules1.Insert(0, new ReportingDescriptor() { DefaultConfiguration = new ReportingConfiguration { Level = FailureLevel.Note }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { DefaultConfiguration = new ReportingConfiguration { Level = FailureLevel.Error }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(1);
+
+ rules1.Insert(0, new ReportingDescriptor() { HelpUri = new Uri("http://example.net/rule/id"), Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { HelpUri = new Uri("http://example.net"), Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(-1);
+
+ rules1.Insert(0, new ReportingDescriptor() { Help = new MultiformatMessageString { Text = "Helping texts." }, Id = "TestRule1" });
+ rules2.Insert(0, new ReportingDescriptor() { Help = new MultiformatMessageString { Text = "For customers." }, Id = "TestRule1" });
+
+ ComparerHelper.CompareList(rules1, rules2, ReportingDescriptorComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(rules2, rules1, ReportingDescriptorComparer.Instance).Should().Be(-1);
+ }
+
+ [Fact]
+ public void RegionComparer_Tests()
+ {
+ var regions1 = new List();
+ var regions2 = new List();
+
+ regions1.Add(null);
+ regions2.Add(null);
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(0);
+
+ regions1.Insert(0, new Region() { StartLine = 0, StartColumn = 0 });
+ regions2.Insert(0, new Region() { StartLine = 1, StartColumn = 0 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(1);
+
+ regions1.Insert(0, new Region() { StartLine = 0, StartColumn = 1 });
+ regions2.Insert(0, new Region() { StartLine = 0, StartColumn = 0 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(-1);
+
+ regions1.Insert(0, new Region() { StartLine = 10, EndLine = 11, StartColumn = 0 });
+ regions2.Insert(0, new Region() { StartLine = 10, EndLine = 10, StartColumn = 0 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(-1);
+
+ regions1.Insert(0, new Region() { StartLine = 10, EndLine = 10, StartColumn = 5, EndColumn = 23 });
+ regions2.Insert(0, new Region() { StartLine = 10, EndLine = 10, StartColumn = 5, EndColumn = 7 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(-1);
+
+ regions1.Insert(0, new Region() { CharOffset = 100, CharLength = 30 });
+ regions2.Insert(0, new Region() { CharOffset = 36, CharLength = 30 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(-1);
+
+ regions1.Insert(0, new Region() { CharOffset = 100, CharLength = 47 });
+ regions2.Insert(0, new Region() { CharOffset = 100, CharLength = 326 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(1);
+
+ regions1.Insert(0, new Region() { ByteOffset = 226, ByteLength = 11 });
+ regions2.Insert(0, new Region() { ByteOffset = 1623, ByteLength = 11 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(1);
+
+ regions1.Insert(0, new Region() { ByteOffset = 67, ByteLength = 9 });
+ regions2.Insert(0, new Region() { CharOffset = 67, ByteLength = 11 });
+
+ ComparerHelper.CompareList(regions1, regions2, RegionComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(regions2, regions1, RegionComparer.Instance).Should().Be(1);
+ }
+
+ [Fact]
+ public void ArtifactComparer_Tests()
+ {
+ var artifacts1 = new List();
+ var artifacts2 = new List();
+
+ artifacts1.Add(null);
+ artifacts2.Add(null);
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(0);
+
+ artifacts1.Insert(0, new Artifact() { Description = new Message { Text = "Represents for an artifact" } });
+ artifacts2.Insert(0, new Artifact() { Description = new Message { Text = "A source file artifact" } });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(-1);
+
+ artifacts1.Insert(0, new Artifact() { Location = new ArtifactLocation { Index = 0 } });
+ artifacts2.Insert(0, new Artifact() { Location = new ArtifactLocation { Index = 1 } });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { ParentIndex = 0 });
+ artifacts2.Insert(0, new Artifact() { ParentIndex = 1 });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { Offset = 2 });
+ artifacts2.Insert(0, new Artifact() { Offset = 1 });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(-1);
+
+ artifacts1.Insert(0, new Artifact() { Length = 102542 });
+ artifacts2.Insert(0, new Artifact() { Length = -1 });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(-1);
+
+ artifacts1.Insert(0, new Artifact() { Roles = ArtifactRoles.AnalysisTarget | ArtifactRoles.Attachment });
+ artifacts2.Insert(0, new Artifact() { Roles = ArtifactRoles.Policy });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { MimeType = "text" });
+ artifacts2.Insert(0, new Artifact() { MimeType = "video" });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { Contents = new ArtifactContent { Text = "\"string\"" } });
+ artifacts2.Insert(0, new Artifact() { Contents = new ArtifactContent { Text = "var result = 0;" } });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { Encoding = "UTF-16BE" });
+ artifacts2.Insert(0, new Artifact() { Encoding = "UTF-16LE" });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { SourceLanguage = "html" });
+ artifacts2.Insert(0, new Artifact() { SourceLanguage = "csharp/7" });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(-1);
+
+ artifacts1.Insert(0, new Artifact() { Hashes = new Dictionary { { "sha-256", "..." } } });
+ artifacts2.Insert(0, new Artifact() { Hashes = new Dictionary { { "sha-512", "..." } } });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(1);
+
+ artifacts1.Insert(0, new Artifact() { LastModifiedTimeUtc = DateTime.UtcNow });
+ artifacts2.Insert(0, new Artifact() { LastModifiedTimeUtc = DateTime.UtcNow.AddDays(-1) });
+
+ ComparerHelper.CompareList(artifacts1, artifacts2, ArtifactComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(artifacts2, artifacts1, ArtifactComparer.Instance).Should().Be(-1);
+ }
+
+ [Fact]
+ public void ThreadFlowLocationComparer_Tests()
+ {
+ var locations1 = new List();
+ var locations2 = new List();
+
+ locations1.Add(null);
+ locations2.Add(null);
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(0);
+
+ Location loc1 = new Location
+ {
+ PhysicalLocation = new PhysicalLocation
+ {
+ ArtifactLocation = new ArtifactLocation
+ {
+ Uri = new Uri("path/to/file1.c", UriKind.Relative)
+ }
+ }
+ };
+
+ Location loc2 = new Location
+ {
+ PhysicalLocation = new PhysicalLocation
+ {
+ ArtifactLocation = new ArtifactLocation
+ {
+ Uri = new Uri("path/to/file2.c", UriKind.Relative)
+ }
+ }
+ };
+
+ locations1.Insert(0, new ThreadFlowLocation() { Location = loc1 });
+ locations2.Insert(0, new ThreadFlowLocation() { Location = loc2 });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { Index = 2, Location = loc1 });
+ locations2.Insert(0, new ThreadFlowLocation() { Index = 1, Location = loc2 });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { Kinds = new string[] { "memory" }, Location = loc1 });
+ locations2.Insert(0, new ThreadFlowLocation() { Kinds = new string[] { "call", "branch" }, Location = loc2 });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { NestingLevel = 3 });
+ locations2.Insert(0, new ThreadFlowLocation() { NestingLevel = 2 });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { ExecutionOrder = 2 });
+ locations2.Insert(0, new ThreadFlowLocation() { ExecutionOrder = 1 });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { ExecutionTimeUtc = DateTime.UtcNow });
+ locations2.Insert(0, new ThreadFlowLocation() { ExecutionTimeUtc = DateTime.UtcNow.AddHours(-2) });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+
+ locations1.Insert(0, new ThreadFlowLocation() { Importance = ThreadFlowLocationImportance.Essential });
+ locations2.Insert(0, new ThreadFlowLocation() { Importance = ThreadFlowLocationImportance.Unimportant });
+
+ ComparerHelper.CompareList(locations1, locations2, ThreadFlowLocationComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(locations2, locations1, ThreadFlowLocationComparer.Instance).Should().Be(1);
+ }
+
+ [Fact]
+ public void RunComparer_Tests()
+ {
+ var runs1 = new List();
+ var runs2 = new List();
+
+ runs1.Add(null);
+ runs2.Add(null);
+ ComparerHelper.CompareList(runs1, runs2, RunComparer.Instance).Should().Be(0);
+
+ runs1.Insert(0, new Run() { Artifacts = new Artifact[] { new Artifact { Description = new Message { Text = "artifact 1" } } } });
+ runs2.Insert(0, new Run() { Artifacts = new Artifact[] { new Artifact { Description = new Message { Text = "artifact 2" } } } });
+
+ ComparerHelper.CompareList(runs1, runs2, RunComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(runs2, runs1, RunComparer.Instance).Should().Be(1);
+
+ Tool tool1 = new Tool { Driver = new ToolComponent { Name = "PREFast", Version = "1.0" } };
+ Tool tool2 = new Tool { Driver = new ToolComponent { Name = "PREFast", Version = "1.3" } };
+
+ runs1.Insert(0, new Run() { Tool = tool1 });
+ runs2.Insert(0, new Run() { Tool = tool2 });
+
+ ComparerHelper.CompareList(runs1, runs2, RunComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(runs2, runs1, RunComparer.Instance).Should().Be(1);
+
+ Result result1 = new Result { RuleId = "CS001", Message = new Message { Text = "Issue of C# code" } };
+ Result result2 = new Result { RuleId = "IDE692", Message = new Message { Text = "Issue by IDE" } };
+
+ runs1.Insert(0, new Run() { Results = new Result[] { result1 } });
+ runs2.Insert(0, new Run() { Results = new Result[] { result2 } });
+
+ ComparerHelper.CompareList(runs1, runs2, RunComparer.Instance).Should().Be(-1);
+ ComparerHelper.CompareList(runs2, runs1, RunComparer.Instance).Should().Be(1);
+ }
+ }
+}
diff --git a/src/Test.UnitTests.Sarif/RandomSarifLogGenerator.cs b/src/Test.UnitTests.Sarif/RandomSarifLogGenerator.cs
index 0d77102ae..d94cb7422 100644
--- a/src/Test.UnitTests.Sarif/RandomSarifLogGenerator.cs
+++ b/src/Test.UnitTests.Sarif/RandomSarifLogGenerator.cs
@@ -29,7 +29,7 @@ public static Random GenerateRandomAndLog(ITestOutputHelper output, [CallerMembe
return random;
}
- public static SarifLog GenerateSarifLogWithRuns(Random randomGen, int runCount, int? resultCount = null)
+ public static SarifLog GenerateSarifLogWithRuns(Random randomGen, int runCount, int? resultCount = null, RandomDataFields dataFields = RandomDataFields.None)
{
SarifLog log = new SarifLog();
@@ -40,13 +40,13 @@ public static SarifLog GenerateSarifLogWithRuns(Random randomGen, int runCount,
for (int i = 0; i < runCount; i++)
{
- log.Runs.Add(GenerateRandomRun(randomGen, resultCount));
+ log.Runs.Add(GenerateRandomRun(randomGen, resultCount, dataFields));
}
return log;
}
- public static Run GenerateRandomRun(Random random, int? resultCount = null)
+ public static Run GenerateRandomRun(Random random, int? resultCount = null, RandomDataFields dataFields = RandomDataFields.None)
{
List ruleIds = new List() { "TEST001", "TEST002", "TEST003", "TEST004", "TEST005" };
List filePaths = GenerateFakeFiles(GeneratorBaseUri, random.Next(20) + 1).Select(a => new Uri(a)).ToList();
@@ -64,7 +64,7 @@ public static Run GenerateRandomRun(Random random, int? resultCount = null)
}
},
Artifacts = GenerateFiles(filePaths),
- Results = GenerateFakeResults(random, ruleIds, filePaths, results)
+ Results = GenerateFakeResults(random, ruleIds, filePaths, results, dataFields)
};
}
@@ -96,7 +96,7 @@ public static IEnumerable GenerateFakeFiles(string baseAddress, int coun
return results;
}
- public static IList GenerateFakeResults(Random random, List ruleIds, List filePaths, int resultCount)
+ public static IList GenerateFakeResults(Random random, List ruleIds, List filePaths, int resultCount, RandomDataFields dataFields = RandomDataFields.None)
{
List results = new List();
for (int i = 0; i < resultCount; i++)
@@ -118,9 +118,15 @@ public static IList GenerateFakeResults(Random random, List rule
Uri = filePaths[fileIndex],
Index = fileIndex
},
- }
+ },
+ LogicalLocations = dataFields.HasFlag(RandomDataFields.LogicalLocation) ?
+ GenerateLogicalLocations(random) :
+ null,
}
- }
+ },
+ CodeFlows = dataFields.HasFlag(RandomDataFields.CodeFlow) ?
+ GenerateCodeFlows(random, filePaths, dataFields) :
+ null,
});
}
return results;
@@ -161,5 +167,92 @@ public static IList GenerateRules(List ruleIds)
}
return rules;
}
+
+ public static IList GenerateCodeFlows(Random random, IList artifacts, RandomDataFields dataFields)
+ {
+ if (artifacts?.Any() != true)
+ {
+ return null;
+ }
+
+ var codeFlow = new CodeFlow
+ {
+ Message = new Message { Text = "code flow message" },
+ ThreadFlows = new[]
+ {
+ new ThreadFlow
+ {
+ Message = new Message { Text = "thread flow message" },
+ Locations = dataFields.HasFlag(RandomDataFields.ThreadFlow) ?
+ GenerateThreadFlowLocations(random, artifacts) :
+ null,
+ },
+ },
+ };
+
+ return new[] { codeFlow };
+ }
+
+ public static IList GenerateThreadFlowLocations(Random random, IList artifacts)
+ {
+ var locations = new List();
+
+ for (int i = 0; i < random.Next(10); i++)
+ {
+ locations.Add(new ThreadFlowLocation
+ {
+ Importance = RandomEnumValue(random),
+ Location = new Location
+ {
+ PhysicalLocation = new PhysicalLocation
+ {
+ ArtifactLocation = new ArtifactLocation
+ {
+ Index = random.Next(artifacts.Count),
+ },
+ Region = new Region
+ {
+ StartLine = random.Next(500),
+ StartColumn = random.Next(100),
+ },
+ },
+ },
+ });
+ }
+
+ return locations;
+ }
+
+ public static IList GenerateLogicalLocations(Random random)
+ {
+ var logicalLocations = new List();
+ for (int i = 0; i < random.Next(5); i++)
+ {
+ logicalLocations.Add(new LogicalLocation
+ {
+ Name = $"Class{i}",
+ Index = i,
+ FullyQualifiedName = "namespaceA::namespaceB::namespaceC",
+ Kind = LogicalLocationKind.Type,
+ });
+ }
+
+ return logicalLocations;
+ }
+
+ public static T RandomEnumValue(Random random) where T : Enum
+ {
+ Array enums = Enum.GetValues(typeof(T));
+ return (T)enums.GetValue(random.Next(enums.Length));
+ }
+ }
+
+ [Flags]
+ public enum RandomDataFields
+ {
+ None = 0,
+ CodeFlow = 0b1,
+ ThreadFlow = 0b10,
+ LogicalLocation = 0b100,
}
}
diff --git a/src/Test.UnitTests.Sarif/Visitors/SortingVisitorTests.cs b/src/Test.UnitTests.Sarif/Visitors/SortingVisitorTests.cs
index 6a5015c96..ea9163ff7 100644
--- a/src/Test.UnitTests.Sarif/Visitors/SortingVisitorTests.cs
+++ b/src/Test.UnitTests.Sarif/Visitors/SortingVisitorTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Configuration;
using System.Linq;
using FluentAssertions;
@@ -22,34 +23,19 @@ public class SortingVisitorTests
public SortingVisitorTests(ITestOutputHelper outputHelper)
{
- random = RandomSarifLogGenerator.GenerateRandomAndLog(outputHelper);
+ this.random = RandomSarifLogGenerator.GenerateRandomAndLog(outputHelper);
}
[Fact]
public void SortingVisitor_ShuffleTest()
{
bool areEqual;
- // create a test sarif log
- SarifLog originalLog = CreateTestSarifLog(this.random);
-
- SarifLog shuffledLog1 = originalLog.DeepClone();
- SarifLog shuffledLog2 = originalLog.DeepClone();
-
- // original log and cloned log should be same
- areEqual = SarifLogEqualityComparer.Instance.Equals(originalLog, shuffledLog1);
- areEqual.Should().BeTrue();
-
- areEqual = SarifLogEqualityComparer.Instance.Equals(originalLog, shuffledLog2);
- areEqual.Should().BeTrue();
-
- areEqual = SarifLogEqualityComparer.Instance.Equals(shuffledLog1, shuffledLog2);
- areEqual.Should().BeTrue();
- // shuffle sarif log
- ShuffleSarifLog(shuffledLog1, this.random);
- ShuffleSarifLog(shuffledLog2, this.random);
+ SarifLog originalLog = CreateTestSarifLog(this.random);
+ SarifLog shuffledLog1 = ShuffleSarifLog(originalLog, this.random);
+ SarifLog shuffledLog2 = ShuffleSarifLog(originalLog, this.random);
- // shuffled logs should be not same as each other and original log.
+ // Shuffled logs should be not same as each other and original log.
areEqual = SarifLogEqualityComparer.Instance.Equals(originalLog, shuffledLog1);
areEqual.Should().BeFalse();
@@ -59,16 +45,13 @@ public void SortingVisitor_ShuffleTest()
areEqual = SarifLogEqualityComparer.Instance.Equals(shuffledLog1, shuffledLog2);
areEqual.Should().BeFalse();
- // sort shuffled logs using visitor
SarifLog sortedLog1 = new SortingVisitor().VisitSarifLog(shuffledLog1);
SarifLog sortedLog2 = new SortingVisitor().VisitSarifLog(shuffledLog2);
- // verify sorted logs should be same (deterministic)
- // sorted logs may not be same with original log due to random values
areEqual = SarifLogEqualityComparer.Instance.Equals(sortedLog1, sortedLog2);
areEqual.Should().BeTrue();
- // make sure result's ruleIndex points to right rule in sorted log
+ // Make sure result's ruleIndex points to right rule in sorted log.
IList rules = sortedLog1.Runs.First().Tool.Driver.Rules;
foreach (Result result in sortedLog1.Runs.First().Results.Where(r => r.RuleIndex != -1))
{
@@ -76,7 +59,7 @@ public void SortingVisitor_ShuffleTest()
result.RuleIndex.Should().Be(ruleIndex);
}
- // make sure artifactLocation index points to right artifacts
+ // Make sure artifactLocation index points to right artifacts.
IList artifacts = sortedLog1.Runs.First().Artifacts;
foreach (Result result in sortedLog1.Runs.First().Results)
{
@@ -91,9 +74,8 @@ public void SortingVisitor_ShuffleTest()
[Fact]
public void SortingVisitor_NullEmptyListTests()
{
- // arrange
- // create a log with all results have same value.
- SarifLog sarifLog = new SarifLog
+ // Create a log with all results have same values.
+ var sarifLog = new SarifLog
{
Runs = new[]
{
@@ -118,19 +100,16 @@ public void SortingVisitor_NullEmptyListTests()
},
};
- // setup results
IList results = sarifLog.Runs[0].Results;
results[0].Locations = new[] { new Location { Message = new Message { Text = "test location" } } };
results[1].Locations = null;
results[2].Locations = new List();
- // act
SarifLog sortedLog = new SortingVisitor().VisitSarifLog(sarifLog);
- // assert
- // if collection elements all values are same expect a child list
+ // If sorting a collection with element has a list type property
// the order should depend on list values.
- // expected order: null < empty < collection has element
+ // Expected order: null < empty < collection has element.
results = sortedLog.Runs[0].Results;
results[0].Locations.Should().BeNull();
results[1].Locations.Should().BeEmpty();
@@ -139,168 +118,116 @@ public void SortingVisitor_NullEmptyListTests()
private static SarifLog CreateTestSarifLog(Random random)
{
- SarifLog sarifLog = RandomSarifLogGenerator.GenerateSarifLogWithRuns(random, runCount: 1, resultCount: random.Next(100));
-
- CreateCodeFlows(sarifLog.Runs?.First()?.Results, sarifLog.Runs?.First()?.Artifacts, random);
+ SarifLog sarifLog = RandomSarifLogGenerator.GenerateSarifLogWithRuns(
+ randomGen: random,
+ runCount: random.Next(1, 5),
+ dataFields: RandomDataFields.CodeFlow | RandomDataFields.ThreadFlow | RandomDataFields.LogicalLocation);
return sarifLog;
}
- private static void CreateCodeFlows(IList results, IList artifacts, Random random)
+ private static SarifLog ShuffleSarifLog(SarifLog originalLog, Random random)
{
- if (results?.Any() != true || artifacts?.Any() != true)
- {
- return;
- }
+ SarifLog logToBeShuffled = originalLog.DeepClone();
- foreach (Result result in results)
+ bool areEqual = SarifLogEqualityComparer.Instance.Equals(originalLog, logToBeShuffled);
+ areEqual.Should().BeTrue();
+
+ // Shuffle the log cloned from original log until
+ // find the log is different than original log.
+ do
{
- result.CodeFlows = new[]
+ foreach (Run run in logToBeShuffled?.Runs)
{
- new CodeFlow
+ IList rules = run?.Tool?.Driver?.Rules;
+ IList results = run?.Results;
+ IList artifacts = run?.Artifacts;
+
+ if (rules != null)
{
- ThreadFlows = new[]
+ IDictionary ruleIndexMapping = new Dictionary();
+
+ rules = rules.Shuffle(random);
+ run.Tool.Driver.Rules = rules;
+
+ for (int i = 0; i < rules.Count; i++)
{
- new ThreadFlow
+ ruleIndexMapping.Add(rules[i].Id, i);
+ }
+
+ foreach (Result result in results.Where(r => r.RuleIndex != -1))
+ {
+ if (ruleIndexMapping.TryGetValue(result.RuleId, out int newIndex))
{
- Locations = GenerateRandomThreadFlowLocations(
- count: random.Next(10), artifacts, random)
+ result.RuleIndex = newIndex;
}
}
}
- };
- }
- }
- private static IList GenerateRandomThreadFlowLocations(int count, IList artifacts, Random random)
- {
- var locations = new List();
-
- for (int i = 0; i < count; i++)
- {
- locations.Add(new ThreadFlowLocation
- {
- Importance = RandomEnumValue(random),
- Location = new Location
+ if (artifacts != null)
{
- PhysicalLocation = new PhysicalLocation
- {
- ArtifactLocation = new ArtifactLocation
- {
- Index = random.Next(artifacts.Count),
- },
- Region = new Region
- {
- StartLine = random.Next(500),
- StartColumn = random.Next(100),
- },
- },
- },
- });
- }
- return locations;
- }
+ IDictionary artifactIndexMapping = new Dictionary();
- private static T RandomEnumValue(Random random) where T : Enum
- {
- Array enums = Enum.GetValues(typeof(T));
- return (T)enums.GetValue(random.Next(enums.Length));
- }
+ IDictionary oldMapping = new Dictionary();
+ for (int i = 0; i < artifacts.Count; i++)
+ {
+ oldMapping.Add(artifacts[i], i);
+ }
- private static void ShuffleSarifLog(SarifLog log, Random random)
- {
- Run run = log?.Runs.First();
- IList rules = run?.Tool?.Driver?.Rules;
- IList results = run?.Results;
- IList artifacts = run?.Artifacts;
+ artifacts = artifacts.Shuffle(random);
+ run.Artifacts = artifacts;
- if (rules != null)
- {
- IDictionary ruleIndexMapping = new Dictionary();
+ for (int i = 0; i < artifacts.Count; i++)
+ {
+ if (oldMapping.TryGetValue(artifacts[i], out int oldIndex))
+ {
+ artifactIndexMapping.Add(oldIndex, i);
+ }
+ }
- // shuffle rules
- rules = rules.Shuffle(random);
- log.Runs.First().Tool.Driver.Rules = rules;
+ var locToUpdate = new List();
+ locToUpdate.AddRange(
+ results
+ .SelectMany(r => r.Locations)
+ .Select(l => l.PhysicalLocation.ArtifactLocation));
- // store new rules indexes
- for (int i = 0; i < rules.Count; i++)
- {
- ruleIndexMapping.Add(rules[i].Id, i);
- }
+ locToUpdate.AddRange(
+ results
+ .SelectMany(r => r.CodeFlows)
+ .SelectMany(c => c.ThreadFlows)
+ .SelectMany(t => t.Locations)
+ .Select(l => l.Location.PhysicalLocation.ArtifactLocation));
- // update results rule index
- foreach (Result result in results.Where(r => r.RuleIndex != -1))
- {
- if (ruleIndexMapping.TryGetValue(result.RuleId, out int newIndex))
- {
- result.RuleIndex = newIndex;
+ foreach (ArtifactLocation artifactLocation in locToUpdate.Where(l => l.Index != -1))
+ {
+ if (artifactIndexMapping.TryGetValue(artifactLocation.Index, out int newIndex))
+ {
+ artifactLocation.Index = newIndex;
+ }
+ }
}
- }
- }
- if (artifacts != null)
- {
- IDictionary artifactIndexMapping = new Dictionary();
- // store old artifacts indexes
- IDictionary oldMapping = new Dictionary();
- for (int i = 0; i < artifacts.Count; i++)
- {
- oldMapping.Add(artifacts[i], i);
- }
-
- // shuffle artifacts
- artifacts = artifacts.Shuffle(random);
- log.Runs.First().Artifacts = artifacts;
-
- // store new artifacts indexes
- for (int i = 0; i < artifacts.Count; i++)
- {
- if (oldMapping.TryGetValue(artifacts[i], out int oldIndex))
- {
- artifactIndexMapping.Add(oldIndex, i);
- }
- }
+ run.Results = results.Shuffle(random);
- // update all artifacts' index if its set.
- List locToUpdate = new List();
- locToUpdate.AddRange(
- results
- .SelectMany(r => r.Locations)
- .Select(l => l.PhysicalLocation.ArtifactLocation));
-
- locToUpdate.AddRange(
- results
- .SelectMany(r => r.CodeFlows)
- .SelectMany(c => c.ThreadFlows)
- .SelectMany(t => t.Locations)
- .Select(l => l.Location.PhysicalLocation.ArtifactLocation));
-
- foreach (ArtifactLocation artifactLocation in locToUpdate.Where(l => l.Index != -1))
- {
- if (artifactIndexMapping.TryGetValue(artifactLocation.Index, out int newIndex))
+ foreach (Result result in run.Results)
{
- artifactLocation.Index = newIndex;
+ result.CodeFlows = result.CodeFlows.Shuffle(random);
+ foreach (CodeFlow codeFlow in result.CodeFlows)
+ {
+ codeFlow.ThreadFlows = codeFlow.ThreadFlows.Shuffle(random);
+ foreach (ThreadFlow threadFlow in codeFlow.ThreadFlows)
+ {
+ threadFlow.Locations = threadFlow.Locations.Shuffle(random);
+ }
+ }
}
}
+ logToBeShuffled.Runs = logToBeShuffled.Runs.Shuffle(random);
}
+ while (SarifLogEqualityComparer.Instance.Equals(logToBeShuffled, originalLog));
- // shuffle results
- log.Runs.First().Results = results.Shuffle(random);
-
- // shuffle codeflow locations
- foreach (Result result in log.Runs.First().Results)
- {
- result.CodeFlows = result.CodeFlows.Shuffle(random);
- foreach (CodeFlow codeFlow in result.CodeFlows)
- {
- codeFlow.ThreadFlows = codeFlow.ThreadFlows.Shuffle(random);
- foreach (ThreadFlow threadFlow in codeFlow.ThreadFlows)
- {
- threadFlow.Locations = threadFlow.Locations.Shuffle(random);
- }
- }
- }
+ return logToBeShuffled;
}
}
}
diff --git a/src/Test.Utilities.Sarif/TestUtilitiesExtensions.cs b/src/Test.Utilities.Sarif/TestUtilitiesExtensions.cs
index 26fe16014..849a2d9f4 100644
--- a/src/Test.Utilities.Sarif/TestUtilitiesExtensions.cs
+++ b/src/Test.Utilities.Sarif/TestUtilitiesExtensions.cs
@@ -37,14 +37,19 @@ public static TestRuleBehaviors AccessibleWithinContextOnly(this TestRuleBehavio
return behaviors & ~behaviors.AccessibleOutsideOfContextOnly();
}
- public static IList Shuffle(this IList list, Random random = null)
+ public static IList Shuffle(this IList list, Random random)
{
if (list == null)
{
return null;
}
- random ??= new Random();
+ if (random == null)
+ {
+ // Random object with seed logged in test is required.
+ throw new ArgumentNullException(nameof(random));
+ }
+
return list.OrderBy(item => random.Next()).ToList();
}
}