diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd226fa69..6a37a4b56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@
##### Enhancements
+* Add Junit reporter.
+ [Matthew Ellis](https://github.com/matthewellis)
+
* LeadingWhitespaceRule is now auto correctable.
[masters3d](https://github.com/masters3d)
diff --git a/README.md b/README.md
index 177009caf..46e7c8df4 100644
--- a/README.md
+++ b/README.md
@@ -200,7 +200,7 @@ variable_name:
- id
- URL
- GlobalAPIKey
-reporter: "xcode" # reporter type (xcode, json, csv, checkstyle)
+reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit)
```
#### Defining Custom Rules
diff --git a/Source/SwiftLintFramework/Protocols/Reporter.swift b/Source/SwiftLintFramework/Protocols/Reporter.swift
index f5aab87e7..8c055c77d 100644
--- a/Source/SwiftLintFramework/Protocols/Reporter.swift
+++ b/Source/SwiftLintFramework/Protocols/Reporter.swift
@@ -22,6 +22,8 @@ public func reporterFromString(string: String) -> Reporter.Type {
return CSVReporter.self
case CheckstyleReporter.identifier:
return CheckstyleReporter.self
+ case JUnitReporter.identifier:
+ return JUnitReporter.self
default:
fatalError("no reporter with identifier '\(string)' available.")
}
diff --git a/Source/SwiftLintFramework/Reporters/JUnitReporter.swift b/Source/SwiftLintFramework/Reporters/JUnitReporter.swift
new file mode 100644
index 000000000..c26d196e7
--- /dev/null
+++ b/Source/SwiftLintFramework/Reporters/JUnitReporter.swift
@@ -0,0 +1,30 @@
+//
+// JUnitReporter.swift
+// SwiftLint
+//
+// Created by Matthew Ellis on 25/05/2016.
+// Copyright © 2016 Realm. All rights reserved.
+//
+
+import Foundation
+
+public struct JUnitReporter: Reporter {
+ public static let identifier = "junit"
+ public static let isRealtime = false
+
+ public var description: String {
+ return "Reports violations as JUnit XML."
+ }
+
+ public static func generateReport(violations: [StyleViolation]) -> String {
+ return "\n" +
+ violations.map({ violation in
+ let fileName = violation.location.file ?? ""
+ let severity = violation.severity.rawValue.lowercaseString + ":\n"
+ let message = severity + "Line:" + String(violation.location.line ?? 0) + " "
+ return ["\n\t\n",
+ "" + message + "",
+ "\t"].joinWithSeparator("")
+ }).joinWithSeparator("") + "\n"
+ }
+}
diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj
index aae05b4ab..f86fcd2db 100644
--- a/SwiftLint.xcodeproj/project.pbxproj
+++ b/SwiftLint.xcodeproj/project.pbxproj
@@ -37,6 +37,7 @@
3BDB224B1C345B4900473680 /* ProjectMock in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB224A1C345B4900473680 /* ProjectMock */; };
4DB7815E1CAD72BA00BC4723 /* LegacyCGGeometryFunctionsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */; };
4DCB8E7F1CBE494E0070FCF0 /* RegexHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCB8E7D1CBE43640070FCF0 /* RegexHelpers.swift */; };
+ 57ED827B1CF656E3002B3513 /* JUnitReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57ED82791CF65183002B3513 /* JUnitReporter.swift */; };
69F88BF71BDA38A6005E7CAE /* OpeningBraceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692B1EB11BD7E00F00EAABFF /* OpeningBraceRule.swift */; };
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7045431C6ADA450003F15A /* SourceKitCrashTests.swift */; };
6CB514E91C760C6900FA02C4 /* Structure+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */; };
@@ -202,6 +203,7 @@
4DCB8E7D1CBE43640070FCF0 /* RegexHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexHelpers.swift; sourceTree = ""; };
5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = ""; };
5499CA971A2394B700783309 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 57ED82791CF65183002B3513 /* JUnitReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JUnitReporter.swift; sourceTree = ""; };
65454F451B14D73800319A6C /* ControlStatementRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatementRule.swift; sourceTree = ""; };
692B1EB11BD7E00F00EAABFF /* OpeningBraceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpeningBraceRule.swift; sourceTree = ""; };
692B60AB1BD8F2E700C7AA22 /* StatementPositionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementPositionRule.swift; sourceTree = ""; };
@@ -563,6 +565,7 @@
isa = PBXGroup;
children = (
E8EA41161C2D1DBE004F9930 /* CheckstyleReporter.swift */,
+ 57ED82791CF65183002B3513 /* JUnitReporter.swift */,
E86396CA1BADB519002C9E88 /* CSVReporter.swift */,
E86396C81BADB2B9002C9E88 /* JSONReporter.swift */,
E86396C41BADAC15002C9E88 /* XcodeReporter.swift */,
@@ -899,6 +902,7 @@
006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */,
E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */,
E88198591BEA95F100333A11 /* LeadingWhitespaceRule.swift in Sources */,
+ 57ED827B1CF656E3002B3513 /* JUnitReporter.swift in Sources */,
24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */,
E80E018F1B92C1350078EB70 /* Region.swift in Sources */,
E88198581BEA956C00333A11 /* FunctionBodyLengthRule.swift in Sources */,
diff --git a/Tests/SwiftLintFramework/ReporterTests.swift b/Tests/SwiftLintFramework/ReporterTests.swift
index 108ee355e..c11484270 100644
--- a/Tests/SwiftLintFramework/ReporterTests.swift
+++ b/Tests/SwiftLintFramework/ReporterTests.swift
@@ -78,4 +78,17 @@ class ReporterTests: XCTestCase {
""
)
}
+
+ func testJunitReporter() {
+ XCTAssertEqual(
+ JUnitReporter.generateReport(generateViolations()),
+ "\n\n" +
+ "\t\n" +
+ "warning:\nLine:1 " +
+ "\t\n" +
+ "\t\n" +
+ "error:\nLine:1 " +
+ "\t\n"
+ )
+ }
}