Skip to content

Commit

Permalink
Add ErrorLogger tests for related locations and ID collision
Browse files Browse the repository at this point in the history
Also make ErrorLogger tolerate "external" locations.
  • Loading branch information
nguerrera committed May 13, 2016
1 parent 5ae04f6 commit f26c807
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<Compile Include="Collections\BoxesTest.cs" />
<Compile Include="Collections\ByteSequenceComparerTests.cs" />
<Compile Include="CryptoBlobParserTests.cs" />
<Compile Include="Diagnostics\ErrorLoggerTests.cs" />
<Compile Include="Diagnostics\AnalysisContextInfoTests.cs" />
<Compile Include="Diagnostics\BoxingOperationAnalyzer.cs" />
<Compile Include="Diagnostics\CouldHaveMoreSpecificTypeAnalyzer.cs" />
Expand Down
229 changes: 229 additions & 0 deletions src/Compilers/Core/CodeAnalysisTest/Diagnostics/ErrorLoggerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Xunit;
using System.IO;
using System;
using System.Globalization;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;

namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics
{
// See also VB and C# command line unit tests for additional coverage.
public class ErrorLoggerTests
{
[Fact]
public void AdditionalLocationsAsRelatedLocations()
{
var stream = new MemoryStream();
using (var logger = new ErrorLogger(stream, "toolName", "1.2.3.4", new Version(1, 2, 3, 4), CultureInfo.InvariantCulture))
{
var mainLocation = Location.Create(@"Z:\MainLocation.cs", new TextSpan(0, 0), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero));
var additionalLocation = Location.Create(@"Z:\AdditionalLocation.cs", new TextSpan(0, 0), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero));
var descriptor = new DiagnosticDescriptor("TST", "_TST_", "", "", DiagnosticSeverity.Error, false);

IEnumerable<Location> additionalLocations = new[] { additionalLocation };
logger.LogDiagnostic(Diagnostic.Create(descriptor, mainLocation, additionalLocations));
}

string expected =
@"{
""$schema"": ""http://json.schemastore.org/sarif-1.0.0-beta.5"",
""version"": ""1.0.0-beta.5"",
""runs"": [
{
""tool"": {
""name"": ""toolName"",
""version"": ""1.2.3.4"",
""fileVersion"": ""1.2.3.4"",
""semanticVersion"": ""1.2.3""
},
""results"": [
{
""ruleId"": ""TST"",
""level"": ""error"",
""locations"": [
{
""resultFile"": {
""uri"": ""file:///Z:/MainLocation.cs"",
""region"": {
""startLine"": 1,
""startColumn"": 1,
""endLine"": 1,
""endColumn"": 1
}
}
}
],
""relatedLocations"": [
{
""physicalLocation"": {
""uri"": ""file:///Z:/AdditionalLocation.cs"",
""region"": {
""startLine"": 1,
""startColumn"": 1,
""endLine"": 1,
""endColumn"": 1
}
}
}
]
}
],
""rules"": {
""TST"": {
""id"": ""TST"",
""shortDescription"": ""_TST_"",
""defaultLevel"": ""error"",
""properties"": {
""isEnabledByDefault"": false
}
}
}
}
]
}";
string actual = Encoding.UTF8.GetString(stream.ToArray());
Assert.Equal(expected, actual);
}

[Fact]
public void DescriptorIdCollision()
{
var descriptors = new[] {
new DiagnosticDescriptor("TST001.001", "_TST001.001_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001.002_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001.003_", "", "", DiagnosticSeverity.Warning, true),
};

var stream = new MemoryStream();
using (var logger = new ErrorLogger(stream, "toolName", "1.2.3.4", new Version(1, 2, 3, 4), CultureInfo.InvariantCulture))
{
for (int i = 0; i < 2; i++)
{
foreach (var descriptor in descriptors)
{
logger.LogDiagnostic(Diagnostic.Create(descriptor, Location.None));
}
}
}

string expected =
@"{
""$schema"": ""http://json.schemastore.org/sarif-1.0.0-beta.5"",
""version"": ""1.0.0-beta.5"",
""runs"": [
{
""tool"": {
""name"": ""toolName"",
""version"": ""1.2.3.4"",
""fileVersion"": ""1.2.3.4"",
""semanticVersion"": ""1.2.3""
},
""results"": [
{
""ruleId"": ""TST001.001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.002"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.003"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001.001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.002"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.003"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
}
],
""rules"": {
""TST001"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.001"": {
""id"": ""TST001.001"",
""shortDescription"": ""_TST001.001_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.002"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001.002_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.003"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001.003_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
}
}
}
]
}";
string actual = Encoding.UTF8.GetString(stream.ToArray());
Assert.Equal(expected, actual);
}
}
}
29 changes: 19 additions & 10 deletions src/Compilers/Core/Portable/CommandLine/ErrorLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void LogDiagnostic(Diagnostic diagnostic)

private void WriteLocations(Location location, IReadOnlyList<Location> additionalLocations)
{
if (location.SourceTree != null)
if (HasPath(location))
{
_writer.WriteArrayStart("locations");
_writer.WriteObjectStart(); // location
Expand All @@ -108,13 +108,13 @@ private void WriteLocations(Location location, IReadOnlyList<Location> additiona
// as SARIF relatedLocations.
if (additionalLocations != null &&
additionalLocations.Count > 0 &&
additionalLocations.Any(l => l.SourceTree != null))
additionalLocations.Any(l => HasPath(l)))
{
_writer.WriteArrayStart("relatedLocations");

foreach (var additionalLocation in additionalLocations)
{
if (additionalLocation.SourceTree != null)
if (HasPath(additionalLocation))
{
_writer.WriteObjectStart(); // annotatedCodeLocation
_writer.WriteKey("physicalLocation");
Expand All @@ -131,13 +131,15 @@ private void WriteLocations(Location location, IReadOnlyList<Location> additiona

private void WritePhysicalLocation(Location location)
{
Debug.Assert(location.SourceTree != null);
Debug.Assert(HasPath(location));

FileLinePositionSpan span = location.GetLineSpan();

_writer.WriteObjectStart();
_writer.Write("uri", GetUri(location.SourceTree));
_writer.Write("uri", GetUri(span.Path));

// Note that SARIF lines and columns are 1-based, but FileLinePositionSpan is 0-based
FileLinePositionSpan span = location.GetLineSpan();

_writer.WriteObjectStart("region");
_writer.Write("startLine", span.StartLinePosition.Line + 1);
_writer.Write("startColumn", span.StartLinePosition.Character + 1);
Expand All @@ -148,17 +150,24 @@ private void WritePhysicalLocation(Location location)
_writer.WriteObjectEnd();
}

private static string GetUri(SyntaxTree syntaxTree)
private static bool HasPath(Location location)
{
return !string.IsNullOrEmpty(location.GetLineSpan().Path);
}

private static string GetUri(string path)
{
Debug.Assert(!string.IsNullOrEmpty(path));

Uri uri;

if (!Uri.TryCreate(syntaxTree.FilePath, UriKind.RelativeOrAbsolute, out uri))
if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out uri))
{
// The only constraint on SyntaxTree.FilePath is that it can be interpreted by
// The only constraint on paths are that they can be interpreted by
// various resolvers so there is no guarantee we can turn the arbitrary string
// in to a URI. If our attempt to do so fails, use the original string as the
// "URI".
return syntaxTree.FilePath;
return path;
}

return uri.ToString();
Expand Down

0 comments on commit f26c807

Please sign in to comment.