(** Corpus tests using ppx_expect for GPX parsing *) open Gpx let test_data_dir = let rec find_data_dir current_dir = let data_path = Filename.concat current_dir "data" in let test_data_path = Filename.concat current_dir "test/data" in if Sys.file_exists data_path && Sys.is_directory data_path then data_path else if Sys.file_exists test_data_path && Sys.is_directory test_data_path then test_data_path else let parent = Filename.dirname current_dir in if parent = current_dir then failwith "Could not find test data directory" else find_data_dir parent in find_data_dir (Sys.getcwd ()) let read_test_file filename = let path = Filename.concat test_data_dir filename in In_channel.with_open_text path In_channel.input_all let%expect_test "parse simple waypoints" = let content = read_test_file "simple_waypoints.gpx" in match parse_string content with | Ok gpx -> let waypoints = Doc.waypoints gpx in Printf.printf "Waypoints count: %d\n" (List.length waypoints); Printf.printf "First waypoint name: %s\n" (match waypoints with | wpt :: _ -> (match Waypoint.name wpt with Some n -> n | None -> "None") | [] -> "None"); Printf.printf "Creator: %s\n" (Doc.creator gpx); [%expect {| Waypoints count: 3 First waypoint name: San Francisco Creator: mlgpx test suite |}] | Error err -> Printf.printf "Error: %s\n" (Error.to_string err); [%expect.unreachable] let%expect_test "parse detailed waypoints" = let content = read_test_file "detailed_waypoints.gpx" in match parse_string content with | Ok gpx -> let waypoints = Doc.waypoints gpx in let metadata = Doc.metadata gpx in Printf.printf "Waypoints count: %d\n" (List.length waypoints); Printf.printf "Has metadata time: %b\n" (match metadata with Some md -> Metadata.time md <> None | None -> false); Printf.printf "Has bounds: %b\n" (match metadata with Some md -> Metadata.bounds_opt md <> None | None -> false); (match waypoints with | wpt :: _ -> Printf.printf "First waypoint has elevation: %b\n" (Waypoint.elevation wpt <> None); Printf.printf "First waypoint has time: %b\n" (Waypoint.time wpt <> None); Printf.printf "First waypoint has links: %b\n" (Waypoint.links wpt <> []) | [] -> ()); [%expect {| Waypoints count: 2 Has metadata time: true Has bounds: true First waypoint has elevation: true First waypoint has time: true First waypoint has links: true |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "parse simple route" = let content = read_test_file "simple_route.gpx" in match parse_string content with | Ok gpx -> let routes = Doc.routes gpx in Printf.printf "Routes count: %d\n" (List.length routes); (match routes with | rte :: _ -> Printf.printf "Route name: %s\n" (match Route.name rte with Some n -> n | None -> "None"); Printf.printf "Route points count: %d\n" (Route.point_count rte); Printf.printf "Route has number: %b\n" (Route.number rte <> None) | [] -> ()); [%expect {| Routes count: 1 Route name: SF to Oakland Route points count: 3 Route has number: true |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "parse simple track" = let content = read_test_file "simple_track.gpx" in match parse_string content with | Ok gpx -> let tracks = Doc.tracks gpx in Printf.printf "Tracks count: %d\n" (List.length tracks); (match tracks with | trk :: _ -> Printf.printf "Track name: %s\n" (match Track.name trk with Some n -> n | None -> "None"); Printf.printf "Track segments: %d\n" (Track.segment_count trk); let segments = Track.segments trk in (match segments with | seg :: _ -> Printf.printf "First segment points: %d\n" (Track.Segment.point_count seg); let points = Track.Segment.points seg in (match points with | pt :: _ -> Printf.printf "First point has elevation: %b\n" (Waypoint.elevation pt <> None); Printf.printf "First point has time: %b\n" (Waypoint.time pt <> None) | [] -> ()) | [] -> ()) | [] -> ()); [%expect {| Tracks count: 1 Track name: Morning Jog Track segments: 1 First segment points: 5 First point has elevation: true First point has time: true |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "parse multi-segment track" = let content = read_test_file "multi_segment_track.gpx" in match parse_string content with | Ok gpx -> let tracks = Doc.tracks gpx in Printf.printf "Tracks count: %d\n" (List.length tracks); (match tracks with | trk :: _ -> Printf.printf "Track segments: %d\n" (Track.segment_count trk); let total_points = Track.point_count trk in Printf.printf "Total track points: %d\n" total_points | [] -> ()); [%expect {| Tracks count: 1 Track segments: 2 Total track points: 6 |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "parse comprehensive gpx" = let content = read_test_file "comprehensive.gpx" in match parse_string content with | Ok gpx -> let waypoints = Doc.waypoints gpx in let routes = Doc.routes gpx in let tracks = Doc.tracks gpx in let metadata = Doc.metadata gpx in Printf.printf "Waypoints: %d\n" (List.length waypoints); Printf.printf "Routes: %d\n" (List.length routes); Printf.printf "Tracks: %d\n" (List.length tracks); Printf.printf "Has author: %b\n" (match metadata with Some md -> Metadata.author md <> None | None -> false); Printf.printf "Has copyright: %b\n" (match metadata with Some md -> Metadata.copyright md <> None | None -> false); Printf.printf "Has keywords: %b\n" (match metadata with Some md -> Metadata.keywords md <> None | None -> false); [%expect {| Waypoints: 2 Routes: 1 Tracks: 1 Has author: true Has copyright: true Has keywords: true |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "parse minimal gpx" = let content = read_test_file "minimal.gpx" in match parse_string content with | Ok gpx -> Printf.printf "Minimal GPX parsed successfully\n"; let waypoints = Doc.waypoints gpx in let routes = Doc.routes gpx in let tracks = Doc.tracks gpx in Printf.printf "Waypoints: %d\n" (List.length waypoints); Printf.printf "Routes: %d\n" (List.length routes); Printf.printf "Tracks: %d\n" (List.length tracks); [%expect {| Minimal GPX parsed successfully Waypoints: 1 Routes: 0 Tracks: 0 |}] | Error err -> Printf.printf "Error: %s\n" (match err with | Invalid_xml s -> "Invalid XML: " ^ s | _ -> "Other error"); [%expect.unreachable] let%expect_test "parse edge cases" = let content = read_test_file "edge_cases.gpx" in match parse_string content with | Ok gpx -> Printf.printf "Edge cases parsed successfully\n"; let waypoints = Doc.waypoints gpx in let tracks = Doc.tracks gpx in Printf.printf "Waypoints: %d\n" (List.length waypoints); Printf.printf "Tracks: %d\n" (List.length tracks); (* Check coordinate ranges *) let check_coords () = match waypoints with | wpt1 :: wpt2 :: wpt3 :: _ -> let lat1, lon1 = Waypoint.to_floats wpt1 in let lat2, lon2 = Waypoint.to_floats wpt2 in let lat3, lon3 = Waypoint.to_floats wpt3 in Printf.printf "South pole coords: %.1f, %.1f\n" lat1 lon1; Printf.printf "North pole coords: %.1f, %.6f\n" lat2 lon2; Printf.printf "Null island coords: %.1f, %.1f\n" lat3 lon3; | _ -> Printf.printf "Unexpected waypoint count\n" in check_coords (); [%expect {| Edge cases parsed successfully Waypoints: 3 Tracks: 1 South pole coords: -90.0, -180.0 North pole coords: 90.0, 179.999999 Null island coords: 0.0, 0.0 |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "test validation" = let content = read_test_file "comprehensive.gpx" in match parse_string content with | Ok gpx -> let validation = validate_gpx gpx in Printf.printf "Is valid: %b\n" validation.is_valid; Printf.printf "Issue count: %d\n" (List.length validation.issues); [%expect {| Is valid: true Issue count: 0 |}] | Error _ -> Printf.printf "Parse error\n"; [%expect.unreachable] let%expect_test "round-trip test" = let content = read_test_file "simple_waypoints.gpx" in match parse_string content with | Ok gpx -> (match write_string gpx with | Ok xml_output -> (match parse_string xml_output with | Ok gpx2 -> Printf.printf "Round-trip successful\n"; let waypoints = Doc.waypoints gpx in let waypoints2 = Doc.waypoints gpx2 in Printf.printf "Original waypoints: %d\n" (List.length waypoints); Printf.printf "Round-trip waypoints: %d\n" (List.length waypoints2); Printf.printf "Creators match: %b\n" (Doc.creator gpx = Doc.creator gpx2); [%expect.unreachable] | Error _ -> Printf.printf "Round-trip parse failed\n"; [%expect.unreachable]) | Error _ -> Printf.printf "Write failed\n"; [%expect {| Write failed |}]) | Error _ -> Printf.printf "Initial parse failed\n"; [%expect.unreachable]