-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TestCaseAccessibilityRule (#3376)
Co-authored-by: JP Simard <jp@jpsim.com>
- Loading branch information
Showing
11 changed files
with
221 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import SourceKittenFramework | ||
|
||
private let testVariableNames: Set = [ | ||
"allTests" | ||
] | ||
|
||
enum XCTestHelpers { | ||
static func isXCTestMember(kind: SwiftDeclarationKind, name: String, | ||
attributes: [SwiftDeclarationAttributeKind]) -> Bool { | ||
return attributes.contains(.override) | ||
|| (kind == .functionMethodInstance && name.hasPrefix("test")) | ||
|| ([.varStatic, .varClass].contains(kind) && testVariableNames.contains(name)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
Source/SwiftLintFramework/Rules/Lint/TestCaseAccessibilityRule.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import SourceKittenFramework | ||
|
||
public struct TestCaseAccessibilityRule: Rule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule { | ||
public var configuration = TestCaseAccessibilityConfiguration() | ||
|
||
public init() {} | ||
|
||
public static let description = RuleDescription( | ||
identifier: "test_case_accessibility", | ||
name: "Test case accessibility", | ||
description: "Test cases should only contain private non-test members.", | ||
kind: .lint, | ||
nonTriggeringExamples: TestCaseAccessibilityRuleExamples.nonTriggeringExamples, | ||
triggeringExamples: TestCaseAccessibilityRuleExamples.triggeringExamples | ||
) | ||
|
||
public func validate(file: SwiftLintFile) -> [StyleViolation] { | ||
return testClasses(in: file).flatMap { violations(in: file, for: $0) } | ||
} | ||
|
||
// MARK: - Private | ||
|
||
private func testClasses(in file: SwiftLintFile) -> [SourceKittenDictionary] { | ||
let dict = file.structureDictionary | ||
return dict.substructure.filter { dictionary in | ||
dictionary.declarationKind == .class && dictionary.inheritedTypes.contains("XCTestCase") | ||
} | ||
} | ||
|
||
private func violations(in file: SwiftLintFile, | ||
for dictionary: SourceKittenDictionary) -> [StyleViolation] { | ||
return dictionary.substructure.compactMap { subDictionary -> StyleViolation? in | ||
guard | ||
let kind = subDictionary.declarationKind, | ||
kind != .varLocal, | ||
let name = subDictionary.name, | ||
!isXCTestMember(kind: kind, name: name, attributes: subDictionary.enclosedSwiftAttributes), | ||
let offset = subDictionary.offset, | ||
subDictionary.accessibility?.isPrivate != true else { return nil } | ||
|
||
return StyleViolation(ruleDescription: Self.description, | ||
severity: configuration.severity, | ||
location: Location(file: file, byteOffset: offset)) | ||
} | ||
} | ||
|
||
private func isXCTestMember(kind: SwiftDeclarationKind, name: String, | ||
attributes: [SwiftDeclarationAttributeKind]) -> Bool { | ||
return XCTestHelpers.isXCTestMember(kind: kind, name: name, attributes: attributes) | ||
|| configuration.allowedPrefixes.contains(where: name.hasPrefix) | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
Source/SwiftLintFramework/Rules/Lint/TestCaseAccessibilityRuleExamples.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
internal struct TestCaseAccessibilityRuleExamples { | ||
static let nonTriggeringExamples = [ | ||
// Valid XCTestCase class | ||
|
||
Example(""" | ||
let foo: String? | ||
class FooTests: XCTestCase { | ||
static let allTests: [String] = [] | ||
private let foo: String { | ||
let nestedMember = "hi" | ||
return nestedMember | ||
} | ||
override static func setUp() { | ||
super.setUp() | ||
} | ||
override func setUp() { | ||
super.setUp() | ||
} | ||
override func setUpWithError() throws { | ||
try super.setUpWithError() | ||
} | ||
override static func tearDown() { | ||
super.tearDown() | ||
} | ||
override func tearDown() { | ||
super.tearDown() | ||
} | ||
override func tearDownWithError() { | ||
try super.tearDownWithError() | ||
} | ||
override func someFutureXCTestFunction() { | ||
super.someFutureXCTestFunction() | ||
} | ||
func testFoo() { | ||
XCTAssertTrue(true) | ||
} | ||
} | ||
"""), | ||
|
||
// Not an XCTestCase class | ||
|
||
Example(""" | ||
class Foobar { | ||
func setUp() {} | ||
func tearDown() {} | ||
func testFoo() {} | ||
} | ||
""") | ||
] | ||
|
||
static let triggeringExamples = [ | ||
Example(""" | ||
class FooTests: XCTestCase { | ||
↓var foo: String? | ||
↓let bar: String? | ||
↓static func foo() {} | ||
↓func setUp(withParam: String) {} | ||
↓func foobar() {} | ||
↓func not_testBar() {} | ||
↓enum Nested {} | ||
↓static func testFoo() {} | ||
↓static func allTests() {} | ||
} | ||
final class BarTests: XCTestCase { | ||
↓class Nested {} | ||
} | ||
""") | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
Source/SwiftLintFramework/Rules/RuleConfigurations/TestCaseAccessibilityConfiguration.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
public struct TestCaseAccessibilityConfiguration: RuleConfiguration, Equatable { | ||
public private(set) var severityConfiguration = SeverityConfiguration(.warning) | ||
public private(set) var allowedPrefixes: Set<String> = [] | ||
|
||
public var consoleDescription: String { | ||
return severityConfiguration.consoleDescription + | ||
", allowed_prefixes: [\(allowedPrefixes)]" | ||
} | ||
|
||
public mutating func apply(configuration: Any) throws { | ||
guard let configuration = configuration as? [String: Any] else { | ||
throw ConfigurationError.unknownConfiguration | ||
} | ||
|
||
if let severityString = configuration["severity"] as? String { | ||
try severityConfiguration.apply(configuration: severityString) | ||
} | ||
|
||
if let allowedPrefixes = configuration["allowed_prefixes"] as? [String] { | ||
self.allowedPrefixes = Set(allowedPrefixes) | ||
} | ||
} | ||
|
||
public var severity: ViolationSeverity { | ||
return severityConfiguration.severity | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters