SwiftLint/Tests/SwiftLintFrameworkTests/CommandTests.swift

446 lines
18 KiB
Swift

// swiftlint:disable file_length
import Foundation
import SourceKittenFramework
@_spi(TestHelper)
@testable import SwiftLintFramework
import XCTest
private extension Command {
init?(string: String) {
let nsString = string.bridge()
guard nsString.length > 7 else { return nil }
let subString = nsString.substring(with: NSRange(location: 3, length: nsString.length - 4))
self.init(actionString: subString, line: 1, character: nsString.length)
}
}
class CommandTests: SwiftLintTestCase {
// MARK: Command Creation
func testNoCommandsInEmptyFile() {
let file = SwiftLintFile(contents: "")
XCTAssertEqual(file.commands(), [])
}
func testEmptyString() {
XCTAssertNil(Command(string: ""))
}
func testDisable() {
let input = "// swiftlint:disable rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 29, modifier: nil)
XCTAssertEqual(file.commands(), [expected])
XCTAssertEqual(Command(string: input), expected)
}
func testDisablePrevious() {
let input = "// swiftlint:disable:previous rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .previous)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testDisableThis() {
let input = "// swiftlint:disable:this rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 34, modifier: .this)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testDisableNext() {
let input = "// swiftlint:disable:next rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 34, modifier: .next)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testEnable() {
let input = "// swiftlint:enable rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 28, modifier: nil)
XCTAssertEqual(file.commands(), [expected])
XCTAssertEqual(Command(string: input), expected)
}
func testEnablePrevious() {
let input = "// swiftlint:enable:previous rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 37,
modifier: .previous)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testEnableThis() {
let input = "// swiftlint:enable:this rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 33, modifier: .this)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testEnableNext() {
let input = "// swiftlint:enable:next rule_id\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 33, modifier: .next)
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testTrailingComment() {
let input = "// swiftlint:enable:next rule_id - Comment\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 43, modifier: .next,
trailingComment: "Comment")
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testTrailingCommentWithUrl() {
let input = "// swiftlint:enable:next rule_id - Comment with URL https://github.com/realm/SwiftLint\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 87, modifier: .next,
trailingComment: "Comment with URL https://github.com/realm/SwiftLint")
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
func testTrailingCommentUrlOnly() {
let input = "// swiftlint:enable:next rule_id - https://github.com/realm/SwiftLint\n"
let file = SwiftLintFile(contents: input)
let expected = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 70, modifier: .next,
trailingComment: "https://github.com/realm/SwiftLint")
XCTAssertEqual(file.commands(), expected.expand())
XCTAssertEqual(Command(string: input), expected)
}
// MARK: Action
func testActionInverse() {
XCTAssertEqual(Command.Action.enable.inverse(), .disable)
XCTAssertEqual(Command.Action.disable.inverse(), .enable)
}
// MARK: Command Expansion
func testNoModifierCommandExpandsToItself() {
do {
let command = Command(action: .disable, ruleIdentifiers: ["rule_id"])
XCTAssertEqual(command.expand(), [command])
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["rule_id"])
XCTAssertEqual(command.expand(), [command])
}
do {
let command = Command(action: .disable, ruleIdentifiers: ["1", "2"])
XCTAssertEqual(command.expand(), [command])
}
}
func testExpandPreviousCommand() {
do {
let command = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .previous)
let expanded = [
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 0, character: nil),
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 0, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .previous)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 0, character: nil),
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 0, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 1, character: 38,
modifier: .previous)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 0, character: nil),
Command(action: .disable, ruleIdentifiers: ["1", "2"], line: 0, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
}
func testExpandThisCommand() {
do {
let command = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .this)
let expanded = [
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: nil),
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .this)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: nil),
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 1, character: 38,
modifier: .this)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 1, character: nil),
Command(action: .disable, ruleIdentifiers: ["1", "2"], line: 1, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
}
func testExpandNextCommand() {
do {
let command = Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .next)
let expanded = [
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 2, character: nil),
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 2, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 1, character: 38,
modifier: .next)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["rule_id"], line: 2, character: nil),
Command(action: .disable, ruleIdentifiers: ["rule_id"], line: 2, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
do {
let command = Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 1, character: 38,
modifier: .next)
let expanded = [
Command(action: .enable, ruleIdentifiers: ["1", "2"], line: 2, character: nil),
Command(action: .disable, ruleIdentifiers: ["1", "2"], line: 2, character: .max)
]
XCTAssertEqual(command.expand(), expanded)
}
}
// MARK: Superfluous Disable Command Detection
func testSuperfluousDisableCommands() {
XCTAssertEqual(
violations(Example("// swiftlint:disable nesting\nprint(123)\n")).map { $0.ruleIdentifier },
["blanket_disable_command", "superfluous_disable_command"]
)
XCTAssertEqual(
violations(Example("// swiftlint:disable:next nesting\nprint(123)\n"))[0].ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123) // swiftlint:disable:this nesting\n"))[0].ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting\n"))[0].ruleIdentifier,
"superfluous_disable_command"
)
}
func testDisableAllOverridesSuperfluousDisableCommand() {
XCTAssert(
violations(
Example("""
// swiftlint:disable all
// swiftlint:disable nesting
print(123)
// swiftlint:enable nesting
// swiftlint:enable all
""")
).isEmpty
)
XCTAssert(
violations(
Example("""
// swiftlint:disable all
// swiftlint:disable:next nesting
print(123)
// swiftlint:enable all
""")
).isEmpty
)
XCTAssert(
violations(
Example("""
// swiftlint:disable all
// swiftlint:disable:this nesting
print(123)
// swiftlint:enable all
""")
).isEmpty
)
XCTAssert(
violations(
Example("// swiftlint:disable all\n// swiftlint:disable:previous nesting\nprint(123)\n")
).isEmpty
)
}
func testSuperfluousDisableCommandsIgnoreDelimiter() {
let longComment = "Comment with a large number of words that shouldn't register as superfluous"
XCTAssertEqual(
violations(Example("// swiftlint:disable nesting - \(longComment)\nprint(123)\n")).map {
$0.ruleIdentifier
},
["blanket_disable_command", "superfluous_disable_command"]
)
XCTAssertEqual(
violations(Example("// swiftlint:disable:next nesting - Comment\nprint(123)\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123) // swiftlint:disable:this nesting - Comment\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting - Comment\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
}
func testInvalidDisableCommands() {
XCTAssertEqual(
violations(Example("// swiftlint:disable nesting_foo\n" +
"print(123)\n" +
"// swiftlint:enable nesting_foo\n"))[0].ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("// swiftlint:disable:next nesting_foo\nprint(123)\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123) // swiftlint:disable:this nesting_foo\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting_foo\n"))[0]
.ruleIdentifier,
"superfluous_disable_command"
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting_foo \n")).count,
1
)
let example = Example("// swiftlint:disable nesting this is a comment\n// swiftlint:enable nesting\n")
let multipleViolations = violations(example)
XCTAssertEqual(multipleViolations.filter({ $0.ruleIdentifier == "superfluous_disable_command" }).count, 9)
XCTAssertEqual(multipleViolations.filter({ $0.ruleIdentifier == "blanket_disable_command" }).count, 4)
let onlyNonExistentRulesViolations = violations(Example("// swiftlint:disable this is a comment\n"))
XCTAssertEqual(
onlyNonExistentRulesViolations.filter({ $0.ruleIdentifier == "superfluous_disable_command" }).count, 4
)
XCTAssertEqual(onlyNonExistentRulesViolations.filter({
$0.ruleIdentifier == "blanket_disable_command"
}).count, 4)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting_foo\n"))[0].reason,
"'nesting_foo' is not a valid SwiftLint rule; remove it from the disable command"
)
XCTAssertEqual(violations(Example("/* swiftlint:disable nesting */\n")).count, 2)
}
func testSuperfluousDisableCommandsDisabled() {
XCTAssertEqual(
violations(Example("// swiftlint:disable superfluous_disable_command nesting\n" +
"print(123)\n" +
"// swiftlint:enable superfluous_disable_command nesting\n")),
[]
)
XCTAssertEqual(
violations(Example("// swiftlint:disable superfluous_disable_command\n" +
"// swiftlint:disable nesting\n" +
"print(123)\n" +
"// swiftlint:enable superfluous_disable_command nesting\n")),
[]
)
XCTAssertEqual(
violations(Example("// swiftlint:disable:next superfluous_disable_command nesting\nprint(123)\n")),
[]
)
XCTAssertEqual(
violations(Example("print(123) // swiftlint:disable:this superfluous_disable_command nesting\n")),
[]
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous superfluous_disable_command nesting\n")),
[]
)
}
func testSuperfluousDisableCommandsDisabledOnConfiguration() {
let rulesMode = Configuration.RulesMode.default(disabled: ["superfluous_disable_command"], optIn: [])
let configuration = Configuration(rulesMode: rulesMode)
XCTAssertEqual(
violations(Example("// swiftlint:disable nesting\n" +
"print(123)\n" +
"// swiftlint:enable nesting\n"), config: configuration),
[]
)
XCTAssertEqual(
violations(Example("// swiftlint:disable:next nesting\nprint(123)\n"), config: configuration),
[]
)
XCTAssertEqual(
violations(Example("print(123) // swiftlint:disable:this nesting\n"), config: configuration),
[]
)
XCTAssertEqual(
violations(Example("print(123)\n// swiftlint:disable:previous nesting\n"), config: configuration),
[]
)
}
func testSuperfluousDisableCommandsDisabledWhenAllRulesDisabled() {
XCTAssertEqual(
violations(Example("""
// swiftlint:disable all
// swiftlint:disable non_existent_rule_name
// swiftlint:enable non_existent_rule_name
// swiftlint:enable all
"""
)),
[]
)
XCTAssertEqual(
violations(Example("""
// swiftlint:disable superfluous_disable_command
// swiftlint:disable non_existent_rule_name
// swiftlint:enable non_existent_rule_name
// swiftlint:enable superfluous_disable_command
"""
)),
[]
)
}
}