at eio-update 8.8 kB view raw
1(** Alcotest suite comparing Unix and Eio implementations *) 2 3open Alcotest 4 5let ( / ) = Eio.Path. ( / ) 6 7let test_data_dir = "test/data" 8 9let test_files = [ 10 "simple_waypoints.gpx"; 11 "detailed_waypoints.gpx"; 12 "simple_route.gpx"; 13 "simple_track.gpx"; 14 "multi_segment_track.gpx"; 15 "comprehensive.gpx"; 16 "minimal.gpx"; 17 "edge_cases.gpx"; 18] 19 20(** Helper to compare GPX documents *) 21let compare_gpx_basic gpx1 gpx2 = 22 let open Gpx in 23 Doc.creator gpx1 = Doc.creator gpx2 && 24 List.length (Doc.waypoints gpx1) = List.length (Doc.waypoints gpx2) && 25 List.length (Doc.routes gpx1) = List.length (Doc.routes gpx2) && 26 List.length (Doc.tracks gpx1) = List.length (Doc.tracks gpx2) 27 28(** Test Unix implementation can read all test files *) 29let test_unix_parsing filename () = 30 let path = Filename.concat test_data_dir filename in 31 match Gpx_unix.read path with 32 | Ok gpx -> 33 let validation = Gpx.validate_gpx gpx in 34 check bool "GPX is valid" true validation.is_valid; 35 check bool "Has some content" true ( 36 List.length (Gpx.Doc.waypoints gpx) > 0 || 37 List.length (Gpx.Doc.routes gpx) > 0 || 38 List.length (Gpx.Doc.tracks gpx) > 0 39 ) 40 | Error err -> 41 failf "Unix parsing failed for %s: %s" filename (Gpx.Error.to_string err) 42 43(** Test Eio implementation can read all test files *) 44let test_eio_parsing filename () = 45 Eio_main.run @@ fun env -> 46 let fs = Eio.Stdenv.fs env in 47 let path = Filename.concat test_data_dir filename in 48 try 49 let gpx = Gpx_eio.read (fs / path) in 50 let validation = Gpx.validate_gpx gpx in 51 check bool "GPX is valid" true validation.is_valid; 52 check bool "Has some content" true ( 53 List.length (Gpx.Doc.waypoints gpx) > 0 || 54 List.length (Gpx.Doc.routes gpx) > 0 || 55 List.length (Gpx.Doc.tracks gpx) > 0 56 ) 57 with 58 | Gpx.Gpx_error err -> 59 failf "Eio parsing failed for %s: %s" filename (Gpx.Error.to_string err) 60 61(** Test Unix and Eio implementations produce equivalent results *) 62let test_unix_eio_equivalence filename () = 63 let path = Filename.concat test_data_dir filename in 64 65 (* Parse with Unix *) 66 let unix_result = Gpx_unix.read path in 67 68 (* Parse with Eio *) 69 let eio_result = 70 try 71 Eio_main.run @@ fun env -> 72 let fs = Eio.Stdenv.fs env in 73 Ok (Gpx_eio.read (fs / path)) 74 with 75 | Gpx.Gpx_error err -> Error err 76 in 77 78 match unix_result, eio_result with 79 | Ok gpx_unix, Ok gpx_eio -> 80 check bool "Unix and Eio produce equivalent results" true 81 (compare_gpx_basic gpx_unix gpx_eio); 82 check string "Creators match" (Gpx.Doc.creator gpx_unix) (Gpx.Doc.creator gpx_eio); 83 check int "Waypoint counts match" 84 (List.length (Gpx.Doc.waypoints gpx_unix)) (List.length (Gpx.Doc.waypoints gpx_eio)); 85 check int "Route counts match" 86 (List.length (Gpx.Doc.routes gpx_unix)) (List.length (Gpx.Doc.routes gpx_eio)); 87 check int "Track counts match" 88 (List.length (Gpx.Doc.tracks gpx_unix)) (List.length (Gpx.Doc.tracks gpx_eio)) 89 | Error _, Error _ -> 90 (* Both failed - that's consistent *) 91 check bool "Both Unix and Eio failed consistently" true true 92 | Ok _, Error _ -> 93 failf "Unix succeeded but Eio failed for %s" filename 94 | Error _, Ok _ -> 95 failf "Eio succeeded but Unix failed for %s" filename 96 97(** Test write-read round-trip with Unix *) 98let test_unix_round_trip filename () = 99 let path = Filename.concat test_data_dir filename in 100 match Gpx_unix.read path with 101 | Ok gpx_original -> 102 (* Write to temporary string *) 103 (match Gpx.write_string gpx_original with 104 | Ok xml_string -> 105 (* Parse the written string *) 106 (match Gpx.parse_string xml_string with 107 | Ok gpx_roundtrip -> 108 check bool "Round-trip preserves basic structure" true 109 (compare_gpx_basic gpx_original gpx_roundtrip); 110 check string "Creator preserved" 111 (Gpx.Doc.creator gpx_original) (Gpx.Doc.creator gpx_roundtrip) 112 | Error _ -> 113 failf "Round-trip parse failed for %s" filename) 114 | Error _ -> 115 failf "Round-trip write failed for %s" filename) 116 | Error _ -> 117 failf "Initial read failed for %s" filename 118 119(** Test write-read round-trip with Eio *) 120let test_eio_round_trip filename () = 121 Eio_main.run @@ fun env -> 122 let fs = Eio.Stdenv.fs env in 123 let path = Filename.concat test_data_dir filename in 124 try 125 let gpx_original = Gpx_eio.read (fs / path) in 126 (* Write to temporary string via GPX core *) 127 match Gpx.write_string gpx_original with 128 | Ok xml_string -> 129 (* Parse the written string *) 130 (match Gpx.parse_string xml_string with 131 | Ok gpx_roundtrip -> 132 check bool "Round-trip preserves basic structure" true 133 (compare_gpx_basic gpx_original gpx_roundtrip); 134 check string "Creator preserved" 135 gpx_original.creator gpx_roundtrip.creator 136 | Error _ -> 137 failf "Round-trip parse failed for %s" filename) 138 | Error _ -> 139 failf "Round-trip write failed for %s" filename 140 with 141 | Gpx.Gpx_error _ -> 142 failf "Initial read failed for %s" filename 143 144(** Test validation works on all files *) 145let test_validation filename () = 146 let path = Filename.concat test_data_dir filename in 147 match Gpx_unix.read path with 148 | Ok gpx -> 149 let validation = Gpx.validate_gpx gpx in 150 check bool "Validation runs without error" true true; 151 (* All our test files should be valid *) 152 if filename <> "invalid.gpx" then 153 check bool "Test file is valid" true validation.is_valid 154 | Error _ -> 155 (* Invalid.gpx should fail to parse - this is expected *) 156 if filename = "invalid.gpx" then 157 check bool "Invalid file correctly fails to parse" true true 158 else 159 failf "Could not read %s for validation test" filename 160 161(** Test error handling with invalid file *) 162let test_error_handling () = 163 let path = Filename.concat test_data_dir "invalid.gpx" in 164 165 (* Test Unix error handling *) 166 (match Gpx_unix.read path with 167 | Ok _ -> 168 failf "Unix should have failed to parse invalid.gpx" 169 | Error _ -> 170 check bool "Unix correctly rejects invalid file" true true); 171 172 (* Test Eio error handling *) 173 (try 174 Eio_main.run @@ fun env -> 175 let fs = Eio.Stdenv.fs env in 176 let _ = Gpx_eio.read (fs / path) in 177 failf "Eio should have failed to parse invalid.gpx" 178 with 179 | Gpx.Gpx_error _ -> 180 check bool "Eio correctly rejects invalid file" true true) 181 182(** Performance comparison test *) 183let test_performance_comparison filename () = 184 let path = Filename.concat test_data_dir filename in 185 186 (* Time Unix parsing *) 187 let start_unix = Sys.time () in 188 let _ = Gpx_unix.read path in 189 let unix_time = Sys.time () -. start_unix in 190 191 (* Time Eio parsing *) 192 let start_eio = Sys.time () in 193 let _ = Eio_main.run @@ fun env -> 194 let fs = Eio.Stdenv.fs env in 195 try Some (Gpx_eio.read (fs / path)) 196 with Gpx.Gpx_error _ -> None 197 in 198 let eio_time = Sys.time () -. start_eio in 199 200 (* Both should complete reasonably quickly (under 1 second for test files) *) 201 check bool "Unix parsing completes quickly" true (unix_time < 1.0); 202 check bool "Eio parsing completes quickly" true (eio_time < 1.0); 203 204 Printf.printf "Performance for %s: Unix=%.3fms, Eio=%.3fms\n" 205 filename (unix_time *. 1000.) (eio_time *. 1000.) 206 207(** Generate test cases for each file *) 208let make_unix_tests () = 209 List.map (fun filename -> 210 test_case filename `Quick (test_unix_parsing filename) 211 ) test_files 212 213let make_eio_tests () = 214 List.map (fun filename -> 215 test_case filename `Quick (test_eio_parsing filename) 216 ) test_files 217 218let make_equivalence_tests () = 219 List.map (fun filename -> 220 test_case filename `Quick (test_unix_eio_equivalence filename) 221 ) test_files 222 223let make_unix_round_trip_tests () = 224 List.map (fun filename -> 225 test_case filename `Quick (test_unix_round_trip filename) 226 ) test_files 227 228let make_eio_round_trip_tests () = 229 List.map (fun filename -> 230 test_case filename `Quick (test_eio_round_trip filename) 231 ) test_files 232 233let make_validation_tests () = 234 List.map (fun filename -> 235 test_case filename `Quick (test_validation filename) 236 ) (test_files @ ["invalid.gpx"]) 237 238let make_performance_tests () = 239 List.map (fun filename -> 240 test_case filename `Quick (test_performance_comparison filename) 241 ) test_files 242 243(** Main test suite *) 244let () = 245 run "GPX Corpus Tests" [ 246 "Unix parsing", make_unix_tests (); 247 "Eio parsing", make_eio_tests (); 248 "Unix vs Eio equivalence", make_equivalence_tests (); 249 "Unix round-trip", make_unix_round_trip_tests (); 250 "Eio round-trip", make_eio_round_trip_tests (); 251 "Validation", make_validation_tests (); 252 "Error handling", [ 253 test_case "invalid file handling" `Quick test_error_handling; 254 ]; 255 "Performance", make_performance_tests (); 256 ]