-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cyclomatic complexity ignores case statments #1299
Changes from 12 commits
ba80b24
42a7186
7124f7f
975e7bd
d561b1a
3d1fe8c
b68219d
fe4aa1e
69ec69e
f7ac3f1
a6175fa
bc0af08
eff0551
50ef6e1
bc3435f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// | ||
// CyclomaticComplexityConfiguration.swift | ||
// SwiftLint | ||
// | ||
// Created by Mike Welles on 2/9/17. | ||
// Copyright © 2017 Realm. All rights reserved. | ||
// | ||
import Foundation | ||
import SourceKittenFramework | ||
|
||
private enum ConfigurationKey: String { | ||
case warning = "warning" | ||
case error = "error" | ||
case ignoresCaseStatements = "ignores_case_statements" | ||
} | ||
|
||
public struct CyclomaticComplexityConfiguration: RuleConfiguration, Equatable { | ||
public var consoleDescription: String { | ||
return length.consoleDescription + ", ignores switch statements: \(ignoresCaseStatements)" | ||
} | ||
|
||
public static let defaultComplexityStatements: Set<StatementKind> = [ | ||
.forEach, | ||
.if, | ||
.guard, | ||
.for, | ||
.repeatWhile, | ||
.while, | ||
.case | ||
] | ||
|
||
private(set) public var length: SeverityLevelsConfiguration | ||
|
||
private(set) public var complexityStatements: Set<StatementKind> | ||
|
||
private(set) public var ignoresCaseStatements: Bool { | ||
didSet { | ||
if ignoresCaseStatements { | ||
complexityStatements.remove(.case) | ||
} else { | ||
complexityStatements.insert(.case) | ||
} | ||
} | ||
} | ||
|
||
var params: [RuleParameter<Int>] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one, no, it's a virtual getter -- returns length.params. The, others yes, updating PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jpsim -- Following up on this, since it's the one remaining issue. I can't add a set to this accessor, it's read-only -- returns length.params, which is itself read-only. |
||
return length.params | ||
} | ||
|
||
public init(warning: Int, error: Int?, ignoresCaseStatements: Bool = false) { | ||
self.length = SeverityLevelsConfiguration(warning: warning, error: error) | ||
self.complexityStatements = type(of: self).defaultComplexityStatements | ||
self.ignoresCaseStatements = ignoresCaseStatements | ||
} | ||
|
||
public mutating func apply(configuration: Any) throws { | ||
if let configurationArray = [Int].array(of: configuration), | ||
!configurationArray.isEmpty { | ||
let warning = configurationArray[0] | ||
let error = (configurationArray.count > 1) ? configurationArray[1] : nil | ||
length = SeverityLevelsConfiguration(warning: warning, error: error) | ||
} else if let configDict = configuration as? [String: Any], !configDict.isEmpty { | ||
for (string, value) in configDict { | ||
guard let key = ConfigurationKey(rawValue:string) else { | ||
throw ConfigurationError.unknownConfiguration | ||
} | ||
switch (key, value) { | ||
case (.error, let intValue as Int): | ||
length.error = intValue | ||
case (.warning, let intValue as Int): | ||
length.warning = intValue | ||
case (.ignoresCaseStatements, let boolValue as Bool): | ||
ignoresCaseStatements = boolValue | ||
default: | ||
throw ConfigurationError.unknownConfiguration | ||
} | ||
} | ||
} else { | ||
throw ConfigurationError.unknownConfiguration | ||
} | ||
} | ||
|
||
} | ||
|
||
public func == (lhs: CyclomaticComplexityConfiguration, rhs: CyclomaticComplexityConfiguration) -> Bool { | ||
return lhs.length == rhs.length && | ||
lhs.ignoresCaseStatements == rhs.ignoresCaseStatements | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// | ||
// CyclomaticComplexityConfigurationTests.swift | ||
// SwiftLint | ||
// | ||
// Created by Michael Welles on 2/15/17. | ||
// Copyright © 2017 Realm. All rights reserved. | ||
// | ||
|
||
import SourceKittenFramework | ||
@testable import SwiftLintFramework | ||
import XCTest | ||
|
||
class CyclomaticComplexityConfigurationTests: XCTestCase { | ||
func testCyclomaticComplexityConfigurationInitializerSetsLevels() { | ||
let warning = 10 | ||
let error = 30 | ||
let level = SeverityLevelsConfiguration(warning: warning, error: error) | ||
let configuration1 = CyclomaticComplexityConfiguration(warning: warning, | ||
error: error) | ||
XCTAssertEqual(configuration1.length, level) | ||
|
||
let length2 = SeverityLevelsConfiguration(warning: warning, error: nil) | ||
let configuration2 = CyclomaticComplexityConfiguration(warning: warning, | ||
error: nil) | ||
XCTAssertEqual(configuration2.length, length2) | ||
} | ||
|
||
func testCyclomaticComplexityConfigurationInitializerSetsIgnoresCaseStatements() { | ||
let configuration1 = CyclomaticComplexityConfiguration(warning: 10, | ||
error: 30, | ||
ignoresCaseStatements: true) | ||
XCTAssertTrue(configuration1.ignoresCaseStatements) | ||
|
||
let configuration2 = CyclomaticComplexityConfiguration(warning:0, | ||
error: 30) | ||
XCTAssertFalse(configuration2.ignoresCaseStatements) | ||
} | ||
|
||
func testCyclomaticComplexityConfigurationApplyConfigurationWithDictionary() { | ||
var configuration = CyclomaticComplexityConfiguration(warning: 0, error: 0) | ||
|
||
let warning1 = 10 | ||
let error1 = 30 | ||
let length1 = SeverityLevelsConfiguration(warning: warning1, error: error1) | ||
let config1: [String: Any] = ["warning": warning1, | ||
"error": error1, | ||
"ignores_case_statements": true] | ||
|
||
let warning2 = 20 | ||
let error2 = 40 | ||
let length2 = SeverityLevelsConfiguration(warning: warning2, error: error2) | ||
let config2: [String: Int] = ["warning": warning2, "error": error2] | ||
let config3: [String: Bool] = ["ignores_case_statements": false] | ||
|
||
do { | ||
try configuration.apply(configuration: config1) | ||
XCTAssertEqual(configuration.length, length1) | ||
XCTAssertTrue(configuration.ignoresCaseStatements) | ||
|
||
try configuration.apply(configuration: config2) | ||
XCTAssertEqual(configuration.length, length2) | ||
XCTAssertTrue(configuration.ignoresCaseStatements) | ||
|
||
try configuration.apply(configuration: config3) | ||
XCTAssertEqual(configuration.length, length2) | ||
XCTAssertFalse(configuration.ignoresCaseStatements) | ||
} catch { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can mark the test as |
||
XCTFail() | ||
} | ||
} | ||
|
||
func testCyclomaticComplexityConfigurationThrowsOnBadConfigValues() { | ||
let badConfigs: [[String: Any]] = [ | ||
["warning": true], | ||
["ignores_case_statements": 300], | ||
["unsupported_key": "unsupported key is unsupported"] | ||
] | ||
|
||
for badConfig in badConfigs { | ||
var configuration = CyclomaticComplexityConfiguration(warning: 100, error: 150) | ||
checkError(ConfigurationError.unknownConfiguration) { | ||
try configuration.apply(configuration: badConfig) | ||
} | ||
} | ||
} | ||
|
||
func testCyclomaticComplexityConfigurationCompares() { | ||
let config1 = CyclomaticComplexityConfiguration(warning: 10, error: 30) | ||
let config2 = CyclomaticComplexityConfiguration(warning: 10, error: 30, ignoresCaseStatements: true) | ||
let config3 = CyclomaticComplexityConfiguration(warning: 10, error: 30, ignoresCaseStatements: false) | ||
let config4 = CyclomaticComplexityConfiguration(warning: 10, error: 40) | ||
let config5 = CyclomaticComplexityConfiguration(warning: 20, error: 30) | ||
XCTAssertFalse(config1 == config2) | ||
XCTAssertTrue(config1 == config3) | ||
XCTAssertFalse(config1 == config4) | ||
XCTAssertFalse(config1 == config5) | ||
} | ||
|
||
} | ||
|
||
extension CyclomaticComplexityConfigurationTests { | ||
static var allTests: [(String, (CyclomaticComplexityConfigurationTests) -> () throws -> Void)] { | ||
return [ | ||
("testCyclomaticComplexityConfigurationInitializerSetsLevels", | ||
testCyclomaticComplexityConfigurationInitializerSetsLevels), | ||
("testCyclomaticComplexityConfigurationInitializerSetsIgnoresCaseStatements", | ||
testCyclomaticComplexityConfigurationInitializerSetsIgnoresCaseStatements), | ||
("testCyclomaticComplexityConfigurationThrowsOnBadConfigValues", | ||
testCyclomaticComplexityConfigurationThrowsOnBadConfigValues), | ||
("testCyclomaticComplexityConfigurationApplyConfigurationWithDictionary", | ||
testCyclomaticComplexityConfigurationApplyConfigurationWithDictionary), | ||
("testCyclomaticComplexityConfigurationCompares", | ||
testCyclomaticComplexityConfigurationCompares) | ||
] | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing new line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, updated LinuxMain + the items mentioned inline.