mirror of
https://github.com/c-cube/linol.git
synced 2025-12-10 13:14:15 -05:00
223 lines
7.7 KiB
OCaml
223 lines
7.7 KiB
OCaml
open Fiber.O
|
|
open Lsp
|
|
open Lsp.Types
|
|
open Lsp_fiber
|
|
|
|
module Test = struct
|
|
module Client = struct
|
|
let run
|
|
?(capabilities = ClientCapabilities.create ())
|
|
?on_request
|
|
?on_notification
|
|
state
|
|
(in_, out)
|
|
=
|
|
let initialize = InitializeParams.create ~capabilities () in
|
|
let client =
|
|
let stream_io = Lsp_fiber.Fiber_io.make in_ out in
|
|
let handler = Client.Handler.make ?on_request ?on_notification () in
|
|
Client.make handler stream_io state
|
|
in
|
|
client, Client.start client initialize
|
|
;;
|
|
end
|
|
|
|
module Server = struct
|
|
let run ?on_request ?on_notification state (in_, out) =
|
|
let server =
|
|
let stream_io = Fiber_io.make in_ out in
|
|
let handler = Server.Handler.make ?on_request ?on_notification () in
|
|
Server.make handler stream_io state
|
|
in
|
|
server, Server.start server
|
|
;;
|
|
end
|
|
end
|
|
|
|
let pipe () = Lev_fiber.Io.pipe ~cloexec:true ()
|
|
|
|
let test make_client make_server =
|
|
Printexc.record_backtrace false;
|
|
let run () =
|
|
let* client_in, server_out = pipe () in
|
|
let* server_in, client_out = pipe () in
|
|
let server () = make_server (server_in, server_out) in
|
|
let client () = make_client (client_in, client_out) in
|
|
let+ () = Fiber.fork_and_join_unit server client in
|
|
print_endline "Successful termination of test"
|
|
in
|
|
Lev_fiber.run run |> Lev_fiber.Error.ok_exn;
|
|
print_endline "[TEST] finished"
|
|
;;
|
|
|
|
let json_pp = Yojson.Safe.pretty_print ~std:false
|
|
|
|
module End_to_end_client = struct
|
|
let on_request (type a) _ (_ : a Server_request.t) =
|
|
Jsonrpc.Response.Error.raise
|
|
(Jsonrpc.Response.Error.make ~message:"not implemented" ~code:InternalError ())
|
|
;;
|
|
|
|
let on_notification (client : _ Client.t) n =
|
|
let state = Client.state client in
|
|
let received_notification = state in
|
|
let req = Server_notification.to_jsonrpc n in
|
|
Format.eprintf
|
|
"client: received notification@.%a@.%!"
|
|
json_pp
|
|
(Jsonrpc.Notification.yojson_of_t req);
|
|
let+ () = Fiber.Ivar.fill received_notification () in
|
|
Format.eprintf "client: filled received_notification@.%!";
|
|
state
|
|
;;
|
|
|
|
let run io =
|
|
let detached = Fiber.Pool.create () in
|
|
let received_notification = Fiber.Ivar.create () in
|
|
let client, running =
|
|
let on_request = { Client.Handler.on_request } in
|
|
Test.Client.run ~on_request ~on_notification received_notification io
|
|
in
|
|
let init () : unit Fiber.t =
|
|
Format.eprintf "client: waiting for initialization@.%!";
|
|
let* (_ : InitializeResult.t) = Client.initialized client in
|
|
Format.eprintf "client: server initialized. sending request@.%!";
|
|
let cancel = Fiber.Cancel.create () in
|
|
let* () =
|
|
Fiber.Pool.task detached ~f:(fun () ->
|
|
Format.eprintf
|
|
"client: waiting to receive notification before cancelling the request@.%!";
|
|
let* () = Fiber.Ivar.read received_notification in
|
|
Format.eprintf "client: received notification, cancelling the request@.%!";
|
|
Fiber.Cancel.fire cancel)
|
|
in
|
|
let* res_cancel =
|
|
let req_cancel =
|
|
Client_request.ExecuteCommand
|
|
(ExecuteCommandParams.create ~command:"cmd_cancel" ())
|
|
in
|
|
Format.eprintf "client: sending request cmd_cancel@.%!";
|
|
Client.request_with_cancel client cancel req_cancel
|
|
and* res_reply =
|
|
let req_reply =
|
|
Client_request.ExecuteCommand
|
|
(ExecuteCommandParams.create ~command:"cmd_reply" ())
|
|
in
|
|
Format.eprintf "client: sending request cmd_reply@.%!";
|
|
Client.request client req_reply
|
|
in
|
|
(match res_cancel with
|
|
| `Cancelled -> Format.eprintf "client: req_cancel got cancelled@.%!"
|
|
| `Ok _ -> assert false);
|
|
Format.eprintf
|
|
"client: Successfully executed req_reply with result:@.%a@."
|
|
json_pp
|
|
res_reply;
|
|
Format.eprintf "client: sending request to shutdown@.%!";
|
|
let* () = Fiber.Pool.stop detached in
|
|
Client.notification client Exit
|
|
in
|
|
Fiber.fork_and_join_unit init (fun () ->
|
|
Fiber.fork_and_join_unit (fun () -> running) (fun () -> Fiber.Pool.run detached))
|
|
;;
|
|
end
|
|
|
|
module End_to_end_server = struct
|
|
type status =
|
|
| Started
|
|
| Initialized
|
|
|
|
let on_request =
|
|
let on_request (type a) self (req : a Client_request.t) : (a Rpc.Reply.t * _) Fiber.t =
|
|
let state = Server.state self in
|
|
let _status, detached = state in
|
|
match req with
|
|
| Client_request.Initialize _ ->
|
|
let capabilities = ServerCapabilities.create () in
|
|
let result = InitializeResult.create ~capabilities () in
|
|
Format.eprintf "server: initializing server@.";
|
|
Format.eprintf "server: returning initialization result@.%!";
|
|
Fiber.return (Rpc.Reply.now result, (Initialized, detached))
|
|
| Client_request.ExecuteCommand { command; _ } ->
|
|
Format.eprintf "server: received command %s@.%!" command;
|
|
let* () =
|
|
match command with
|
|
| "cmd_cancel" ->
|
|
Fiber.Pool.task detached ~f:(fun () ->
|
|
Format.eprintf "server: sending message notification to client@.%!";
|
|
let msg =
|
|
ShowMessageParams.create
|
|
~type_:MessageType.Info
|
|
~message:"notifying client"
|
|
in
|
|
Server.notification self (Server_notification.ShowMessage msg))
|
|
| _ -> Fiber.return ()
|
|
in
|
|
let* () = Fiber.Pool.stop detached in
|
|
let result = `String "successful execution" in
|
|
let* cancel = Rpc.Server.cancel_token () in
|
|
(match command with
|
|
| "cmd_cancel" ->
|
|
let+ () = Lev_fiber.Timer.sleepf 0.2 in
|
|
( Rpc.Reply.later (fun k ->
|
|
let* cancel = Rpc.Server.cancel_token () in
|
|
(* Make sure that we can access the cancel token in a Reply
|
|
response *)
|
|
assert (Option.is_some cancel);
|
|
k result)
|
|
, state )
|
|
| _ ->
|
|
(* Make sure that we can access the cancel token in a Now response *)
|
|
assert (Option.is_some cancel);
|
|
Fiber.return (Rpc.Reply.now result, state))
|
|
| _ ->
|
|
Jsonrpc.Response.Error.raise
|
|
(Jsonrpc.Response.Error.make ~code:InternalError ~message:"not supported" ())
|
|
in
|
|
{ Server.Handler.on_request }
|
|
;;
|
|
|
|
let on_notification self _ =
|
|
let state = Server.state self in
|
|
Format.eprintf "server: Received notification@.%!";
|
|
Fiber.return state
|
|
;;
|
|
|
|
let run io =
|
|
let detached = Fiber.Pool.create () in
|
|
let _server, running =
|
|
Test.Server.run ~on_request ~on_notification (Started, detached) io
|
|
in
|
|
Fiber.fork_and_join_unit (fun () -> running) (fun () -> Fiber.Pool.run detached)
|
|
;;
|
|
end
|
|
|
|
let%expect_test "end to end run of lsp tests" =
|
|
test End_to_end_client.run End_to_end_server.run;
|
|
[%expect
|
|
{|
|
|
client: waiting for initialization
|
|
server: initializing server
|
|
server: returning initialization result
|
|
client: server initialized. sending request
|
|
client: sending request cmd_cancel
|
|
client: sending request cmd_reply
|
|
client: waiting to receive notification before cancelling the request
|
|
server: received command cmd_cancel
|
|
server: sending message notification to client
|
|
client: received notification
|
|
{
|
|
"params": { "message": "notifying client", "type": 3 },
|
|
"method": "window/showMessage",
|
|
"jsonrpc": "2.0"
|
|
}
|
|
client: filled received_notification
|
|
client: received notification, cancelling the request
|
|
server: received command cmd_reply
|
|
client: req_cancel got cancelled
|
|
client: Successfully executed req_reply with result:
|
|
"successful execution"
|
|
client: sending request to shutdown
|
|
Successful termination of test
|
|
[TEST] finished |}]
|
|
;;
|