-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
split test_httpaf into one file for each module
The primary goal is to split up the client connection and server connection tests, since it's very easy to jump around in test_httpaf.ml as it was without realizing you jumped from one module to the other and now you're looking at a completely different kind of test.
- Loading branch information
Showing
11 changed files
with
1,014 additions
and
1,017 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
open Httpaf | ||
|
||
let maybe_serialize_body f body = | ||
match body with | ||
| None -> () | ||
| Some body -> Faraday.write_string f body | ||
|
||
let request_to_string ?body r = | ||
let f = Faraday.create 0x1000 in | ||
Httpaf_private.Serialize.write_request f r; | ||
maybe_serialize_body f body; | ||
Faraday.serialize_to_string f | ||
|
||
let response_to_string ?body r = | ||
let f = Faraday.create 0x1000 in | ||
Httpaf_private.Serialize.write_response f r; | ||
maybe_serialize_body f body; | ||
Faraday.serialize_to_string f | ||
|
||
module Read_operation = struct | ||
type t = [ `Read | `Yield | `Close ] | ||
|
||
let pp_hum fmt (t : t) = | ||
let str = | ||
match t with | ||
| `Read -> "Read" | ||
| `Yield -> "Yield" | ||
| `Close -> "Close" | ||
in | ||
Format.pp_print_string fmt str | ||
;; | ||
end | ||
|
||
module Write_operation = struct | ||
type t = [ `Write of Bigstringaf.t IOVec.t list | `Yield | `Close of int ] | ||
|
||
let iovecs_to_string iovecs = | ||
let len = IOVec.lengthv iovecs in | ||
let bytes = Bytes.create len in | ||
let dst_off = ref 0 in | ||
List.iter (fun { IOVec.buffer; off = src_off; len } -> | ||
Bigstringaf.unsafe_blit_to_bytes buffer ~src_off bytes ~dst_off:!dst_off ~len; | ||
dst_off := !dst_off + len) | ||
iovecs; | ||
Bytes.unsafe_to_string bytes | ||
;; | ||
|
||
let pp_hum fmt (t : t) = | ||
match t with | ||
| `Write iovecs -> Format.fprintf fmt "Write %S" (iovecs_to_string iovecs) | ||
| `Yield -> Format.pp_print_string fmt "Yield" | ||
| `Close len -> Format.fprintf fmt "Close %i" len | ||
;; | ||
|
||
let to_write_as_string t = | ||
match t with | ||
| `Write iovecs -> Some (iovecs_to_string iovecs) | ||
| `Close _ | `Yield -> None | ||
;; | ||
end | ||
|
||
let write_operation = Alcotest.of_pp Write_operation.pp_hum | ||
let read_operation = Alcotest.of_pp Read_operation.pp_hum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
open Httpaf | ||
open Helpers | ||
open Client_connection | ||
|
||
module Response = struct | ||
include Response | ||
|
||
let pp = pp_hum | ||
let equal x y = x = y | ||
end | ||
|
||
let feed_string t str = | ||
let len = String.length str in | ||
let input = Bigstringaf.of_string str ~off:0 ~len in | ||
read t input ~off:0 ~len | ||
|
||
let read_string t str = | ||
let c = feed_string t str in | ||
Alcotest.(check int) "read consumes all input" (String.length str) c; | ||
;; | ||
|
||
let read_response t r = | ||
let request_string = response_to_string r in | ||
read_string t request_string | ||
;; | ||
|
||
let reader_ready t = | ||
Alcotest.check read_operation "Reader is ready" | ||
`Read (next_read_operation t :> [`Close | `Read | `Yield]); | ||
;; | ||
|
||
let write_string ?(msg="output written") t str = | ||
let len = String.length str in | ||
Alcotest.(check (option string)) msg | ||
(Some str) | ||
(next_write_operation t |> Write_operation.to_write_as_string); | ||
report_write_result t (`Ok len); | ||
;; | ||
|
||
let write_request ?(msg="request written") t r = | ||
let request_string = request_to_string r in | ||
write_string ~msg t request_string | ||
;; | ||
|
||
let writer_yielded t = | ||
Alcotest.check write_operation "Writer is in a yield state" | ||
`Yield (next_write_operation t); | ||
;; | ||
|
||
let writer_closed t = | ||
Alcotest.check write_operation "Writer is closed" | ||
(`Close 0) (next_write_operation t); | ||
;; | ||
|
||
let connection_is_shutdown t = | ||
Alcotest.check read_operation "Reader is closed" | ||
`Close (next_read_operation t :> [`Close | `Read | `Yield]); | ||
writer_closed t; | ||
;; | ||
|
||
let default_response_handler expected_response response body = | ||
Alcotest.check (module Response) "expected response" expected_response response; | ||
let on_read _ ~off:_ ~len:_ = () in | ||
let on_eof () = () in | ||
Body.schedule_read body ~on_read ~on_eof; | ||
;; | ||
|
||
let no_error_handler _ = assert false | ||
|
||
let test_get () = | ||
let request' = Request.create `GET "/" in | ||
let response = Response.create `OK in | ||
|
||
(* Single GET *) | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:no_error_handler | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
writer_closed t; | ||
read_response t response; | ||
|
||
(* Single GET, reponse closes connection *) | ||
let response = | ||
Response.create `OK ~headers:(Headers.of_list [ "connection", "close" ]) | ||
in | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:no_error_handler | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
read_response t response; | ||
let c = read_eof t Bigstringaf.empty ~off:0 ~len:0 in | ||
Alcotest.(check int) "read_eof with no input returns 0" 0 c; | ||
connection_is_shutdown t; | ||
|
||
(* Single GET, streaming body *) | ||
let response = | ||
Response.create `OK ~headers:(Headers.of_list [ "transfer-encoding", "chunked" ]) | ||
in | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:no_error_handler | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
read_response t response; | ||
read_string t "d\r\nHello, world!\r\n0\r\n\r\n"; | ||
;; | ||
|
||
let test_response_eof () = | ||
let request' = Request.create `GET "/" in | ||
let response = Response.create `OK in (* not actually writen to the channel *) | ||
|
||
let error_message = ref None in | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:(function | ||
| `Malformed_response msg -> error_message := Some msg | ||
| _ -> assert false) | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
writer_closed t; | ||
reader_ready t; | ||
let c = read_eof t Bigstringaf.empty ~off:0 ~len:0 in | ||
Alcotest.(check int) "read_eof with no input returns 0" 0 c; | ||
connection_is_shutdown t; | ||
Alcotest.(check (option string)) "unexpected eof" | ||
(Some "unexpected eof") | ||
!error_message | ||
;; | ||
|
||
let test_report_exn () = | ||
let request' = Request.create `GET "/" in | ||
let response = Response.create `OK in (* not actually writen to the channel *) | ||
|
||
let error_message = ref None in | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:(function | ||
| `Exn (Failure msg) -> error_message := Some msg | ||
| _ -> assert false) | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
writer_closed t; | ||
reader_ready t; | ||
report_exn t (Failure "something went wrong"); | ||
connection_is_shutdown t; | ||
Alcotest.(check (option string)) "something went wrong" | ||
(Some "something went wrong") | ||
!error_message | ||
;; | ||
|
||
let test_input_shrunk () = | ||
let request' = Request.create `GET "/" in | ||
let response = Response.create `OK in (* not actually writen to the channel *) | ||
|
||
let error_message = ref None in | ||
let body, t = | ||
request | ||
request' | ||
~response_handler:(default_response_handler response) | ||
~error_handler:(function | ||
| `Exn (Failure msg) -> error_message := Some msg | ||
| _ -> assert false) | ||
in | ||
Body.close_writer body; | ||
write_request t request'; | ||
writer_closed t; | ||
reader_ready t; | ||
let c = feed_string t "HTTP/1.1 200 OK\r\nDate" in | ||
Alcotest.(check int) "read the status line" c 17; | ||
report_exn t (Failure "something went wrong"); | ||
connection_is_shutdown t; | ||
Alcotest.(check (option string)) "something went wrong" | ||
(Some "something went wrong") | ||
!error_message | ||
;; | ||
|
||
|
||
let tests = | ||
[ "GET" , `Quick, test_get | ||
; "Response EOF", `Quick, test_response_eof | ||
; "report_exn" , `Quick, test_report_exn | ||
; "input_shrunk", `Quick, test_input_shrunk | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
open Httpaf | ||
|
||
let check msg ~expect actual = | ||
Alcotest.(check (list (pair string string))) msg expect (Headers.to_list actual) | ||
;; | ||
|
||
let test_replace () = | ||
check "replace trailing element" | ||
~expect:["c", "d"; "a", "d"] | ||
(Headers.replace | ||
(Headers.of_list ["c", "d"; "a", "b"]) | ||
"a" | ||
"d"); | ||
|
||
check "replace middle element" | ||
~expect:["e", "f"; "c", "z"; "a", "b"] | ||
(Headers.replace | ||
(Headers.of_list ["e", "f"; "c", "d"; "a", "b"]) | ||
"c" | ||
"z"); | ||
|
||
check "remove multiple trailing elements" | ||
~expect:["c", "d"; "a", "d"] | ||
(Headers.replace | ||
(Headers.of_list [ "c", "d"; "a", "b"; "a", "c"]) | ||
"a" | ||
"d"); | ||
;; | ||
|
||
let test_remove () = | ||
check "remove leading element" | ||
~expect:["c", "d"] | ||
(Headers.remove | ||
(Headers.of_list ["a", "b"; "c", "d"]) | ||
"a"); | ||
check "remove trailing element" | ||
~expect:["c", "d"] | ||
(Headers.remove | ||
(Headers.of_list ["c", "d"; "a", "b"]) | ||
"a"); | ||
;; | ||
|
||
let tests = | ||
[ "remove" , `Quick, test_remove | ||
; "replace", `Quick, test_replace | ||
] |
Oops, something went wrong.