Skip to content

Commit baa3f61

Browse files
committed
Handle XCTest extensions
1 parent e740bb3 commit baa3f61

File tree

3 files changed

+37
-20
lines changed

3 files changed

+37
-20
lines changed

Sources/SourceKitLSP/Swift/SwiftTestingScanner.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ final class SyntacticSwiftTestingTestScanner: SyntaxVisitor {
249249
}
250250

251251
let range = snapshot.range(of: node.positionAfterSkippingLeadingTrivia..<node.endPositionBeforeTrailingTrivia)
252-
// Members wont be extensions since extensions will only be at the top level.
252+
// Members won't be extensions since extensions will only be at the top level.
253253
let testItem = AnnotatedTestItem(
254254
testItem: TestItem(
255255
id: (parentTypeNames + typeNames).joined(separator: "/"),

Sources/SourceKitLSP/Swift/SyntacticTestIndex.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ fileprivate func testItems(in url: URL) async -> [AnnotatedTestItem] {
7979
syntaxTreeManager: syntaxTreeManager
8080
)
8181
async let xcTests = SyntacticSwiftXCTestScanner.findTestSymbols(in: snapshot, syntaxTreeManager: syntaxTreeManager)
82-
.map { AnnotatedTestItem(testItem: $0, isExtension: false) }
8382

8483
return await swiftTestingTests + xcTests
8584
}

Sources/SourceKitLSP/TestDiscovery.swift

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public struct AnnotatedTestItem {
2525
/// The test item to be annotated
2626
public var testItem: TestItem
2727

28-
/// Whether the `TestItem` is declared in an extension.
28+
/// Whether the `TestItem` is an extension.
2929
public var isExtension: Bool
3030

3131
public init(
@@ -360,7 +360,7 @@ final class SyntacticSwiftXCTestScanner: SyntaxVisitor {
360360
private var snapshot: DocumentSnapshot
361361

362362
/// The workspace symbols representing the found `XCTestCase` subclasses and test methods.
363-
private var result: [TestItem] = []
363+
private var result: [AnnotatedTestItem] = []
364364

365365
private init(snapshot: DocumentSnapshot) {
366366
self.snapshot = snapshot
@@ -370,7 +370,7 @@ final class SyntacticSwiftXCTestScanner: SyntaxVisitor {
370370
public static func findTestSymbols(
371371
in snapshot: DocumentSnapshot,
372372
syntaxTreeManager: SyntaxTreeManager
373-
) async -> [TestItem] {
373+
) async -> [AnnotatedTestItem] {
374374
guard snapshot.text.contains("XCTestCase") || snapshot.text.contains("test") else {
375375
// If the file contains tests that can be discovered syntactically, it needs to have a class inheriting from
376376
// `XCTestCase` or a function starting with `test`.
@@ -437,21 +437,25 @@ final class SyntacticSwiftXCTestScanner: SyntaxVisitor {
437437
return .visitChildren
438438
}
439439
let range = snapshot.range(of: node.positionAfterSkippingLeadingTrivia..<node.endPositionBeforeTrailingTrivia)
440-
let testItem = TestItem(
441-
id: node.name.text,
442-
label: node.name.text,
443-
disabled: false,
444-
style: TestStyle.xcTest,
445-
location: Location(uri: snapshot.uri, range: range),
446-
children: testMethods,
447-
tags: []
440+
let testItem = AnnotatedTestItem(
441+
testItem: TestItem(
442+
id: node.name.text,
443+
label: node.name.text,
444+
disabled: false,
445+
style: TestStyle.xcTest,
446+
location: Location(uri: snapshot.uri, range: range),
447+
children: testMethods,
448+
tags: []
449+
),
450+
isExtension: false
448451
)
449452
result.append(testItem)
450453
return .visitChildren
451454
}
452455

453456
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
454457
result += findTestMethods(in: node.memberBlock.members, containerName: node.extendedType.trimmedDescription)
458+
.map { AnnotatedTestItem(testItem: $0, isExtension: true) }
455459
return .visitChildren
456460
}
457461
}
@@ -485,6 +489,20 @@ extension TestItem {
485489
}
486490
}
487491

492+
extension AnnotatedTestItem {
493+
/// Use out-of-date semantic information to filter syntactic symbols.
494+
///
495+
/// Delegates to the `TestItem`'s `filterUsing(semanticSymbols:)` method to perform the filtering.
496+
fileprivate func filterUsing(semanticSymbols: [Symbol]?) -> AnnotatedTestItem? {
497+
guard let testItem = self.testItem.filterUsing(semanticSymbols: semanticSymbols) else {
498+
return nil
499+
}
500+
var test = self
501+
test.testItem = testItem
502+
return test
503+
}
504+
}
505+
488506
extension Array<AnnotatedTestItem> {
489507
/// When the test scanners discover tests in extensions they are captured in their own parent `TestItem`, not the
490508
/// `TestItem` generated from the class/struct's definition. This is largely because of the syntatic nature of the
@@ -569,13 +587,11 @@ extension Array<AnnotatedTestItem> {
569587
}
570588
}
571589

572-
// Filter out the items that have been merged into their parents, sorting the tests by location.
573-
// TestItems not in extensions should be priotitized first.
590+
// Sort the tests by location, prioritizing TestItems not in extensions.
574591
let sortedItems = itemDict.values
575-
.compactMap { $0 }
576592
.sorted { ($0.isExtension != $1.isExtension) ? !$0.isExtension : ($0.testItem.location < $1.testItem.location) }
577593

578-
return sortedItems.map {
594+
let result = sortedItems.map {
579595
guard !$0.testItem.children.isEmpty, mergedIds.contains($0.testItem.id) else {
580596
return $0.testItem
581597
}
@@ -585,20 +601,22 @@ extension Array<AnnotatedTestItem> {
585601
.mergingTestsInExtensions()
586602
return newItem
587603
}
604+
return result
588605
}
589606
}
590607

591608
extension SwiftLanguageService {
592-
public func syntacticDocumentTests(for uri: DocumentURI, in workspace: Workspace) async throws -> [AnnotatedTestItem]?
593-
{
609+
public func syntacticDocumentTests(
610+
for uri: DocumentURI,
611+
in workspace: Workspace
612+
) async throws -> [AnnotatedTestItem]? {
594613
let snapshot = try documentManager.latestSnapshot(uri)
595614
let semanticSymbols = workspace.index(checkedFor: .deletedFiles)?.symbols(inFilePath: snapshot.uri.pseudoPath)
596615
let xctestSymbols = await SyntacticSwiftXCTestScanner.findTestSymbols(
597616
in: snapshot,
598617
syntaxTreeManager: syntaxTreeManager
599618
)
600619
.compactMap { $0.filterUsing(semanticSymbols: semanticSymbols) }
601-
.map { AnnotatedTestItem(testItem: $0, isExtension: false) }
602620

603621
let swiftTestingSymbols = await SyntacticSwiftTestingTestScanner.findTestSymbols(
604622
in: snapshot,

0 commit comments

Comments
 (0)