From d68d368e8ffc609004120a4cad420f0a7ec6f500 Mon Sep 17 00:00:00 2001 From: Tomas Bjerre Date: Mon, 3 Oct 2016 19:00:36 +0200 Subject: [PATCH] Supporting StyleCop * And project level issue in FxCop. --- README.md | 2 +- .../violations/lib/parsers/FxCopParser.java | 13 +- .../lib/parsers/StyleCopParser.java | 8 +- .../lib/parsers/ViolationParserUtils.java | 5 + .../se/bjurr/violations/lib/FxCopTest.java | 32 ++-- .../se/bjurr/violations/lib/StyleCopTest.java | 22 +-- .../fxcop/fxcop-withprojectlevelissue.xml | 158 ++++++++++++++++++ src/test/resources/stylecop/stylecop.xml | 65 +++++-- 8 files changed, 258 insertions(+), 47 deletions(-) create mode 100644 src/test/resources/fxcop/fxcop-withprojectlevelissue.xml diff --git a/README.md b/README.md index 24cd1518..c66617e2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ It supports: * [_PiTest_](http://pitest.org/) * [_PMD_](https://pmd.github.io/) * [_ReSharper_](https://www.jetbrains.com/resharper/) - * [_StyleCop_](https://stylecop.codeplex.com/) Not fully supported. Cannot figure out how to get the filename from the reportfile. + * [_StyleCop_](https://stylecop.codeplex.com/) * [_XMLLint_](http://xmlsoft.org/xmllint.html) Example reports are available [in the test resources](https://github.com/tomasbjerre/violations-lib/tree/master/src/test/resources), examples of how to generate them are available [here](https://github.com/tomasbjerre/violations-test/blob/master/build.sh). diff --git a/src/main/java/se/bjurr/violations/lib/parsers/FxCopParser.java b/src/main/java/se/bjurr/violations/lib/parsers/FxCopParser.java index f1e55bdc..00725a6f 100644 --- a/src/main/java/se/bjurr/violations/lib/parsers/FxCopParser.java +++ b/src/main/java/se/bjurr/violations/lib/parsers/FxCopParser.java @@ -1,6 +1,7 @@ package se.bjurr.violations.lib.parsers; import static com.google.common.collect.Lists.newArrayList; +import static java.util.logging.Level.FINE; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import static se.bjurr.violations.lib.model.SEVERITY.ERROR; import static se.bjurr.violations.lib.model.SEVERITY.INFO; @@ -14,14 +15,18 @@ import java.io.FileInputStream; import java.io.InputStream; import java.util.List; +import java.util.logging.Logger; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; +import com.google.common.base.Optional; + import se.bjurr.violations.lib.model.SEVERITY; import se.bjurr.violations.lib.model.Violation; public class FxCopParser implements ViolationsParser { + private static Logger LOG = Logger.getLogger(FxCopParser.class.getSimpleName()); @Override public List parseFile(File file) throws Exception { @@ -49,12 +54,16 @@ public List parseFile(File file) throws Exception { } if (xmlr.getLocalName().equals("Issue")) { String level = getAttribute(xmlr, "Level"); - String path = getAttribute(xmlr, "Path"); + Optional path = ViolationParserUtils.findAttribute(xmlr, "Path"); + if (!path.isPresent()) { + LOG.log(FINE, "Ignoring project level issue"); + continue; + } String fileName = getAttribute(xmlr, "File"); Integer line = getIntegerAttribute(xmlr, "Line"); String message = xmlr.getElementText().replaceAll("\\s+", " "); - String filename = path + "/" + fileName; + String filename = path.get() + "/" + fileName; SEVERITY severity = toSeverity(level); violations.add(// violationBuilder()// diff --git a/src/main/java/se/bjurr/violations/lib/parsers/StyleCopParser.java b/src/main/java/se/bjurr/violations/lib/parsers/StyleCopParser.java index bebb6437..115d4e7c 100644 --- a/src/main/java/se/bjurr/violations/lib/parsers/StyleCopParser.java +++ b/src/main/java/se/bjurr/violations/lib/parsers/StyleCopParser.java @@ -42,7 +42,7 @@ public List parseFile(File file) throws Exception { Integer lineNumber = getIntegerAttribute(xmlr, "LineNumber"); String message = xmlr.getElementText().replaceAll("\\s+", " "); SEVERITY severity = INFO; - String filename = toFile(source); + String filename = source.replaceAll("\\\\", "/"); violations.add(// violationBuilder()// .setReporter(STYLECOP)// @@ -51,7 +51,7 @@ public List parseFile(File file) throws Exception { .setStartLine(lineNumber)// .setRule(rule)// .setSeverity(severity)// - .setSource(source)// + .setSource(filename)// .setSpecific("section", section)// .setSpecific("source", source)// .setSpecific("ruleNamespace", ruleNamespace)// @@ -65,8 +65,4 @@ public List parseFile(File file) throws Exception { } return violations; } - - private String toFile(String source) { - return source.replaceAll("\\.", "/"); - } } diff --git a/src/main/java/se/bjurr/violations/lib/parsers/ViolationParserUtils.java b/src/main/java/se/bjurr/violations/lib/parsers/ViolationParserUtils.java index 96e679ce..6b832913 100644 --- a/src/main/java/se/bjurr/violations/lib/parsers/ViolationParserUtils.java +++ b/src/main/java/se/bjurr/violations/lib/parsers/ViolationParserUtils.java @@ -1,6 +1,7 @@ package se.bjurr.violations.lib.parsers; import static com.google.common.base.Optional.absent; +import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Optional.of; import static com.google.common.base.Throwables.propagate; import static com.google.common.collect.Lists.newArrayList; @@ -44,6 +45,10 @@ public static Optional findAttribute(String in, String attribute) { return absent(); } + public static Optional findAttribute(XMLStreamReader in, String attribute) { + return fromNullable(in.getAttributeValue("", attribute)); + } + public static Optional findIntegerAttribute(String in, String attribute) { if (findAttribute(in, attribute).isPresent()) { return of(parseInt(getAttribute(in, attribute))); diff --git a/src/test/java/se/bjurr/violations/lib/FxCopTest.java b/src/test/java/se/bjurr/violations/lib/FxCopTest.java index 3468155a..e5a0644e 100644 --- a/src/test/java/se/bjurr/violations/lib/FxCopTest.java +++ b/src/test/java/se/bjurr/violations/lib/FxCopTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static se.bjurr.violations.lib.TestUtils.getRootFolder; import static se.bjurr.violations.lib.ViolationsReporterApi.violationsReporterApi; -import static se.bjurr.violations.lib.model.SEVERITY.ERROR; +import static se.bjurr.violations.lib.model.SEVERITY.WARN; import static se.bjurr.violations.lib.reports.Reporter.FXCOP; import java.util.List; @@ -25,43 +25,43 @@ public void testThatViolationsCanBeParsed() { .violations(); assertThat(actual)// - .hasSize(2); + .hasSize(25); Violation actualViolationZero = actual.get(0); assertThat(actualViolationZero.getFile())// - .isEqualTo("c:/Hudson/data/jobs/job1/workspace/test/Space/TestBase.cs"); + .isEqualTo("C:/git/test-project/Test Solution 1/GenericsSample/Form1.Designer.cs"); assertThat(actualViolationZero.getStartLine())// - .isEqualTo(299); + .isEqualTo(212); assertThat(actualViolationZero.getMessage())// - .startsWith("Because the behavior o"); + .startsWith("Method 'Form"); assertThat(actualViolationZero.getReporter())// .isEqualTo(FXCOP); assertThat(actualViolationZero.getRule().orNull())// - .isEqualTo("SpecifyIFormatProvider"); + .isEqualTo("Do not pass literals as localized parameters"); assertThat(actualViolationZero.getSeverity())// - .isEqualTo(ERROR); + .isEqualTo(WARN); assertThat(actualViolationZero.getSource().orNull())// - .isEqualTo("TestBase"); + .isEqualTo("Form1"); assertThat(actualViolationZero.getSpecifics().get("TARGET_NAME"))// - .isEqualTo("C:/Hudson/data/jobs/job1/workspace/test/bin/test.dll"); + .isEqualTo("C:/git/test-project/Test Solution 1/GenericsSample/bin/Debug/GenericsSample.exe"); Violation actualViolationOne = actual.get(1); assertThat(actualViolationOne.getFile())// - .isEqualTo("c:/Hudson/data/jobs/job1/workspace/web/UserControls/MyControl.ascx.cs"); + .isEqualTo("C:/git/test-project/Test Solution 1/GenericsSample/Form1.Designer.cs"); assertThat(actualViolationOne.getStartLine())// - .isEqualTo(37); + .isEqualTo(203); assertThat(actualViolationOne.getMessage())// - .startsWith("In member"); + .startsWith("Method 'Form"); assertThat(actualViolationOne.getReporter())// .isEqualTo(FXCOP); assertThat(actualViolationOne.getRule().orNull())// - .isEqualTo("CompoundWordsShouldBeCasedCorrectly"); + .isEqualTo("Do not pass literals as localized parameters"); assertThat(actualViolationOne.getSeverity())// - .isEqualTo(ERROR); + .isEqualTo(WARN); assertThat(actualViolationOne.getSource().orNull())// - .isEqualTo("MyControl"); + .isEqualTo("Form1"); assertThat(actualViolationOne.getSpecifics().get("TARGET_NAME"))// - .isEqualTo("C:/Hudson/data/jobs/job1/workspace/web/bin/web.dll"); + .isEqualTo("C:/git/test-project/Test Solution 1/GenericsSample/bin/Debug/GenericsSample.exe"); } } diff --git a/src/test/java/se/bjurr/violations/lib/StyleCopTest.java b/src/test/java/se/bjurr/violations/lib/StyleCopTest.java index 2fc53f4e..3c5ad697 100644 --- a/src/test/java/se/bjurr/violations/lib/StyleCopTest.java +++ b/src/test/java/se/bjurr/violations/lib/StyleCopTest.java @@ -25,39 +25,39 @@ public void testThatViolationsCanBeParsed() { .violations(); assertThat(actual)// - .hasSize(2); + .hasSize(53); Violation actualViolationZero = actual.get(0); assertThat(actualViolationZero.getFile())// - .isEqualTo("Form1/Designer/cs"); + .isEqualTo("E:/Jenkins/jobs/Tools Development/workspace/Tools/Tools.SSOTester/Form1.cs"); assertThat(actualViolationZero.getStartLine())// - .isEqualTo(18); + .isEqualTo(17); assertThat(actualViolationZero.getMessage())// - .startsWith("The call to"); + .startsWith("The do"); assertThat(actualViolationZero.getReporter())// .isEqualTo(STYLECOP); assertThat(actualViolationZero.getRule().orNull())// - .isEqualTo("PrefixLocalCallsWithThis"); + .isEqualTo("ElementDocumentationMustNotHaveDefaultSummary"); assertThat(actualViolationZero.getSeverity())// .isEqualTo(INFO); assertThat(actualViolationZero.getSource().orNull())// - .isEqualTo("Form1.Designer.cs"); + .isEqualTo("E:/Jenkins/jobs/Tools Development/workspace/Tools/Tools.SSOTester/Form1.cs"); Violation actualViolationOne = actual.get(1); assertThat(actualViolationOne.getFile())// - .isEqualTo("Form1/Designer/cs"); + .isEqualTo("E:/Jenkins/jobs/Tools Development/workspace/Tools/Tools.SSOTester/Form1.cs"); assertThat(actualViolationOne.getStartLine())// - .isEqualTo(16); + .isEqualTo(19); assertThat(actualViolationOne.getMessage())// - .startsWith("The call to"); + .startsWith("The field"); assertThat(actualViolationOne.getReporter())// .isEqualTo(STYLECOP); assertThat(actualViolationOne.getRule().orNull())// - .isEqualTo("PrefixLocalCallsWithThis"); + .isEqualTo("ElementsMustBeDocumented"); assertThat(actualViolationOne.getSeverity())// .isEqualTo(INFO); assertThat(actualViolationOne.getSource().orNull())// - .isEqualTo("Form1.Designer.cs"); + .isEqualTo("E:/Jenkins/jobs/Tools Development/workspace/Tools/Tools.SSOTester/Form1.cs"); } } diff --git a/src/test/resources/fxcop/fxcop-withprojectlevelissue.xml b/src/test/resources/fxcop/fxcop-withprojectlevelissue.xml new file mode 100644 index 00000000..1ce57859 --- /dev/null +++ b/src/test/resources/fxcop/fxcop-withprojectlevelissue.xml @@ -0,0 +1,158 @@ + + + + + + + + + + Because assembly 'GenericsSample.exe' contains a ResX-based resource file, mark it with the NeutralResourcesLanguage attribute, specifying the language of the resources within the assembly. This could improve lookup performance the first time a resource is retrieved. + + + + + + + + + + + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Add to Safe List". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Add to unsafe List". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Exception Log". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Generics Sample". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Iterate Safe List(ms):". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Iterate Unsafe List(ms):". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Load Safe List(ms):". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Load Unsafe List(ms):". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Master List". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Safe List". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Select items from the master list, and select add to safe list and add to unsafe...". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Select the \"Start Speed test\" button to see the speed difference between an Ar...". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Speed Test". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Start Speed test". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Type Safety". + Method 'Form1.InitializeComponent()' passes a literal string as parameter 'value' of a call to 'Control.Text.set(string)'. Retrieve the following string(s) from a resource table instead: "Unsafe List". + + + Correct the spelling of the unrecognized token 'ArrayList' in the literal '"Select the \"Start Speed test\" button to see the speed difference between an Ar..."'. + + + Correct the spelling of the unrecognized token 'Genric' in the literal '"Select the \"Start Speed test\" button to see the speed difference between an Ar..."'. + + + Correct the spelling of the unrecognized token 'ms' in the literal '"Iterate Safe List(ms):"'. + Correct the spelling of the unrecognized token 'ms' in the literal '"Iterate Unsafe List(ms):"'. + Correct the spelling of the unrecognized token 'ms' in the literal '"Load Safe List(ms):"'. + Correct the spelling of the unrecognized token 'ms' in the literal '"Load Unsafe List(ms):"'. + + + + + + + + + + + Method 'Program.Main()' passes a literal string as parameter 'caption' of a call to 'MessageBox.Show(string, string, MessageBoxButtons, MessageBoxIcon)'. Retrieve the following string(s) from a resource table instead: "Application Error". + + + + + + + + + + + + + + + Do not pass literals as localized parameters + Literal strings that are embedded in source code are difficult to localize. Avoid passing literal strings as arguments in circumstances where a localized string is generally expected. + Method {0} passes a literal string as parameter '{1}' of a call to {2}. Retrieve the following string(s) from a resource table instead: {3}. + RuleOwner + http://msdn.microsoft.com/library/ms182187.aspx + + Warning + + + + Literals should be spelled correctly + This rule parses the literal string into words, tokenizing compound words, and checks the spelling of each word/token. + Correct the spelling of the unrecognized token '{0}' in the literal '{1}'. + RuleOwner + http://msdn.microsoft.com/library/bb264488.aspx + + Warning + + + + Mark assemblies with NeutralResourcesLanguageAttribute + The NeutralResourcesLanguage attribute informs the ResourceManager of the language used to render the neutral culture's resources for an assembly. When looking up resources in the same culture as the neutral resources language, the ResourceManager automatically uses the resources located in the main assembly, instead of searching for a satellite assembly with the current user interface culture for the current thread. This will improve lookup performance for the first resource you load and can reduce your working set. + Because assembly {0} contains a ResX-based resource file, mark it with the NeutralResourcesLanguage attribute, specifying the language of the resources within the assembly. This could improve lookup performance the first time a resource is retrieved. + + http://msdn.microsoft.com/library/bb385967.aspx + [none] + Warning + + + + + Category + Certainty + Collapse All + Check Id + Error + error(s) + Expand All + Help + Line + message(s) + [Location not stored in Pdb] + Project + Resolution + Rule + Rule File + Rule Description + Source + Status + Target + Warning + warning(s) + Code Analysis Report + + + + C:\git\test-project\Test Solution 1\GenericsSample\bin\Debug + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5 + C:\Program Files (x86)\Microsoft.NET\ADOMD.NET\120 + C:\Program Files (x86)\Common Files\ComponentOne\ActiveReports 9 + C:\Program Files (x86)\IBM\WebSphere MQ\Tools\lib + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0 + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5 + C:\Program Files\IIS\Microsoft Web Deploy V3 + C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies + C:\Program Files (x86)\WiX Toolset v3.10\SDK + C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation + C:\Windows\Microsoft.NET\Framework\v3.0\WPF + C:\Windows\Microsoft.NET\Framework\v3.5 + + + + + + + + + + + + + + + + diff --git a/src/test/resources/stylecop/stylecop.xml b/src/test/resources/stylecop/stylecop.xml index 2d8222bb..a18398b0 100644 --- a/src/test/resources/stylecop/stylecop.xml +++ b/src/test/resources/stylecop/stylecop.xml @@ -1,12 +1,55 @@ - - The call to components must - begin with the 'this.' prefix to indicate that the item is a member of - the class. - The call to components must - begin with the 'this.' prefix to indicate that the item is a member of - the class. + + The documentation header still contains the default summary text generated by Visual Studio. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The field must have a documentation header. + The constructor must have a documentation header. + The documentation header must contain param tags matching the element's parameter list. + The method must have a documentation header. + The method must have a documentation header. + Adjacent elements must be separated by a blank line. + The body of the if statement must be wrapped in opening and closing curly brackets. + A documentation header must be preceded by a blank line or must be the first item in its scope. + Statements or elements wrapped in curly brackets must be followed by a blank line. + A closing curly bracket must not be preceded by a blank line. + A closing curly bracket must not be preceded by a blank line. + The method must have an access modifier. + method names begin with an upper-case letter: btnGenerateSSOPacket_Click. + All protected methods must be placed after all public methods. + All static private methods must be placed before all non-static private methods. + Using directives must be sorted alphabetically by the namespaces. + Using directives must be sorted alphabetically by the namespaces. + Using directives must be sorted alphabetically by the namespaces. + The comment is empty. Add text to the comment or remove it. + The comment is empty. Add text to the comment or remove it. + The comment is empty. Add text to the comment or remove it. + The comment is empty. Add text to the comment or remove it. + Use string.Empty rather than "". + Use string.Empty rather than "". + Use the built-in type alias 'byte' rather than Byte or System.Byte. + Use the built-in type alias 'byte' rather than Byte or System.Byte. + Use string.Empty rather than "". + Use string.Empty rather than "". + The spacing around the keyword 'if' is invalid. + The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. + The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. + The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. + The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. + The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. \ No newline at end of file