Add ability to run only one(focused) example (#3911)

* Add ability to focus on a specific test example

* Update CHANGELOG.md

Co-authored-by: JP Simard <jp@jpsim.com>

* Update CONTRIBUTING.md

Co-authored-by: JP Simard <jp@jpsim.com>

Co-authored-by: JP Simard <jp@jpsim.com>
This commit is contained in:
Paul Taykalo 2022-03-22 19:21:25 +02:00 committed by GitHub
parent 702b36a781
commit 1616023b63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 7 deletions

View File

@ -48,6 +48,10 @@
environments, such as in Swift Package Manager plugins. environments, such as in Swift Package Manager plugins.
[Juozas Valancius](https://github.com/juozasvalancius) [Juozas Valancius](https://github.com/juozasvalancius)
* Add ability to run only one (focused) example.
[PaulTaykalo](https://github.com/PaulTaykalo)
[#3911](https://github.com/realm/SwiftLint/issues/3911)
#### Bug Fixes #### Bug Fixes
* Extend `class_delegate_protocol` to correctly identify cases with the protocol * Extend `class_delegate_protocol` to correctly identify cases with the protocol

View File

@ -72,6 +72,19 @@ over time. This way adding a unit test for your new Rule is just a matter of
adding a test case in `RulesTests.swift` which simply calls adding a test case in `RulesTests.swift` which simply calls
`verifyRule(YourNewRule.description)`. `verifyRule(YourNewRule.description)`.
For debugging purposes examples can be marked as `focused`. If there are any
focused examples found, then only those will be run when running tests for that rule.
```
nonTriggeringExamples: [
Example("let x: [Int]"),
Example("let x: [Int: String]").focused() // only this one will be run in tests
],
triggeringExamples: [
Example("let x: ↓Array<String>"),
Example("let x: ↓Dictionary<Int, String>")
]
```
### `ConfigurationProviderRule` ### `ConfigurationProviderRule`
If your rule supports user-configurable options via `.swiftlint.yml`, you can If your rule supports user-configurable options via `.swiftlint.yml`, you can

View File

@ -32,6 +32,9 @@ public struct Example {
/// pathological use cases which are indeed important to test but not helpful for understanding can be /// pathological use cases which are indeed important to test but not helpful for understanding can be
/// hidden from the documentation with this option. /// hidden from the documentation with this option.
let excludeFromDocumentation: Bool let excludeFromDocumentation: Bool
/// Specifies whether the test example should be the only example run during the current test case execution.
var isFocused: Bool
} }
public extension Example { public extension Example {
@ -55,6 +58,7 @@ public extension Example {
self.file = file self.file = file
self.line = line self.line = line
self.excludeFromDocumentation = excludeFromDocumentation self.excludeFromDocumentation = excludeFromDocumentation
self.isFocused = false
} }
/// Returns the same example, but with the `code` that is passed in /// Returns the same example, but with the `code` that is passed in
@ -69,6 +73,13 @@ public extension Example {
func removingViolationMarkers() -> Example { func removingViolationMarkers() -> Example {
return with(code: code.replacingOccurrences(of: "", with: "")) return with(code: code.replacingOccurrences(of: "", with: ""))
} }
/// Makes the current example focused.
func focused() -> Example {
var new = self
new.isFocused = true
return new
}
} }
extension Example: Hashable { extension Example: Hashable {

View File

@ -356,7 +356,11 @@ extension XCTestCase {
requiresFileOnDisk: ruleDescription.requiresFileOnDisk, requiresFileOnDisk: ruleDescription.requiresFileOnDisk,
file: file, line: line) file: file, line: line)
} }
func makeViolations(_ example: Example) -> [StyleViolation] {
return violations(example, config: config, requiresFileOnDisk: ruleDescription.requiresFileOnDisk)
}
let ruleDescription = ruleDescription.focused()
let triggers = ruleDescription.triggeringExamples let triggers = ruleDescription.triggeringExamples
let nonTriggers = ruleDescription.nonTriggeringExamples let nonTriggers = ruleDescription.nonTriggeringExamples
verify(triggers: triggers, nonTriggers: nonTriggers) verify(triggers: triggers, nonTriggers: nonTriggers)
@ -369,10 +373,6 @@ extension XCTestCase {
verify(triggers: triggers.map(addShebang), nonTriggers: nonTriggers.map(addShebang)) verify(triggers: triggers.map(addShebang), nonTriggers: nonTriggers.map(addShebang))
} }
func makeViolations(_ example: Example) -> [StyleViolation] {
return violations(example, config: config, requiresFileOnDisk: ruleDescription.requiresFileOnDisk)
}
// Comment doesn't violate // Comment doesn't violate
if !skipCommentTests { if !skipCommentTests {
XCTAssertEqual( XCTAssertEqual(
@ -401,6 +401,8 @@ extension XCTestCase {
func verifyCorrections(_ ruleDescription: RuleDescription, config: Configuration, func verifyCorrections(_ ruleDescription: RuleDescription, config: Configuration,
disableCommands: [String], testMultiByteOffsets: Bool) { disableCommands: [String], testMultiByteOffsets: Bool) {
let ruleDescription = ruleDescription.focused()
parserDiagnosticsDisabledForTests = true parserDiagnosticsDisabledForTests = true
// corrections // corrections
@ -509,3 +511,37 @@ extension XCTestCase {
} }
} }
} }
private struct FocusedRuleDescription {
let nonTriggeringExamples: [Example]
let triggeringExamples: [Example]
let corrections: [Example: Example]
init(rule: RuleDescription) {
let nonTriggering = rule.nonTriggeringExamples.filter(\.isFocused)
let triggering = rule.triggeringExamples.filter(\.isFocused)
let corrections = rule.corrections.filter { _, value in value.isFocused }
let anyFocused = nonTriggering.isNotEmpty || triggering.isNotEmpty || corrections.isNotEmpty
if anyFocused {
self.nonTriggeringExamples = nonTriggering
self.triggeringExamples = triggering
self.corrections = corrections
#if DISABLE_FOCUSED_EXAMPLES
(nonTriggering + triggering + corrections.values).forEach { example in
XCTFail("Focused examples are disabled", file: example.file, line: example.line)
}
#endif
} else {
self.nonTriggeringExamples = rule.nonTriggeringExamples
self.triggeringExamples = rule.triggeringExamples
self.corrections = rule.corrections
}
}
}
private extension RuleDescription {
func focused() -> FocusedRuleDescription {
return FocusedRuleDescription(rule: self)
}
}

View File

@ -14,7 +14,7 @@ jobs:
containerImage: swift:5.6 containerImage: swift:5.6
container: $[ variables['containerImage'] ] container: $[ variables['containerImage'] ]
steps: steps:
- script: swift test --parallel - script: swift test --parallel -Xswiftc -DDISABLE_FOCUSED_EXAMPLES
displayName: swift test displayName: swift test
- job: Xcode - job: Xcode
@ -32,7 +32,7 @@ jobs:
sw_vers sw_vers
xcodebuild -version xcodebuild -version
displayName: Version Informations displayName: Version Informations
- script: xcodebuild -scheme swiftlint test -destination "platform=macOS" - script: xcodebuild -scheme swiftlint test -destination "platform=macOS" OTHER_SWIFT_FLAGS="-D DISABLE_FOCUSED_EXAMPLES"
displayName: xcodebuild test displayName: xcodebuild test
- job: SwiftPM - job: SwiftPM
@ -50,7 +50,7 @@ jobs:
sw_vers sw_vers
xcodebuild -version xcodebuild -version
displayName: Version Informations displayName: Version Informations
- script: swift test --parallel --enable-code-coverage - script: swift test --parallel --enable-code-coverage -Xswiftc -DDISABLE_FOCUSED_EXAMPLES
displayName: swift test displayName: swift test
- script: | - script: |
xcrun llvm-cov export -format="lcov" .build/debug/SwiftLintPackageTests.xctest/Contents/MacOS/SwiftLintPackageTests -instr-profile .build/debug/codecov/default.profdata > coverage.lcov xcrun llvm-cov export -format="lcov" .build/debug/SwiftLintPackageTests.xctest/Contents/MacOS/SwiftLintPackageTests -instr-profile .build/debug/codecov/default.profdata > coverage.lcov