forked from dotnet/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
QuickInfoProviderTests.fs
320 lines (272 loc) · 11.6 KB
/
QuickInfoProviderTests.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace FSharp.Editor.Tests
open System
open System.Threading
open Xunit
open FSharp.Compiler.EditorServices
open FSharp.Compiler.CodeAnalysis
open Microsoft.VisualStudio.FSharp.Editor
open Microsoft.VisualStudio.FSharp.Editor.QuickInfo
open FSharp.Editor.Tests.Helpers
open FSharp.Test
open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks
type public AssemblyResolverTestFixture() =
member public __.Init() = AssemblyResolver.addResolver ()
module QuickInfoProviderTests =
type Expected =
| QuickInfo of description: string * docs: string
| Desc of string
| Empty
| Error
type TestCase = TestCase of prompt: string * Expected
let private normalizeLineEnds (s: string) =
s.Replace("\r\n", "\n").Replace("\n\n", "\n")
let mkFull prompt desc docs =
TestCase(prompt, QuickInfo(normalizeLineEnds desc, normalizeLineEnds docs))
let mkDesc prompt desc =
TestCase(prompt, Desc(normalizeLineEnds desc))
let mkNone prompt = TestCase(prompt, Empty)
let filePath = "C:\\test.fs"
let private tooltipElementToExpected expected =
function
| ToolTipElement.None -> Empty
| ToolTipElement.Group(xs) ->
let descriptions = xs |> List.map (fun item -> item.MainDescription)
let descriptionTexts =
descriptions
|> List.map (fun taggedTexts -> taggedTexts |> Array.map (fun taggedText -> taggedText.Text))
let descriptionText = descriptionTexts |> Array.concat |> String.concat ""
let remarks = xs |> List.choose (fun item -> item.Remarks)
let remarkTexts =
remarks |> Array.concat |> Array.map (fun taggedText -> taggedText.Text)
let remarkText =
(match remarks with
| [] -> ""
| _ -> "\n" + String.concat "" remarkTexts)
let tps = xs |> List.collect (fun item -> item.TypeMapping)
let tpTexts =
tps |> List.map (fun x -> x |> Array.map (fun y -> y.Text) |> String.concat "")
let tpText =
(match tps with
| [] -> ""
| _ -> "\n" + String.concat "\n" tpTexts)
let collectDocs (element: ToolTipElementData) =
match element.XmlDoc with
| FSharp.Compiler.Symbols.FSharpXmlDoc.FromXmlText xmlDoc -> xmlDoc.UnprocessedLines |> String.concat "\n"
| _ -> ""
let desc =
[ descriptionText; remarkText; tpText ] |> String.concat "" |> normalizeLineEnds
let docs = xs |> List.map collectDocs |> String.concat "" |> normalizeLineEnds
match expected with
| QuickInfo _ -> QuickInfo(desc, docs)
| _ -> Desc desc
| ToolTipElement.CompositionError _ -> Error
let executeQuickInfoTest (programText: string) testCases =
let document =
RoslynTestHelpers.CreateSolution(programText)
|> RoslynTestHelpers.GetSingleDocument
for TestCase(symbol, expected) in testCases do
let caretPosition = programText.IndexOf(symbol) + symbol.Length - 1
let quickInfo =
let task =
FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition)
|> CancellableTask.start CancellationToken.None
task.Result
let actual =
quickInfo
|> Option.map (fun (_, _, _, ToolTipText elements) -> elements |> List.map (tooltipElementToExpected expected))
|> Option.defaultValue [ Empty ]
actual.Head |> Assert.shouldBeEqualWith expected $"Symbol: {symbol}"
[<Fact>]
let ShouldShowQuickInfoAtCorrectPositions () =
let fileContents =
"""
let x = 1
let y = 2
System.Console.WriteLine(x + y)
"""
let testCases =
[
mkFull "let" "let" "Used to associate, or bind, a name to a value or function."
mkDesc "x" "val x: int\nFull name: Test.x"
mkDesc "y" "val y: int\nFull name: Test.y"
mkNone "1"
mkNone "2"
mkDesc
"x +"
"""val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+))
Full name: Microsoft.FSharp.Core.Operators.(+)
'T1 is int
'T2 is int
'T3 is int"""
mkDesc "System" "namespace System"
mkDesc "WriteLine" "System.Console.WriteLine(value: int) : unit"
]
executeQuickInfoTest fileContents testCases
[<Fact>]
let ShouldShowQuickKeywordInfoAtCorrectPositionsForSignatureFiles () =
let fileContents =
"""
namespace TestNs
module internal MyModule =
val MyVal: isDecl:bool -> string
"""
let testCases =
[
mkFull
"namespace"
"namespace"
"Used to associate a name with a group of related types and modules, to logically separate it from other code."
mkFull
"module"
"module"
"Used to associate a name with a group of related types, values, and functions, to logically separate it from other code."
mkFull "internal" "internal" "Used to specify that a member is visible inside an assembly but not outside it."
mkFull "val" "val" "Used in a signature to indicate a value, or in a type to declare a member, in limited situations."
mkFull
"->"
"->"
"In function types, delimits arguments and return values. Yields an expression (in sequence expressions); equivalent to the yield keyword. Used in match expressions"
]
executeQuickInfoTest fileContents testCases
[<Fact>]
let ShouldShowQuickKeywordInfoAtCorrectPositionsWithinComputationExpressions () =
let fileContents =
"""
type MyOptionBuilder() =
member __.Zero() = None
member __.Return(x: 'T) = Some x
member __.Bind(m: 'T option, f) = Option.bind f m
let myOpt = MyOptionBuilder()
let x =
myOpt{
let! x = Some 5
let! y = Some 11
return x + y
}
"""
let testCases =
[
mkFull "let!" "let!" "Used in computation expressions to bind a name to the result of another computation expression."
mkFull "return" "return" "Used to provide a value for the result of the containing computation expression."
]
executeQuickInfoTest fileContents testCases
[<Fact>]
let ShouldShowQuickInfoForGenericParameters () =
let fileContents =
"""
type C() =
member x.FSharpGenericMethodExplicitTypeParams<'T>(a:'T, y:'T) = (a,y)
member x.FSharpGenericMethodInferredTypeParams(a, y) = (a,y)
open System.Linq
let coll = [ for i in 1 .. 100 -> (i, string i) ]
let res1 = coll.GroupBy (fun (a, b) -> a)
let res2 = System.Array.Sort [| 1 |]
let test4 x = C().FSharpGenericMethodExplicitTypeParams([x], [x])
let test5<'U> (x: 'U) = C().FSharpGenericMethodExplicitTypeParams([x], [x])
let test6 = C().FSharpGenericMethodExplicitTypeParams(1, 1)
let test7 x = C().FSharpGenericMethodInferredTypeParams([x], [x])
let test8 = C().FSharpGenericMethodInferredTypeParams(1, 1)
let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams([x], [x])
let res3 = [1] |> List.map id
let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s + string x) // note there is a type error here, still check quickinfo any way
let res5 = 1 + 2
let res6 = System.DateTime.Now + System.TimeSpan.Zero
let res7 = sin 5.0
let res8 = abs 5.0<kg>
"""
let testCases =
[
mkDesc
"GroupBy"
"(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : System.Collections.Generic.IEnumerable<IGrouping<'TKey,'TSource>>
'TSource is int * string
'TKey is int"
mkDesc
"Sort"
"System.Array.Sort<'T>(array: 'T array) : unit
'T is int"
mkDesc
"let test4 x = C().FSharpGenericMethodExplicitTypeParams"
"member C.FSharpGenericMethodExplicitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0
'T is 'a list"
mkDesc
"let test5<'U> (x: 'U) = C().FSharpGenericMethodExplicitTypeParams"
"member C.FSharpGenericMethodExplicitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0
'T is 'U list"
mkDesc
"let test6 = C().FSharpGenericMethodExplicitTypeParams"
"member C.FSharpGenericMethodExplicitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0
'T is int"
mkDesc
"let test7 x = C().FSharpGenericMethodInferredTypeParams"
"member C.FSharpGenericMethodInferredTypeParams: a: 'a1 * y: 'b2 -> 'a1 * 'b2
'a is 'a0 list
'b is 'a0 list"
mkDesc
"let test8 = C().FSharpGenericMethodInferredTypeParams"
"member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1
'a is int
'b is int"
mkDesc
"let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams"
"member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1
'a is 'U list
'b is 'U list"
mkDesc
"let res3 = [1] |>"
"val (|>) : arg: 'T1 -> func: ('T1 -> 'U) -> 'U
Full name: Microsoft.FSharp.Core.Operators.(|>)
'T1 is int list
'U is int list"
mkDesc
"let res3 = [1] |> List.map id"
"val id: x: 'T -> 'T
Full name: Microsoft.FSharp.Core.Operators.id
'T is int"
mkDesc
"let res4 = (1.0,[1]) ||>"
"val (||>) : arg1: 'T1 * arg2: 'T2 -> func: ('T1 -> 'T2 -> 'U) -> 'U
Full name: Microsoft.FSharp.Core.Operators.(||>)
'T1 is float
'T2 is int list
'U is float"
mkDesc
"let res4 = (1.0,[1]) ||> List.fold"
"val fold<'T,'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State
Full name: Microsoft.FSharp.Collections.List.fold
'T is int
'State is float"
mkDesc
"let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s +"
"val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+))
Full name: Microsoft.FSharp.Core.Operators.(+)
'T1 is string
'T2 is string
'T3 is float"
mkDesc
"let res5 = 1 +"
"val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+))
Full name: Microsoft.FSharp.Core.Operators.(+)
'T1 is int
'T2 is int
'T3 is int"
mkDesc
"let res6 = System.DateTime.Now +"
"val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+))
Full name: Microsoft.FSharp.Core.Operators.(+)
'T1 is System.DateTime
'T2 is System.TimeSpan
'T3 is System.DateTime"
mkDesc
"let res7 = sin"
"val sin: value: 'T -> 'T (requires member Sin)
Full name: Microsoft.FSharp.Core.Operators.sin
'T is float"
mkDesc
"let res8 = abs"
"val abs: value: 'T -> 'T (requires member Abs)
Full name: Microsoft.FSharp.Core.Operators.abs
'T is int"
]
executeQuickInfoTest fileContents testCases