@@ -27,14 +27,39 @@ let read_str filename =
2727
2828open Idl
2929
30- module type PATHS = sig
30+ module type CONFIG = sig
3131 val test_data_path : string
3232 (* * Path under which we look for or generate requests and responses. For example,
3333 if test_data_path = 'foo', this module will search for or generate requests
3434 matching 'foo/requests/<RPC name>.request.<n>' and responses matching
3535 'foo/responses/<RPC name>.response.<n>' *)
3636end
3737
38+ module type MARSHALLER = sig
39+ val string_of_call : Rpc .call -> string
40+ val call_of_string : string -> Rpc .call
41+ val string_of_response : Rpc .response -> string
42+ val response_of_string : string -> Rpc .response
43+ val to_string : Rpc .t -> string
44+ val of_string : string -> Rpc .t
45+ end
46+
47+ (* Slightly annoyingly, both RPC modules have a slightly different signature. Fix it here *)
48+ module TJsonrpc : MARSHALLER = struct
49+ include Jsonrpc
50+ let string_of_call call = string_of_call call
51+ let string_of_response response = string_of_response response
52+ end
53+
54+ module TXmlrpc : MARSHALLER = struct
55+ include Xmlrpc
56+ let call_of_string s = call_of_string s
57+ let response_of_string s = response_of_string s
58+ let of_string s = of_string s
59+ end
60+
61+
62+
3863(* * The following module implements test cases that write test
3964 RPC requests and responses in JSON that can be used to
4065 verify that subsequent versions of an API can still parse
4570 this module.
4671
4772 The test data will be written to the path specified in the
48- PATH module passed in *)
49- module GenTestData (P : PATHS ) = struct
73+ CONFIG module passed in *)
74+ module GenTestData (C : CONFIG ) ( M : MARSHALLER ) = struct
5075 type implementation = unit Alcotest .test_case list ref
5176
5277 let tests : unit Alcotest.test_case list ref = ref []
@@ -63,14 +88,36 @@ module GenTestData (P:PATHS) = struct
6388 let returning a err = Returning (a, err)
6489 let (@-> ) = fun t f -> Function (t, f)
6590
91+ open M
92+
6693 let declare name _ ty =
6794 let rec inner : type b. (((string * Rpc.t) list * Rpc.t list) list) -> b fn -> unit = fun params ->
6895 function
6996 | Function (t , f ) -> begin
7097 let vs = Rpc_genfake. genall 2 (match t.Param. name with Some n -> n | None -> t.Param. typedef.Rpc.Types. name) t.Param. typedef.Rpc.Types. ty in
7198 let marshalled = List. map (fun v -> Rpcmarshal. marshal t.Param. typedef.Rpc.Types. ty v) vs in
7299 match t.Param. name with
73- | Some n -> inner (List. flatten (List. map (fun marshalled -> List. map (fun (named ,unnamed ) -> (((n,marshalled)::named),unnamed)) params) marshalled)) f
100+ | Some n ->
101+ inner
102+ (List. flatten
103+ (List. map
104+ (fun marshalled ->
105+ match marshalled, t.Param. typedef.Rpc.Types. ty with
106+ | Rpc. Enum [] , Rpc.Types. Option _ ->
107+ params
108+ | Rpc. Enum [x], Rpc.Types. Option _ ->
109+ List. map
110+ (fun (named ,unnamed ) ->
111+ (((n, x)::named),unnamed))
112+ params
113+ | _ , _ ->
114+ List. map
115+ (fun (named ,unnamed ) ->
116+ (((n,marshalled)::named),unnamed))
117+ params
118+ ) marshalled
119+ )
120+ ) f
74121 | None -> inner (List. flatten (List. map (fun marshalled -> List. map (fun (named ,unnamed ) -> (named,(marshalled::unnamed))) params) marshalled)) f
75122 end
76123 | Returning (t , e ) ->
@@ -85,40 +132,44 @@ module GenTestData (P:PATHS) = struct
85132 let call = Rpc. call wire_name args in
86133 call) params in
87134 List. iteri (fun i call ->
88- let request_str = Jsonrpc. string_of_call call in
135+ let request_str = string_of_call call in
89136 write_str
90- (Printf. sprintf " %s/requests/%s.request.%d" P . test_data_path wire_name i)
137+ (Printf. sprintf " %s/requests/%s.request.%d" C . test_data_path wire_name i)
91138 request_str) calls;
92139 let vs = Rpc_genfake. genall 2 (match t.Param. name with Some n -> n | None -> t.Param. typedef.Rpc.Types. name) t.Param. typedef.Rpc.Types. ty in
93140 let marshalled_vs = List. map (fun v -> Rpc. success (Rpcmarshal. marshal t.Param. typedef.Rpc.Types. ty v)) vs in
94141 let errs = Rpc_genfake. genall 2 " error" e.Error. def.Rpc.Types. ty in
95142 let marshalled_errs = List. map (fun err -> Rpc. failure (Rpcmarshal. marshal e.Error. def.Rpc.Types. ty err)) errs in
96143 List. iteri (fun i response ->
97- let response_str = Jsonrpc. string_of_response response in
144+ let response_str = string_of_response response in
98145 write_str
99- (Printf. sprintf " %s/responses/%s.response.%d" P . test_data_path wire_name i)
146+ (Printf. sprintf " %s/responses/%s.response.%d" C . test_data_path wire_name i)
100147 response_str) (marshalled_vs @ marshalled_errs)
101148 in
102149 let test_fn () =
103150 let mkdir_safe p = begin try Unix. mkdir p 0o755 with Unix. Unix_error (EEXIST, _ , _ ) -> () end in
104- mkdir_safe P . test_data_path;
105- mkdir_safe (Printf. sprintf " %s/requests" P . test_data_path);
106- mkdir_safe (Printf. sprintf " %s/responses" P . test_data_path);
151+ mkdir_safe C . test_data_path;
152+ mkdir_safe (Printf. sprintf " %s/requests" C . test_data_path);
153+ mkdir_safe (Printf. sprintf " %s/responses" C . test_data_path);
107154 inner [[],[]] ty in
108155 tests := (Printf. sprintf " Generate test data for '%s'" (Idl. get_wire_name ! description name), `Quick , test_fn) :: ! tests
109156end
110157
111- let get_arg call has_named name =
158+ let get_arg call has_named name is_opt =
112159 match has_named, name, call.Rpc. params with
113160 | true , Some n , (Rpc. Dict named )::unnamed -> begin
114- match List. partition (fun (x ,_ ) -> x = n) named with
115- | (_ ,arg )::dups ,others -> Result. Ok (arg, {call with Rpc. params = (Rpc. Dict (dups @ others))::unnamed })
116- | _ ,_ -> Result. Error (`Msg (Printf. sprintf " Expecting named argument '%s'" n))
161+ match List. partition (fun (x ,_ ) -> x = n) named with
162+ | (_ ,arg )::dups ,others when is_opt ->
163+ Result. Ok (Rpc. Enum [arg], {call with Rpc. params = (Rpc. Dict (dups @ others))::unnamed })
164+ | [] , _others when is_opt -> Result. Ok (Rpc. Enum [] , call)
165+ | (_ ,arg )::dups ,others ->
166+ Result. Ok (arg, {call with Rpc. params = (Rpc. Dict (dups @ others))::unnamed })
167+ | _ ,_ -> Result. Error (`Msg (Printf. sprintf " Expecting named argument '%s'" n))
117168 end
118169 | true , None , (Rpc. Dict named )::unnamed -> begin
119- match unnamed with
120- | head ::tail -> Result. Ok (head, {call with Rpc. params = (Rpc. Dict named)::tail})
121- | _ -> Result. Error (`Msg " Incorrect number of arguments" )
170+ match unnamed with
171+ | head ::tail -> Result. Ok (head, {call with Rpc. params = (Rpc. Dict named)::tail})
172+ | _ -> Result. Error (`Msg " Incorrect number of arguments" )
122173 end
123174 | true , _ , _ -> begin
124175 Result. Error (`Msg " Marshalling error: Expecting dict as first argument when named parameters exist" )
@@ -127,9 +178,9 @@ let get_arg call has_named name =
127178 Result. Ok (head, {call with Rpc. params = tail})
128179 end
129180 | false , None , [] ->
130- Result. Error (`Msg " Incorrect number of arguments" )
181+ Result. Error (`Msg " Incorrect number of arguments" )
131182 | false , Some _ , _ ->
132- failwith " Can't happen by construction"
183+ failwith " Can't happen by construction"
133184
134185exception NoDescription
135186exception MarshalError of string
@@ -138,7 +189,7 @@ exception MarshalError of string
138189(* * The following module will generate alcotest test cases to verify
139190 that a set of requests and responses can be successfully parsed.
140191
141- The PATHS module specifies the location for the test data as
192+ The CONFIG module specifies the location for the test data as
142193 `test_data_path`. Requests and responses will be looked up in
143194 this location in the subdirectories `requests` and `responses`.
144195 The actual data must be in files following the naming convention
@@ -148,7 +199,7 @@ exception MarshalError of string
148199 ensure it accurately represents how the server would parse the
149200 json.
150201 *)
151- module TestOldRpcs (P : PATHS ) = struct
202+ module TestOldRpcs (C : CONFIG ) ( M : MARSHALLER ) = struct
152203 open Rpc
153204 type implementation = unit Alcotest .test_case list ref
154205
@@ -167,6 +218,8 @@ module TestOldRpcs (P : PATHS) = struct
167218 let returning a b = Returning (a,b)
168219 let (@-> ) = fun t f -> Function (t, f)
169220
221+ open M
222+
170223 let rec has_named_args : type a. a fn -> bool =
171224 function
172225 | Function (t , f ) -> begin
@@ -191,22 +244,31 @@ module TestOldRpcs (P : PATHS) = struct
191244 let rec read_all path extension i =
192245 try
193246 let call =
194- read_str (Printf. sprintf " %s/%s/%s.%s.%d" P . test_data_path path wire_name extension i) in
247+ read_str (Printf. sprintf " %s/%s/%s.%s.%d" C . test_data_path path wire_name extension i) in
195248 call :: read_all path extension (i+ 1 )
196249 with _ -> []
197250 in
198251
199- let calls = read_all " requests" " request" 0 |> List. map Jsonrpc. call_of_string in
200- let responses = read_all " responses" " response" 0 |> List. map Jsonrpc. response_of_string in
252+ let calls = read_all " requests" " request" 0 |> List. map call_of_string in
253+ let responses = read_all " responses" " response" 0 |> List. map response_of_string in
201254
202255 let verify : type a. a Rpc.Types.typ -> Rpc.t -> a = fun typ rpc ->
256+ let rec sort_dicts ty =
257+ let open Rpc in
258+ match ty with
259+ | Dict kvs' ->
260+ let kvs = List. map (fun (k ,v ) -> (k, sort_dicts v)) kvs' in
261+ Dict (List. sort (fun (k1 ,_ ) (k2 ,_ ) -> String. compare k1 k2) kvs)
262+ | Enum ts -> Enum (List. map sort_dicts ts)
263+ | _ -> ty
264+ in
203265 match Rpcmarshal. unmarshal typ rpc with
204266 | Ok x ->
205267 let check = Rpcmarshal. marshal typ x in
206- if check <> rpc then begin
268+ if (to_string (sort_dicts check)) <> (to_string (sort_dicts rpc)) then begin
207269 let err = Printf. sprintf " Round-trip failed. Before: '%s' After: '%s'"
208- (Jsonrpc. to_string rpc)
209- (Jsonrpc. to_string check) in
270+ (to_string rpc)
271+ (to_string check) in
210272 raise (MarshalError err)
211273 end ;
212274 x
@@ -220,7 +282,8 @@ module TestOldRpcs (P : PATHS) = struct
220282 match f with
221283 | Function (t , f ) -> begin
222284 let (arg_rpc, call') =
223- match get_arg call has_named t.Param. name with
285+ let is_opt = match t.Param. typedef.Rpc.Types. ty with Rpc.Types. Option _ -> true | _ -> false in
286+ match get_arg call has_named t.Param. name is_opt with
224287 | Result. Ok (x ,y ) -> (x,y)
225288 | Result. Error (`Msg m ) -> raise (MarshalError m)
226289 in
0 commit comments