Skip to content

Commit 7a5bdd9

Browse files
committed
Add PostProcessingSuggestionServiceMiddlewareTests
1 parent 0bf0b91 commit 7a5bdd9

4 files changed

Lines changed: 232 additions & 2 deletions

File tree

TestPlan.xctestplan

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@
140140
"identifier" : "XcodeInspectorTests",
141141
"name" : "XcodeInspectorTests"
142142
}
143+
},
144+
{
145+
"target" : {
146+
"containerPath" : "container:Tool",
147+
"identifier" : "SuggestionProviderTests",
148+
"name" : "SuggestionProviderTests"
149+
}
143150
}
144151
],
145152
"version" : 1

Tool/Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ let package = Package(
290290
.product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"),
291291
]),
292292

293+
.testTarget(name: "SuggestionProviderTests", dependencies: ["SuggestionProvider"]),
294+
293295
// MARK: - GitHub Copilot
294296

295297
.target(

Tool/Sources/SuggestionProvider/PostProcessingSuggestionServiceMiddleware.swift

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ public struct PostProcessingSuggestionServiceMiddleware: SuggestionServiceMiddle
1313

1414
return suggestions.compactMap {
1515
var suggestion = $0
16+
if suggestion.text.allSatisfy({ $0.isWhitespace || $0.isNewline }) { return nil }
1617
Self.removeTrailingWhitespacesAndNewlines(&suggestion)
17-
if suggestion.text.isEmpty { return nil }
18+
if !Self.checkIfSuggestionHasNoEffect(suggestion, request: request) { return nil }
1819
return suggestion
1920
}
2021
}
@@ -31,7 +32,40 @@ public struct PostProcessingSuggestionServiceMiddleware: SuggestionServiceMiddle
3132
_ suggestion: CodeSuggestion,
3233
request: SuggestionRequest
3334
) -> Bool {
34-
suggestion.text.isEmpty
35+
// We only check suggestions that are on a single line.
36+
if suggestion.range.isOneLine {
37+
let line = suggestion.range.start.line
38+
if line >= 0, line < request.lines.count {
39+
let replacingText = request.lines[line]
40+
41+
let start = suggestion.range.start.character
42+
let end = suggestion.range.end.character
43+
if let endIndex = replacingText.utf16.index(
44+
replacingText.startIndex,
45+
offsetBy: end,
46+
limitedBy: replacingText.endIndex
47+
),
48+
let startIndex = replacingText.utf16.index(
49+
replacingText.startIndex,
50+
offsetBy: start,
51+
limitedBy: endIndex
52+
),
53+
startIndex < endIndex
54+
{
55+
let replacingRange = startIndex..<endIndex
56+
// Build up the replaced text.
57+
let replacedText = replacingText.replacingCharacters(
58+
in: replacingRange,
59+
with: suggestion.text
60+
)
61+
62+
// If it's identical to the original text, ignore the suggestion.
63+
if replacedText == replacingText { return false }
64+
}
65+
}
66+
}
67+
68+
return true
3569
}
3670
}
3771

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import Foundation
2+
import SuggestionModel
3+
import XCTest
4+
5+
@testable import SuggestionProvider
6+
7+
class PostProcessingSuggestionServiceMiddlewareTests: XCTestCase {
8+
func createRequest(
9+
_ code: String = "",
10+
_ cursorPosition: CursorPosition = .zero
11+
) -> SuggestionRequest {
12+
let lines = code.breakLines()
13+
return SuggestionRequest(
14+
fileURL: URL(fileURLWithPath: "/path/to/file.swift"),
15+
relativePath: "file.swift",
16+
content: code,
17+
lines: lines,
18+
cursorPosition: cursorPosition,
19+
cursorOffset: {
20+
if cursorPosition == .outOfScope { return 0 }
21+
let prefixLines = if cursorPosition.line > 0 {
22+
lines[0..<cursorPosition.line]
23+
} else {
24+
[] as ArraySlice<String>
25+
}
26+
let offset = prefixLines.reduce(0) { $0 + $1.utf8.count }
27+
return offset
28+
+ lines[cursorPosition.line].prefix(cursorPosition.character).utf8.count
29+
}(),
30+
tabSize: 4,
31+
indentSize: 4,
32+
usesTabsForIndentation: false,
33+
relevantCodeSnippets: []
34+
)
35+
}
36+
37+
func test_trailing_whitespaces_and_new_lines_should_be_removed() async throws {
38+
let middleware = PostProcessingSuggestionServiceMiddleware()
39+
40+
let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in
41+
[
42+
.init(
43+
id: "1",
44+
text: "hello world \n \n",
45+
position: .init(line: 0, character: 0),
46+
range: .init(startPair: (0, 0), endPair: (0, 0))
47+
),
48+
.init(
49+
id: "2",
50+
text: " \n hello world \n \n",
51+
position: .init(line: 0, character: 0),
52+
range: .init(startPair: (0, 0), endPair: (0, 0))
53+
),
54+
]
55+
}
56+
57+
let suggestions = try await middleware.getSuggestion(
58+
createRequest(),
59+
configuration: .init(
60+
acceptsRelevantCodeSnippets: true,
61+
mixRelevantCodeSnippetsInSource: true,
62+
acceptsRelevantSnippetsFromOpenedFiles: true
63+
),
64+
next: handler
65+
)
66+
67+
XCTAssertEqual(suggestions, [
68+
.init(
69+
id: "1",
70+
text: "hello world",
71+
position: .init(line: 0, character: 0),
72+
range: .init(startPair: (0, 0), endPair: (0, 0))
73+
),
74+
.init(
75+
id: "2",
76+
text: " \n hello world",
77+
position: .init(line: 0, character: 0),
78+
range: .init(startPair: (0, 0), endPair: (0, 0))
79+
),
80+
])
81+
}
82+
83+
func test_remove_suggestions_that_contains_only_whitespaces_and_new_lines() async throws {
84+
let middleware = PostProcessingSuggestionServiceMiddleware()
85+
86+
let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in
87+
[
88+
.init(
89+
id: "1",
90+
text: "hello world \n \n",
91+
position: .init(line: 0, character: 0),
92+
range: .init(startPair: (0, 0), endPair: (0, 0))
93+
),
94+
.init(
95+
id: "2",
96+
text: " \n\n\r",
97+
position: .init(line: 0, character: 0),
98+
range: .init(startPair: (0, 0), endPair: (0, 0))
99+
),
100+
.init(
101+
id: "3",
102+
text: " ",
103+
position: .init(line: 0, character: 0),
104+
range: .init(startPair: (0, 0), endPair: (0, 0))
105+
),
106+
.init(
107+
id: "4",
108+
text: "\n\n\n",
109+
position: .init(line: 0, character: 0),
110+
range: .init(startPair: (0, 0), endPair: (0, 0))
111+
),
112+
]
113+
}
114+
115+
let suggestions = try await middleware.getSuggestion(
116+
createRequest(),
117+
configuration: .init(
118+
acceptsRelevantCodeSnippets: true,
119+
mixRelevantCodeSnippetsInSource: true,
120+
acceptsRelevantSnippetsFromOpenedFiles: true
121+
),
122+
next: handler
123+
)
124+
125+
XCTAssertEqual(suggestions, [
126+
.init(
127+
id: "1",
128+
text: "hello world",
129+
position: .init(line: 0, character: 0),
130+
range: .init(startPair: (0, 0), endPair: (0, 0))
131+
),
132+
])
133+
}
134+
135+
func test_remove_suggestion_that_takes_no_effect_after_being_accepted() async throws {
136+
let middleware = PostProcessingSuggestionServiceMiddleware()
137+
138+
let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in
139+
[
140+
.init(
141+
id: "1",
142+
text: "hello world \n \n",
143+
position: .init(line: 0, character: 0),
144+
range: .init(startPair: (0, 0), endPair: (0, 0))
145+
),
146+
.init(
147+
id: "2",
148+
text: "let cat = 100",
149+
position: .init(line: 0, character: 13),
150+
range: .init(startPair: (0, 0), endPair: (0, 13))
151+
),
152+
.init(
153+
id: "3",
154+
text: "let cat = 10",
155+
position: .init(line: 0, character: 13),
156+
range: .init(startPair: (0, 0), endPair: (0, 13))
157+
),
158+
]
159+
}
160+
161+
let suggestions = try await middleware.getSuggestion(
162+
createRequest("let cat = 100", .init(line: 0, character: 3)),
163+
configuration: .init(
164+
acceptsRelevantCodeSnippets: true,
165+
mixRelevantCodeSnippetsInSource: true,
166+
acceptsRelevantSnippetsFromOpenedFiles: true
167+
),
168+
next: handler
169+
)
170+
171+
XCTAssertEqual(suggestions, [
172+
.init(
173+
id: "1",
174+
text: "hello world",
175+
position: .init(line: 0, character: 0),
176+
range: .init(startPair: (0, 0), endPair: (0, 0))
177+
),
178+
.init(
179+
id: "3",
180+
text: "let cat = 10",
181+
position: .init(line: 0, character: 13),
182+
range: .init(startPair: (0, 0), endPair: (0, 13))
183+
),
184+
])
185+
}
186+
}
187+

0 commit comments

Comments
 (0)