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