mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
21d66e5dc7
Co-authored-by: Danny Mösch <danny.moesch@icloud.com>
264 lines
9.7 KiB
Swift
264 lines
9.7 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
import TestHelpers
|
|
import XCTest
|
|
|
|
@testable import SwiftLintBuiltInRules
|
|
@testable import SwiftLintFramework
|
|
|
|
final class ReporterTests: SwiftLintTestCase {
|
|
private let violations = [
|
|
StyleViolation(
|
|
ruleDescription: LineLengthRule.description,
|
|
location: Location(file: "filename", line: 1, character: 1),
|
|
reason: "Violation Reason 1"
|
|
),
|
|
StyleViolation(
|
|
ruleDescription: LineLengthRule.description,
|
|
severity: .error,
|
|
location: Location(file: "filename", line: 1),
|
|
reason: "Violation Reason 2"
|
|
),
|
|
StyleViolation(
|
|
ruleDescription: SyntacticSugarRule.description,
|
|
severity: .error,
|
|
location: Location(
|
|
file: FileManager.default.currentDirectoryPath + "/path/file.swift",
|
|
line: 1,
|
|
character: 2
|
|
),
|
|
reason: "Shorthand syntactic sugar should be used, i.e. [Int] instead of Array<Int>"),
|
|
StyleViolation(
|
|
ruleDescription: ColonRule.description,
|
|
severity: .error,
|
|
location: Location(file: nil),
|
|
reason: nil
|
|
),
|
|
]
|
|
|
|
func testReporterFromString() {
|
|
for reporter in reportersList {
|
|
XCTAssertEqual(reporter.identifier, reporterFrom(identifier: reporter.identifier).identifier)
|
|
}
|
|
}
|
|
|
|
private func stringFromFile(_ filename: String) -> String {
|
|
SwiftLintFile(path: "\(TestResources.path())/\(filename)")!.contents
|
|
}
|
|
|
|
func testXcodeReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedXcodeReporterOutput.txt",
|
|
reporterType: XcodeReporter.self
|
|
)
|
|
}
|
|
|
|
func testEmojiReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedEmojiReporterOutput.txt",
|
|
reporterType: EmojiReporter.self
|
|
)
|
|
}
|
|
|
|
func testGitHubActionsLoggingReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedGitHubActionsLoggingReporterOutput.txt",
|
|
reporterType: GitHubActionsLoggingReporter.self
|
|
)
|
|
}
|
|
|
|
func testGitLabJUnitReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedGitLabJUnitReporterOutput.xml",
|
|
reporterType: GitLabJUnitReporter.self
|
|
)
|
|
}
|
|
|
|
private func jsonValue(_ jsonString: String) throws -> NSObject {
|
|
let data = jsonString.data(using: .utf8)!
|
|
let result = try JSONSerialization.jsonObject(with: data, options: [])
|
|
if let dict = (result as? [String: Any])?.bridge() {
|
|
return dict
|
|
}
|
|
if let array = (result as? [Any])?.bridge() {
|
|
return array
|
|
}
|
|
queuedFatalError("Unexpected value in JSON: \(result)")
|
|
}
|
|
|
|
func testJSONReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedJSONReporterOutput.json",
|
|
reporterType: JSONReporter.self,
|
|
stringConverter: { try jsonValue($0) }
|
|
)
|
|
}
|
|
|
|
func testCSVReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedCSVReporterOutput.csv",
|
|
reporterType: CSVReporter.self
|
|
)
|
|
}
|
|
|
|
func testCheckstyleReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedCheckstyleReporterOutput.xml",
|
|
reporterType: CheckstyleReporter.self
|
|
)
|
|
}
|
|
|
|
func testCodeClimateReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedCodeClimateReporterOutput.json",
|
|
reporterType: CodeClimateReporter.self
|
|
)
|
|
}
|
|
|
|
func testSARIFReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedSARIFReporterOutput.sarif",
|
|
reporterType: SARIFReporter.self,
|
|
stringConverter: { try sarifValueWithoutRules($0) }
|
|
)
|
|
}
|
|
|
|
private func sarifValueWithoutRules(_ jsonString: String) throws -> NSObject {
|
|
let data = jsonString.data(using: .utf8)!
|
|
guard var json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
|
|
queuedFatalError("Expected dictionary in SARIF JSON")
|
|
}
|
|
|
|
// Extract and verify rules before removing them
|
|
if var runs = json["runs"] as? [[String: Any]],
|
|
var firstRun = runs.first,
|
|
var tool = firstRun["tool"] as? [String: Any],
|
|
var driver = tool["driver"] as? [String: Any],
|
|
let rules = driver["rules"] as? [[String: Any]] {
|
|
// Verify rules array is not empty
|
|
XCTAssertFalse(rules.isEmpty, "Rules array should not be empty")
|
|
|
|
// Verify rule structure
|
|
if let firstRule = rules.first {
|
|
XCTAssertNotNil(firstRule["id"], "Rule should have id")
|
|
XCTAssertNotNil(firstRule["shortDescription"], "Rule should have shortDescription")
|
|
XCTAssertNotNil(firstRule["fullDescription"], "Rule should have fullDescription")
|
|
XCTAssertNotNil(firstRule["helpUri"], "Rule should have helpUri")
|
|
}
|
|
|
|
// Remove rules array for comparison
|
|
driver.removeValue(forKey: "rules")
|
|
tool["driver"] = driver
|
|
firstRun["tool"] = tool
|
|
runs[0] = firstRun
|
|
json["runs"] = runs
|
|
}
|
|
|
|
return json.bridge()
|
|
}
|
|
|
|
func testJunitReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedJunitReporterOutput.xml",
|
|
reporterType: JUnitReporter.self
|
|
)
|
|
}
|
|
|
|
func testHTMLReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedHTMLReporterOutput.html",
|
|
reporterType: HTMLReporter.self
|
|
)
|
|
}
|
|
|
|
func testSonarQubeReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedSonarQubeReporterOutput.json",
|
|
reporterType: SonarQubeReporter.self,
|
|
stringConverter: { try jsonValue($0) }
|
|
)
|
|
}
|
|
|
|
func testMarkdownReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedMarkdownReporterOutput.md",
|
|
reporterType: MarkdownReporter.self
|
|
)
|
|
}
|
|
|
|
func testRelativePathReporter() throws {
|
|
try assertEqualContent(
|
|
referenceFile: "CannedRelativePathReporterOutput.txt",
|
|
reporterType: RelativePathReporter.self
|
|
)
|
|
}
|
|
|
|
func testRelativePathReporterPaths() {
|
|
let relativePath = "filename"
|
|
let absolutePath = FileManager.default.currentDirectoryPath + "/" + relativePath
|
|
let location = Location(file: absolutePath, line: 1, character: 2)
|
|
let violation = StyleViolation(ruleDescription: LineLengthRule.description,
|
|
location: location,
|
|
reason: "Violation Reason")
|
|
let result = RelativePathReporter.generateReport([violation])
|
|
XCTAssertFalse(result.contains(absolutePath))
|
|
XCTAssertTrue(result.contains(relativePath))
|
|
}
|
|
|
|
func testSummaryReporter() {
|
|
let expectedOutput = stringFromFile("CannedSummaryReporterOutput.txt")
|
|
.trimmingTrailingCharacters(in: .whitespacesAndNewlines)
|
|
let correctableViolation = StyleViolation(
|
|
ruleDescription: VerticalWhitespaceOpeningBracesRule.description,
|
|
location: Location(file: "filename", line: 1, character: 2),
|
|
reason: "Violation Reason"
|
|
)
|
|
let result = SummaryReporter.generateReport(violations + [correctableViolation])
|
|
XCTAssertEqual(result, expectedOutput)
|
|
}
|
|
|
|
func testSummaryReporterWithNoViolations() {
|
|
let expectedOutput = stringFromFile("CannedSummaryReporterNoViolationsOutput.txt")
|
|
.trimmingTrailingCharacters(in: .whitespacesAndNewlines)
|
|
let result = SummaryReporter.generateReport([])
|
|
XCTAssertEqual(result, expectedOutput)
|
|
}
|
|
|
|
private func assertEqualContent(referenceFile: String,
|
|
reporterType: any Reporter.Type,
|
|
stringConverter: (String) throws -> some Equatable = \.self,
|
|
file: StaticString = #filePath,
|
|
line: UInt = #line) throws {
|
|
let dateFormatter = DateFormatter()
|
|
dateFormatter.dateStyle = .short
|
|
let reference = stringFromFile(referenceFile).replacingOccurrences(
|
|
of: "${CURRENT_WORKING_DIRECTORY}",
|
|
with: FileManager.default.currentDirectoryPath
|
|
).replacingOccurrences(
|
|
of: "${SWIFTLINT_VERSION}",
|
|
with: SwiftLintFramework.Version.current.value
|
|
).replacingOccurrences(
|
|
of: "${TODAYS_DATE}",
|
|
with: dateFormatter.string(from: Date())
|
|
)
|
|
let reporterOutput = reporterType.generateReport(violations)
|
|
let convertedReference = try stringConverter(reference)
|
|
let convertedReporterOutput = try stringConverter(reporterOutput)
|
|
if convertedReference != convertedReporterOutput {
|
|
let referenceURL = URL(fileURLWithPath: "\(TestResources.path())/\(referenceFile)")
|
|
try reporterOutput.replacingOccurrences(
|
|
of: FileManager.default.currentDirectoryPath,
|
|
with: "${CURRENT_WORKING_DIRECTORY}"
|
|
).replacingOccurrences(
|
|
of: SwiftLintFramework.Version.current.value,
|
|
with: "${SWIFTLINT_VERSION}"
|
|
).replacingOccurrences(
|
|
of: dateFormatter.string(from: Date()),
|
|
with: "${TODAYS_DATE}"
|
|
)
|
|
.write(to: referenceURL, atomically: true, encoding: .utf8)
|
|
}
|
|
XCTAssertEqual(convertedReference, convertedReporterOutput, file: file, line: line)
|
|
}
|
|
}
|