diff --git a/.editorconfig b/.editorconfig
index 2b7c019..db932fc 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,12 +1,33 @@
root = true
+[*]
+end_of_line = CRLF
+
[*.{msbuild,xml}]
charset = utf-8
-end_of_line = crlf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
-indent_size = 4
\ No newline at end of file
+indent_size = 4
+
+[*.{config,cs,json}]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+[*.{proj,props,sln,targets}]
+indent_style = tab
+trim_trailing_whitespace = true
+
+[*.{kproj,csproj,ps1,resx,rst}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+[NuGet.Config]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 2ae903d..bb50c2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -230,3 +230,7 @@ _Pvt_Extensions
# FAKE - F# Make
.fake/
+
+.idea/
+*.sln.iml
+*.zip
\ No newline at end of file
diff --git a/Plugin/agent/bin/PvsStudioIssueTypes.xml b/Plugin/agent/bin/PvsStudioIssueTypes.xml
index d943ac0..b1eff14 100644
--- a/Plugin/agent/bin/PvsStudioIssueTypes.xml
+++ b/Plugin/agent/bin/PvsStudioIssueTypes.xml
@@ -1,44 +1,59 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plugin/agent/bin/ResharperReport.xslt b/Plugin/agent/bin/ResharperReport.xslt
index a9a6c16..ee4b3e1 100644
--- a/Plugin/agent/bin/ResharperReport.xslt
+++ b/Plugin/agent/bin/ResharperReport.xslt
@@ -1,6 +1,7 @@
+
@@ -14,10 +15,10 @@
Solution
-
-
+
+
@@ -55,6 +56,38 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ERROR
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plugin/server/metaRunners/MRPLUGIN_PvsStudioBuildRunnerPlugin.xml b/Plugin/server/metaRunners/MRPLUGIN_PvsStudioBuildRunnerPlugin.xml
index d7c5aba..35e880d 100644
--- a/Plugin/server/metaRunners/MRPLUGIN_PvsStudioBuildRunnerPlugin.xml
+++ b/Plugin/server/metaRunners/MRPLUGIN_PvsStudioBuildRunnerPlugin.xml
@@ -6,12 +6,14 @@
+
+
diff --git a/Plugin/teamcity-plugin.xml b/Plugin/teamcity-plugin.xml
index 7adf9f1..5764c8e 100644
--- a/Plugin/teamcity-plugin.xml
+++ b/Plugin/teamcity-plugin.xml
@@ -4,7 +4,7 @@
PvsStudioBuildRunnerPlugin
PVS Studio Build Runner
- snapshot
+ 1.1.0.0
Adds a PVS Studio Build Step option to TeamCity
Ales Buzhynsky
diff --git a/documents/releaseNotes/1.1.0.0.md b/documents/releaseNotes/1.1.0.0.md
new file mode 100644
index 0000000..67bb1c3
--- /dev/null
+++ b/documents/releaseNotes/1.1.0.0.md
@@ -0,0 +1,18 @@
+#TeamCity PVS Studio Meta-Runner v1.1.0.0
+
+##Changes:
+
+###Added grouping by category name and priority (e.g. PVS-Studio General Analysis Priority: 1)
+
+###Added possibility to treat priority 1 issues as errors
+Now there is a checkbox in build step to toggle this behaviour.
+
+###Added PVS-Studio 6.01 inspections support
+
+###Added support of PVS-Studio next releases inspections
+They will be displayed without category name in build results but with inspection Id and inspection message.
+
+##Update instructions:
+1. Download 'pvs-studio-build-runner.zip' from the [latest release](https://github.com/abuzhynsky/teamcity-pvs-studio-meta-runner/releases/latest)
+2. Copy this file into the _[Team City Data Directory]\plugins_ directory on the TeamCity Server, by default this is _C:\ProgramData\JetBrains\TeamCity\plugins_ or upload it using TeamCity 'Plugins List' tab (_Administration-Plugins List_)
+3. Restart the Team City server
\ No newline at end of file
diff --git a/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln b/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln
new file mode 100644
index 0000000..c3d7650
--- /dev/null
+++ b/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamCity.PvsStudio.MetaRunner.TestProject", "app\TeamCity.PvsStudio.MetaRunner.TestProject\TeamCity.PvsStudio.MetaRunner.TestProject.csproj", "{0C821208-FBE5-4421-826C-E9E22ADF7303}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamCity.PvsStudio.MetaRunner.Tests", "tests\TeamCity.PvsStudio.MetaRunner.Tests\TeamCity.PvsStudio.MetaRunner.Tests.csproj", "{282843E9-1846-4874-805A-5CCB59B3E633}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0C821208-FBE5-4421-826C-E9E22ADF7303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C821208-FBE5-4421-826C-E9E22ADF7303}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C821208-FBE5-4421-826C-E9E22ADF7303}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C821208-FBE5-4421-826C-E9E22ADF7303}.Release|Any CPU.Build.0 = Release|Any CPU
+ {282843E9-1846-4874-805A-5CCB59B3E633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {282843E9-1846-4874-805A-5CCB59B3E633}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {282843E9-1846-4874-805A-5CCB59B3E633}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {282843E9-1846-4874-805A-5CCB59B3E633}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln.DotSettings b/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln.DotSettings
new file mode 100644
index 0000000..a038c82
--- /dev/null
+++ b/tests/TeamCity.PvsStudio.MetaRunner.Tests.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ ExplicitlyExcluded
\ No newline at end of file
diff --git a/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Class1.cs b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Class1.cs
new file mode 100644
index 0000000..3ad86e9
--- /dev/null
+++ b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Class1.cs
@@ -0,0 +1,22 @@
+namespace TeamCity.PvsStudio.MetaRunner.TestProject
+{
+ public class Class1
+ {
+ public void Method()
+ {
+ //intentionally made mistakes for PVS-Studio testing
+ var s = "qwerty";
+
+ string f = null;
+
+ var a = 0;
+
+ if (f == string.Empty || f == string.Empty)
+ {
+ a = 5;
+ }
+
+ s.Replace("s", a.ToString());
+ }
+ }
+}
diff --git a/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8bf41d0
--- /dev/null
+++ b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("TeamCity.PvsStudio.MetaRunner.TestProject")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TeamCity.PvsStudio.MetaRunner.TestProject")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("0c821208-fbe5-4421-826c-e9e22adf7303")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj
new file mode 100644
index 0000000..53edc62
--- /dev/null
+++ b/tests/app/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj
@@ -0,0 +1,55 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {0C821208-FBE5-4421-826C-E9E22ADF7303}
+ Library
+ Properties
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ v4.6.1
+ 512
+ 1.0.0.0
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PluginParameters.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PluginParameters.cs
new file mode 100644
index 0000000..8a0bb1d
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PluginParameters.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace TeamCity.PvsStudio.MetaRunner.Tests.Helpers
+{
+ public static class PluginParameters
+ {
+ public static string PvsStudioOutputPath
+ {
+ get { return Path.Combine(CurrentAssemblyPath, "temp", "result.xml"); }
+ }
+
+ public static string XsltOutputPath
+ {
+ get { return Path.Combine(CurrentAssemblyPath, "temp", "resharperResult.xml"); }
+ }
+
+ public static string PvsStudioConsolePath
+ {
+ get { return @"C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cs.exe"; }
+ }
+
+ public static string MsXslPath
+ {
+ get { return Path.Combine(PluginPath, "msxsl.exe"); }
+ }
+
+ public static string PvsStudioParameters
+ {
+ get { return $"-r -o {PvsStudioOutputPath} -t {SolutionPath}"; }
+ }
+
+ public static string MsXslParameters
+ {
+ get { return GenerateXsltParameters(PvsStudioOutputPath, false); }
+ }
+
+ public static string GenerateXsltParameters(string inputPath, bool treatPriority1IssuesAsErrors)
+ {
+ return $@"{inputPath} {XsltPath} -o {XsltOutputPath} treatPriority1IssuesAsErrors=""{Convert.ToInt32(treatPriority1IssuesAsErrors)}""";
+ }
+
+ public static string UnknownIssueTypeReportPath
+ {
+ get { return Path.Combine(CurrentAssemblyPath, "TestData", "UnknownIssueTypeReport.xml"); }
+ }
+
+ private static string SolutionPath
+ {
+ get
+ {
+ var solutionDirectoryPath = GetParentDirectoryPath(CurrentAssemblyPath, 4);
+
+ return Path.Combine(solutionDirectoryPath, "TeamCity.PvsStudio.MetaRunner.Tests.sln");
+ }
+ }
+
+ private static string PluginPath
+ {
+ get
+ {
+ var rootPath = GetParentDirectoryPath(CurrentAssemblyPath, 5);
+
+ return Path.Combine(rootPath, "plugin", "agent", "bin");
+ }
+ }
+
+ private static string XsltPath
+ {
+ get { return Path.Combine(PluginPath, "ResharperReport.xslt"); }
+ }
+
+ private static string CurrentAssemblyPath
+ {
+ get
+ {
+ var currentAssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
+
+ if (currentAssemblyPath == null)
+ {
+ throw new DirectoryNotFoundException();
+ }
+
+ return new Uri(currentAssemblyPath).LocalPath;
+ }
+ }
+
+ private static string GetParentDirectoryPath(string directoryPath, int levelsUp)
+ {
+ var resultPath = directoryPath;
+
+ for (var i = 0; i < levelsUp; i++)
+ {
+ resultPath = Directory.GetParent(resultPath).FullName;
+ }
+
+ return resultPath;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PvsStudioExpectedErrorCode.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PvsStudioExpectedErrorCode.cs
new file mode 100644
index 0000000..5cab4f6
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Helpers/PvsStudioExpectedErrorCode.cs
@@ -0,0 +1,21 @@
+namespace TeamCity.PvsStudio.MetaRunner.Tests.Helpers
+{
+ public class PvsStudioExpectedErrorCode
+ {
+ public PvsStudioExpectedErrorCode(string errorCode, string category, int priority, int occurrenceCount)
+ {
+ ErrorCode = errorCode;
+ Category = category;
+ OccurrenceCount = occurrenceCount;
+ Priority = priority;
+ }
+
+ public string ErrorCode { get; private set; }
+
+ public string Category { get; private set; }
+
+ public int OccurrenceCount { get; private set; }
+
+ public int Priority { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Properties/AssemblyInfo.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e4bb4f7
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("TeamCity.PvsStudio.MetaRunner.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TeamCity.PvsStudio.MetaRunner.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("282843e9-1846-4874-805a-5ccb59b3e633")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/PvsStudioTests.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/PvsStudioTests.cs
new file mode 100644
index 0000000..30f6563
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/PvsStudioTests.cs
@@ -0,0 +1,195 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+using JetBrains.Annotations;
+using RunProcessAsTask;
+using TeamCity.PvsStudio.MetaRunner.Tests.Helpers;
+using Xunit;
+
+namespace TeamCity.PvsStudio.MetaRunner.Tests
+{
+ public class PvsStudioTests
+ {
+ private const string CategoryGeneralAnalysis = "General Analysis";
+ private const string CategoryMissing = "Unknown Inspections";
+
+ private const string ErrorCode3001 = "V3001";
+ private const string ErrorCode3010 = "V3010";
+ private const string ErrorCodeMissing = "V9999";
+
+ public PvsStudioTests()
+ {
+ DeleteExistingFile(PluginParameters.PvsStudioOutputPath);
+ DeleteExistingFile(PluginParameters.XsltOutputPath);
+ }
+
+ [Fact]
+ public async Task PvsStudioOutputTest()
+ {
+ var result = await ProcessEx.RunAsync(PluginParameters.PvsStudioConsolePath, PluginParameters.PvsStudioParameters);
+ AssertSuccessfulProcessCompletion(result);
+
+ var expectedErrorCodes = new List
+ {
+ new PvsStudioExpectedErrorCode(ErrorCode3001, CategoryGeneralAnalysis, 1, 1),
+ new PvsStudioExpectedErrorCode(ErrorCode3010, CategoryGeneralAnalysis, 1, 1)
+ };
+
+ AssertValidPvsStudioAnalysisReport(PluginParameters.PvsStudioOutputPath, expectedErrorCodes);
+ }
+
+ [Fact]
+ public async Task UnknownIssueTypeXsltOutputTest()
+ {
+ var xsltResult = await ProcessEx.RunAsync(PluginParameters.MsXslPath, PluginParameters.GenerateXsltParameters(PluginParameters.UnknownIssueTypeReportPath, false));
+ AssertSuccessfulProcessCompletion(xsltResult);
+
+ var expectedErrorCodes = new List
+ {
+ new PvsStudioExpectedErrorCode(ErrorCode3001, CategoryGeneralAnalysis, 1, 1),
+ new PvsStudioExpectedErrorCode(ErrorCodeMissing, CategoryMissing, 2, 2)
+ };
+
+ AssertValidResharperAnalysisReport(PluginParameters.XsltOutputPath, expectedErrorCodes, false);
+ }
+
+ [Fact]
+ public async Task TreatPriority1IssuesAsErrorsXsltOutputTest()
+ {
+ var xsltResult = await ProcessEx.RunAsync(PluginParameters.MsXslPath, PluginParameters.GenerateXsltParameters(PluginParameters.UnknownIssueTypeReportPath, true));
+ AssertSuccessfulProcessCompletion(xsltResult);
+
+ var expectedErrorCodes = new List
+ {
+ new PvsStudioExpectedErrorCode(ErrorCode3001, CategoryGeneralAnalysis, 1, 1),
+ new PvsStudioExpectedErrorCode(ErrorCodeMissing, CategoryMissing, 2, 2)
+ };
+
+ AssertValidResharperAnalysisReport(PluginParameters.XsltOutputPath, expectedErrorCodes, true);
+ }
+
+ [Fact]
+ public async Task XsltOutputTest()
+ {
+ var pvsStudioResult = await ProcessEx.RunAsync(PluginParameters.PvsStudioConsolePath, PluginParameters.PvsStudioParameters);
+ AssertSuccessfulProcessCompletion(pvsStudioResult);
+
+ var expectedErrorCodes = new List
+ {
+ new PvsStudioExpectedErrorCode(ErrorCode3001, CategoryGeneralAnalysis, 1, 1),
+ new PvsStudioExpectedErrorCode(ErrorCode3010, CategoryGeneralAnalysis, 1, 1)
+ };
+
+ AssertValidPvsStudioAnalysisReport(PluginParameters.PvsStudioOutputPath, expectedErrorCodes);
+
+ var xsltResult = await ProcessEx.RunAsync(PluginParameters.MsXslPath, PluginParameters.MsXslParameters);
+ AssertSuccessfulProcessCompletion(xsltResult);
+
+ AssertValidResharperAnalysisReport(PluginParameters.XsltOutputPath, expectedErrorCodes, false);
+ }
+
+ private static void DeleteExistingFile(string filePath)
+ {
+ if (File.Exists(filePath))
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ [AssertionMethod]
+ private static void AssertFileExists(string filePath)
+ {
+ Assert.True(File.Exists(filePath));
+ }
+
+ [AssertionMethod]
+ private static void AssertSuccessfulProcessCompletion(ProcessResults result)
+ {
+ Assert.NotNull(result);
+ Assert.NotEqual(1, result.Process.ExitCode);
+ Assert.Empty(result.StandardError);
+ }
+
+ [AssertionMethod]
+ private static void AssertValidPvsStudioAnalysisReport(string filePath, ICollection expectedErrorCodes)
+ {
+ AssertFileExists(filePath);
+
+ var outputDocument = XDocument.Load(filePath);
+ Assert.NotNull(outputDocument);
+
+ var analysisLogs = outputDocument.Descendants("PVS-Studio_Analysis_Log").ToList();
+
+ Assert.NotNull(analysisLogs);
+ Assert.Equal(expectedErrorCodes.Count, analysisLogs.Count);
+
+ var errorCodes = analysisLogs.Select(analysisLog => analysisLog.Element("ErrorCode")).ToList();
+
+ var groupedCodesInReport = errorCodes.GroupBy(_ => _).ToList();
+
+ foreach (var expectedErrorCode in expectedErrorCodes)
+ {
+ var grouping = Assert.Single(groupedCodesInReport, _ => _.Key.Value == expectedErrorCode.ErrorCode);
+ Assert.NotNull(grouping);
+ Assert.Equal(expectedErrorCode.OccurrenceCount, grouping.Count());
+ }
+ }
+
+ [AssertionMethod]
+ private static void AssertValidResharperAnalysisReport(string filePath, ICollection expectedErrorCodes, bool treatPriority1IssuesAsErrors)
+ {
+ AssertFileExists(filePath);
+
+ var resharperOutputDocument = XDocument.Load(filePath);
+ Assert.NotNull(resharperOutputDocument);
+
+ var reportElement = resharperOutputDocument.Element("Report");
+ Assert.NotNull(reportElement);
+
+ var issueTypesElement = reportElement.Element("IssueTypes");
+ Assert.NotNull(issueTypesElement);
+
+ var issueTypeElements = issueTypesElement.Descendants("IssueType").ToList();
+
+ Assert.NotNull(issueTypeElements);
+ Assert.Equal(expectedErrorCodes.Count, issueTypeElements.Count);
+
+ foreach (var expectedErrorCode in expectedErrorCodes)
+ {
+ AssertResharperIssueTypes(issueTypeElements, expectedErrorCode, treatPriority1IssuesAsErrors);
+ }
+
+ var issuesElement = reportElement.Element("Issues");
+ Assert.NotNull(issuesElement);
+
+ var projectElement = issuesElement.Element("Project");
+ Assert.NotNull(projectElement);
+
+ var issueElements = projectElement.Descendants("Issue").ToList();
+
+ Assert.NotNull(issueElements);
+ Assert.Equal(expectedErrorCodes.Select(_ => _.OccurrenceCount).Sum(), issueElements.Count);
+ }
+
+ [AssertionMethod]
+ private static void AssertResharperIssueTypes(IEnumerable issueTypeElements, PvsStudioExpectedErrorCode expectedErrorCode, bool treatPriority1IssuesAsErrors)
+ {
+ var issueType = Assert.Single(issueTypeElements, _ => _.Attribute("Id").Value == expectedErrorCode.ErrorCode);
+ Assert.NotNull(issueType);
+
+ Assert.Equal($"PVS-Studio {expectedErrorCode.Category}. Priority: {expectedErrorCode.Priority}", issueType.Attribute("Category").Value);
+ Assert.Equal($"{issueType.Attribute("Id").Value}. {issueType.Attribute("Description").Value}", issueType.Attribute("SubCategory").Value);
+
+ AssertSeverity(expectedErrorCode, issueType, treatPriority1IssuesAsErrors);
+ }
+
+ [AssertionMethod]
+ private static void AssertSeverity(PvsStudioExpectedErrorCode expectedErrorCode, XElement issueType, bool treatPriority1IssuesAsErrors)
+ {
+ var expectedSeverity = treatPriority1IssuesAsErrors && expectedErrorCode.Priority == 1 ? "ERROR" : "WARNING";
+ Assert.Equal(expectedSeverity, issueType.Attribute("Severity").Value);
+ }
+ }
+}
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/App.config b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/App.config
new file mode 100644
index 0000000..731f6de
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Program.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Program.cs
new file mode 100644
index 0000000..af2557e
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Program.cs
@@ -0,0 +1,14 @@
+namespace TeamCity.PvsStudio.MetaRunner.TestProject
+{
+ static class Program
+ {
+ static void Main()
+ {
+ var s = "qwerty";
+
+ //issue is made intentionally for testing PVS Studio
+ // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+ s.Replace("q", string.Empty);
+ }
+ }
+}
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..29aecaf
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("TeamCity.PvsStudio.MetaRunner.TestProject")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TeamCity.PvsStudio.MetaRunner.TestProject")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2850fbfa-1f9d-46be-a8e3-0ecf5fff7a75")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj
new file mode 100644
index 0000000..84ecd98
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.TestProject/TeamCity.PvsStudio.MetaRunner.TestProject.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2850FBFA-1F9D-46BE-A8E3-0ECF5FFF7A75}
+ Exe
+ Properties
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ v4.6.1
+ 512
+ true
+ 1.0.0.0
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.Tests.csproj b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.Tests.csproj
new file mode 100644
index 0000000..c261d6c
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TeamCity.PvsStudio.MetaRunner.Tests.csproj
@@ -0,0 +1,89 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {282843E9-1846-4874-805A-5CCB59B3E633}
+ Library
+ Properties
+ TeamCity.PvsStudio.MetaRunner.Tests
+ TeamCity.PvsStudio.MetaRunner.Tests
+ v4.6.1
+ 512
+ 1.0.0.0
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\packages\JetBrains.Annotations.10.0.0\lib\net20\JetBrains.Annotations.dll
+ True
+
+
+ ..\..\packages\RunProcessAsTask.1.1.0\lib\RunProcessAsTask.dll
+ True
+
+
+
+
+
+
+
+
+
+
+ ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
+ True
+
+
+ ..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TestData/UnknownIssueTypeReport.xml b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TestData/UnknownIssueTypeReport.xml
new file mode 100644
index 0000000..6e6fb41
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/TestData/UnknownIssueTypeReport.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ D:\Projects\teamcity-pvs-studio-meta-runner\tests\TeamCity.PvsStudio.MetaRunner.Tests.sln
+ 14.0
+ 3
+ 2016-02-29T14:49:54.0332805Z
+
+
+ false
+ 1
+ 1
+ V3001
+ There are identical sub-expressions 'f == string.Empty' to the left and to the right of the '||' operator.
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ TRIAL RESTRICTION
+ 14
+ false
+ 1
+ 214879571
+ 1629368839
+ 123
+ true
+ 4
+
+ false
+
+
+ false
+ 2
+ 2
+ V9999
+ The return value of function 'Replace' is required to be utilized.
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ TRIAL RESTRICTION
+ 21
+ false
+ 2
+ 1345105634
+ 3418268110
+ 125
+ true
+ 4
+
+ false
+
+
+ false
+ 2
+ 2
+ V9999
+ The return value of function 'Replace' is required to be utilized.
+ TeamCity.PvsStudio.MetaRunner.TestProject
+ TRIAL RESTRICTION
+ 23
+ false
+ 2
+ 1345105634
+ 3418268110
+ 125
+ true
+ 4
+
+ false
+
+
diff --git a/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/packages.config b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/packages.config
new file mode 100644
index 0000000..09efc5c
--- /dev/null
+++ b/tests/tests/TeamCity.PvsStudio.MetaRunner.Tests/packages.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file