Skip to content

Commit c0cea35

Browse files
committed
fix tests around serialization of LSP types
1 parent b5cd972 commit c0cea35

File tree

6 files changed

+58
-74
lines changed

6 files changed

+58
-74
lines changed

src/LanguageServerProtocol.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module Server =
1212
open Newtonsoft.Json
1313
open Ionide.LanguageServerProtocol.JsonUtils
1414

15-
let private jsonRpcFormatter () =
15+
let jsonRpcFormatter () =
1616
let f = new JsonMessageFormatter()
1717
f.JsonSerializer.NullValueHandling <- NullValueHandling.Ignore
1818
f.JsonSerializer.ConstructorHandling <- ConstructorHandling.AllowNonPublicDefaultConstructor

src/Types.fs

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ type U2<'a, 'b> =
1818
/// The LSP any type
1919
type LSPAny = JToken
2020

21+
module LspError =
22+
open StreamJsonRpc.Protocol
23+
open StreamJsonRpc
24+
25+
let invalidParams message = LocalRpcException(message, ErrorCode = int JsonRpcErrorCode.InvalidParams)
26+
let internalError message = LocalRpcException(message, ErrorCode = int JsonRpcErrorCode.InternalError)
27+
let notImplemented () = NotImplementedException()
28+
2129
type TextDocumentSyncKind =
2230
| None = 0
2331
| Full = 1

tests/Benchmarks.fs

+16-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ open System
1111
open System.Collections.Concurrent
1212
open System.Collections.Generic
1313
open BenchmarkDotNet.Order
14+
open System.Text
15+
open Newtonsoft.Json
16+
open System.IO
1417

1518
let inline private memorise (f: 'a -> 'b) : 'a -> 'b =
1619
let d = ConcurrentDictionary<'a, 'b>()
@@ -514,15 +517,23 @@ type MultipleTypesBenchmarks() =
514517
/// Some complex data which covers all converters
515518
let example = Example.createData (1234, 9, 5)
516519
let option = {| Some = Some 123; None = (None: int option) |}
520+
let formatter = jsonRpcFormatter ()
521+
522+
let serialize item =
523+
let sb = new StringBuilder()
524+
use writer = new JsonTextWriter(new StringWriter(sb))
525+
formatter.JsonSerializer.Serialize(writer, item)
526+
JToken.Parse(sb.ToString())
517527

518528
let withCounts (counts) data =
519529
data
520530
|> Array.collect (fun data -> counts |> Array.map (fun count -> [| box count; box data |]))
521531

522532
member _.AllLsp_Roundtrip() =
533+
523534
for o in allLsp do
524535
let json = inlayHint |> serialize
525-
let res = json.ToObject(o.GetType(), jsonRpcFormatter.JsonSerializer)
536+
let res = json.ToObject(o.GetType(), formatter.JsonSerializer)
526537
()
527538

528539
[<BenchmarkCategory("LSP"); Benchmark>]
@@ -534,7 +545,7 @@ type MultipleTypesBenchmarks() =
534545

535546
member _.Example_Roundtrip() =
536547
let json = example |> serialize
537-
let res = json.ToObject(example.GetType(), jsonRpcFormatter.JsonSerializer)
548+
let res = json.ToObject(example.GetType(), formatter.JsonSerializer)
538549
()
539550

540551
[<BenchmarkCategory("Example"); Benchmark>]
@@ -550,7 +561,7 @@ type MultipleTypesBenchmarks() =
550561
member _.Option_Roundtrips(count: int) =
551562
for _ in 1..count do
552563
let json = option |> serialize
553-
let _ = json.ToObject(option.GetType(), jsonRpcFormatter.JsonSerializer)
564+
let _ = json.ToObject(option.GetType(), formatter.JsonSerializer)
554565
()
555566

556567
member _.SingleCaseUnion_ArgumentsSource() =
@@ -562,7 +573,7 @@ type MultipleTypesBenchmarks() =
562573
member _.SingleCaseUnion_Roundtrips(count: int, data: Example.SingleCaseUnion) =
563574
for _ in 1..count do
564575
let json = data |> serialize
565-
let _ = json.ToObject(typeof<Example.SingleCaseUnion>, jsonRpcFormatter.JsonSerializer)
576+
let _ = json.ToObject(typeof<Example.SingleCaseUnion>, formatter.JsonSerializer)
566577
()
567578

568579
member _.ErasedUnion_ArgumentsSource() =
@@ -582,7 +593,7 @@ type MultipleTypesBenchmarks() =
582593
member _.ErasedUnion_Roundtrips(count: int, data: Example.ErasedUnionData) =
583594
for _ in 1..count do
584595
let json = data |> serialize
585-
let _ = json.ToObject(typeof<Example.ErasedUnionData>, jsonRpcFormatter.JsonSerializer)
596+
let _ = json.ToObject(typeof<Example.ErasedUnionData>, formatter.JsonSerializer)
586597
()
587598

588599

tests/Shotgun.fs

+19-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ open Newtonsoft.Json.Linq
1414
open Expecto
1515
open Ionide.LanguageServerProtocol.Server
1616
open Ionide.LanguageServerProtocol.Types
17+
open System.Text
18+
open Newtonsoft.Json
19+
open System.IO
1720

1821
// must be public
1922
type Gens =
@@ -85,8 +88,22 @@ type Gens =
8588

8689
let private fsCheckConfig = { FsCheckConfig.defaultConfig with arbitrary = [ typeof<Gens> ] }
8790

88-
type private Roundtripper =
89-
static member ThereAndBackAgain(input: 'a) = input |> serialize |> deserialize<'a>
91+
type private Roundtripper() =
92+
93+
static let formatter = jsonRpcFormatter ()
94+
95+
static member serialize item =
96+
let sb = new StringBuilder()
97+
use writer = new JsonTextWriter(new StringWriter(sb))
98+
formatter.JsonSerializer.Serialize(writer, item)
99+
JToken.Parse(sb.ToString())
100+
101+
static member deserialize<'T>(json: JToken) = json.ToObject<'T>(formatter.JsonSerializer)
102+
103+
static member ThereAndBackAgain(input: 'a) =
104+
input
105+
|> Roundtripper.serialize
106+
|> Roundtripper.deserialize<'a>
90107

91108
static member TestThereAndBackAgain(input: 'a) =
92109
let output = Roundtripper.ThereAndBackAgain input

tests/Tests.fs

+13-65
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ open Newtonsoft.Json
1010
open Ionide.LanguageServerProtocol.JsonRpc
1111
open System.Collections.Generic
1212
open System.Runtime.Serialization
13+
open System.Text
14+
open System.IO
1315

1416
type Record1 = { Name: string; Value: int }
1517
type Record2 = { Name: string; Position: int }
@@ -88,6 +90,16 @@ type ExtensionDataField =
8890
if isNull x.AdditionalData then
8991
x.AdditionalData <- Map.empty
9092

93+
let private formatter = jsonRpcFormatter ()
94+
95+
let serialize item =
96+
let sb = new StringBuilder()
97+
use writer = new JsonTextWriter(new StringWriter(sb))
98+
formatter.JsonSerializer.Serialize(writer, item)
99+
JToken.Parse(sb.ToString())
100+
101+
let deserialize<'T> (json: JToken) = json.ToObject<'T>(formatter.JsonSerializer)
102+
91103
let private serializationTests =
92104
testList
93105
"(de)serialization"
@@ -674,71 +686,7 @@ let private serializationTests =
674686
testCase "can deserialize null Version in VersionedTextDocumentIdentifier"
675687
<| fun _ ->
676688
let textDoc = { VersionedTextDocumentIdentifier.Uri = "..."; Version = None }
677-
testThereAndBackAgain textDoc
678-
679-
testCase "serialize to name specified in JsonProperty in Response"
680-
<| fun _ ->
681-
let response: Response = { Version = "123"; Id = None; Error = None; Result = None }
682-
let json = response |> serialize
683-
// Version -> jsonrpc
684-
Expect.isNone
685-
(json |> tryGetProperty (nameof response.Version))
686-
"Version should exist, but instead as jsonrpc"
687-
688-
Expect.isSome (json |> tryGetProperty "jsonrpc") "jsonrcp should exist because of Version"
689-
// Id & Error optional -> not in json
690-
Expect.isNone (json |> tryGetProperty (nameof response.Id)) "None Id shouldn't be in json"
691-
Expect.isNone (json |> tryGetProperty (nameof response.Error)) "None Error shouldn't be in json"
692-
// Result even when null/None
693-
let prop =
694-
json
695-
|> tryGetProperty (nameof response.Result)
696-
|> Flip.Expect.wantSome "Result should exist even when null/None"
697-
698-
Expect.equal prop.Value.Type (JTokenType.Null) "Result should be null"
699-
testCase "can (de)serialize empty response"
700-
<| fun _ ->
701-
let response: Response = { Version = "123"; Id = None; Error = None; Result = None }
702-
testThereAndBackAgain response
703-
testCase "can (de)serialize Response.Result"
704-
<| fun _ ->
705-
let response: Response =
706-
{ Version = "123"
707-
Id = None
708-
Error = None
709-
Result = Some(JToken.Parse "\"some result\"") }
710-
711-
testThereAndBackAgain response
712-
testCase "can (de)serialize Result when Error is None"
713-
<| fun _ ->
714-
// Note: It's either `Error` or `Result`, but not both together
715-
let response: Response =
716-
{ Version = "123"
717-
Id = Some 42
718-
Error = None
719-
Result = Some(JToken.Parse "\"some result\"") }
720-
721-
testThereAndBackAgain response
722-
testCase "can (de)serialize Error when error is Some"
723-
<| fun _ ->
724-
let response: Response =
725-
{ Version = "123"
726-
Id = Some 42
727-
Error = Some { Code = 13; Message = "oh no"; Data = Some(JToken.Parse "\"some data\"") }
728-
Result = None }
729-
730-
testThereAndBackAgain response
731-
testCase "doesn't serialize Result when Error is Some"
732-
<| fun _ ->
733-
let response: Response =
734-
{ Version = "123"
735-
Id = Some 42
736-
Error = Some { Code = 13; Message = "oh no"; Data = Some(JToken.Parse "\"some data\"") }
737-
Result = Some(JToken.Parse "\"some result\"") }
738-
739-
let output = thereAndBackAgain response
740-
Expect.isSome output.Error "Error should be serialized"
741-
Expect.isNone output.Result "Result should not be serialized when Error is Some" ]
689+
testThereAndBackAgain textDoc ]
742690

743691
testList
744692
(nameof InlayHint)

tests/Utils.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ let tests =
246246
testCase "Client isn't lsp"
247247
<| fun _ ->
248248
let isLsp =
249-
typeof<Ionide.LanguageServerProtocol.Client.Client>
249+
typeof<Ionide.LanguageServerProtocol.ILspClient>
250250
|> isLspType []
251251

252252
Expect.isFalse isLsp "Client isn't lsp" ]

0 commit comments

Comments
 (0)