1(** Corpus tests using ppx_expect for GPX parsing *) 2 3open Gpx 4 5let test_data_dir = 6 let cwd = Sys.getcwd () in 7 let basename = Filename.basename cwd in 8 if basename = "test" then 9 "data" (* Running from test/ directory *) 10 else if basename = "_build" || String.contains cwd '_' then 11 "../test/data" (* Running from _build during tests *) 12 else 13 "test/data" (* Running from project root *) 14 15let read_test_file filename = 16 let path = Filename.concat test_data_dir filename in 17 In_channel.with_open_text path In_channel.input_all 18 19let%expect_test "parse simple waypoints" = 20 let content = read_test_file "simple_waypoints.gpx" in 21 match parse_string content with 22 | Ok gpx -> 23 Printf.printf "Waypoints count: %d\n" (List.length gpx.waypoints); 24 Printf.printf "First waypoint name: %s\n" 25 (match gpx.waypoints with 26 | wpt :: _ -> (match wpt.name with Some n -> n | None -> "None") 27 | [] -> "None"); 28 Printf.printf "Creator: %s\n" gpx.creator; 29 [%expect {| 30 Waypoints count: 3 31 First waypoint name: San Francisco 32 Creator: mlgpx test suite |}] 33 | Error err -> 34 Printf.printf "Error: %s\n" (match err with 35 | Invalid_xml s -> "Invalid XML: " ^ s 36 | Invalid_coordinate s -> "Invalid coordinate: " ^ s 37 | _ -> "Other error"); 38 [%expect.unreachable] 39 40let%expect_test "parse detailed waypoints" = 41 let content = read_test_file "detailed_waypoints.gpx" in 42 match parse_string content with 43 | Ok gpx -> 44 Printf.printf "Waypoints count: %d\n" (List.length gpx.waypoints); 45 Printf.printf "Has metadata time: %b\n" 46 (match gpx.metadata with Some md -> md.time <> None | None -> false); 47 Printf.printf "Has bounds: %b\n" 48 (match gpx.metadata with Some md -> md.bounds <> None | None -> false); 49 (match gpx.waypoints with 50 | wpt :: _ -> 51 Printf.printf "First waypoint has elevation: %b\n" (wpt.ele <> None); 52 Printf.printf "First waypoint has time: %b\n" (wpt.time <> None); 53 Printf.printf "First waypoint has links: %b\n" (wpt.links <> []) 54 | [] -> ()); 55 [%expect {| 56 Waypoints count: 2 57 Has metadata time: true 58 Has bounds: true 59 First waypoint has elevation: true 60 First waypoint has time: true 61 First waypoint has links: true |}] 62 | Error _ -> 63 Printf.printf "Parse error\n"; 64 [%expect.unreachable] 65 66let%expect_test "parse simple route" = 67 let content = read_test_file "simple_route.gpx" in 68 match parse_string content with 69 | Ok gpx -> 70 Printf.printf "Routes count: %d\n" (List.length gpx.routes); 71 (match gpx.routes with 72 | rte :: _ -> 73 Printf.printf "Route name: %s\n" 74 (match rte.name with Some n -> n | None -> "None"); 75 Printf.printf "Route points count: %d\n" (List.length rte.rtepts); 76 Printf.printf "Route has number: %b\n" (rte.number <> None) 77 | [] -> ()); 78 [%expect {| 79 Routes count: 1 80 Route name: SF to Oakland 81 Route points count: 3 82 Route has number: true |}] 83 | Error _ -> 84 Printf.printf "Parse error\n"; 85 [%expect.unreachable] 86 87let%expect_test "parse simple track" = 88 let content = read_test_file "simple_track.gpx" in 89 match parse_string content with 90 | Ok gpx -> 91 Printf.printf "Tracks count: %d\n" (List.length gpx.tracks); 92 (match gpx.tracks with 93 | trk :: _ -> 94 Printf.printf "Track name: %s\n" 95 (match trk.name with Some n -> n | None -> "None"); 96 Printf.printf "Track segments: %d\n" (List.length trk.trksegs); 97 (match trk.trksegs with 98 | seg :: _ -> 99 Printf.printf "First segment points: %d\n" (List.length seg.trkpts); 100 (match seg.trkpts with 101 | pt :: _ -> 102 Printf.printf "First point has elevation: %b\n" (pt.ele <> None); 103 Printf.printf "First point has time: %b\n" (pt.time <> None) 104 | [] -> ()) 105 | [] -> ()) 106 | [] -> ()); 107 [%expect {| 108 Tracks count: 1 109 Track name: Morning Jog 110 Track segments: 1 111 First segment points: 5 112 First point has elevation: true 113 First point has time: true |}] 114 | Error _ -> 115 Printf.printf "Parse error\n"; 116 [%expect.unreachable] 117 118let%expect_test "parse multi-segment track" = 119 let content = read_test_file "multi_segment_track.gpx" in 120 match parse_string content with 121 | Ok gpx -> 122 Printf.printf "Tracks count: %d\n" (List.length gpx.tracks); 123 (match gpx.tracks with 124 | trk :: _ -> 125 Printf.printf "Track segments: %d\n" (List.length trk.trksegs); 126 let total_points = List.fold_left (fun acc seg -> 127 acc + List.length seg.trkpts) 0 trk.trksegs in 128 Printf.printf "Total track points: %d\n" total_points 129 | [] -> ()); 130 [%expect {| 131 Tracks count: 1 132 Track segments: 2 133 Total track points: 6 |}] 134 | Error _ -> 135 Printf.printf "Parse error\n"; 136 [%expect.unreachable] 137 138let%expect_test "parse comprehensive gpx" = 139 let content = read_test_file "comprehensive.gpx" in 140 match parse_string content with 141 | Ok gpx -> 142 Printf.printf "Waypoints: %d\n" (List.length gpx.waypoints); 143 Printf.printf "Routes: %d\n" (List.length gpx.routes); 144 Printf.printf "Tracks: %d\n" (List.length gpx.tracks); 145 Printf.printf "Has author: %b\n" 146 (match gpx.metadata with Some md -> md.author <> None | None -> false); 147 Printf.printf "Has copyright: %b\n" 148 (match gpx.metadata with Some md -> md.copyright <> None | None -> false); 149 Printf.printf "Has keywords: %b\n" 150 (match gpx.metadata with Some md -> md.keywords <> None | None -> false); 151 [%expect {| 152 Waypoints: 2 153 Routes: 1 154 Tracks: 1 155 Has author: true 156 Has copyright: true 157 Has keywords: true |}] 158 | Error _ -> 159 Printf.printf "Parse error\n"; 160 [%expect.unreachable] 161 162let%expect_test "parse minimal gpx" = 163 let content = read_test_file "minimal.gpx" in 164 match parse_string content with 165 | Ok gpx -> 166 Printf.printf "Minimal GPX parsed successfully\n"; 167 Printf.printf "Waypoints: %d\n" (List.length gpx.waypoints); 168 Printf.printf "Routes: %d\n" (List.length gpx.routes); 169 Printf.printf "Tracks: %d\n" (List.length gpx.tracks); 170 [%expect {| 171 Minimal GPX parsed successfully 172 Waypoints: 1 173 Routes: 0 174 Tracks: 0 |}] 175 | Error err -> 176 Printf.printf "Error: %s\n" (match err with 177 | Invalid_xml s -> "Invalid XML: " ^ s 178 | _ -> "Other error"); 179 [%expect.unreachable] 180 181let%expect_test "parse edge cases" = 182 let content = read_test_file "edge_cases.gpx" in 183 match parse_string content with 184 | Ok gpx -> 185 Printf.printf "Edge cases parsed successfully\n"; 186 Printf.printf "Waypoints: %d\n" (List.length gpx.waypoints); 187 Printf.printf "Tracks: %d\n" (List.length gpx.tracks); 188 (* Check coordinate ranges *) 189 let check_coords () = 190 match gpx.waypoints with 191 | wpt1 :: wpt2 :: wpt3 :: _ -> 192 Printf.printf "South pole coords: %.1f, %.1f\n" 193 (latitude_to_float wpt1.lat) (longitude_to_float wpt1.lon); 194 Printf.printf "North pole coords: %.1f, %.6f\n" 195 (latitude_to_float wpt2.lat) (longitude_to_float wpt2.lon); 196 Printf.printf "Null island coords: %.1f, %.1f\n" 197 (latitude_to_float wpt3.lat) (longitude_to_float wpt3.lon); 198 | _ -> Printf.printf "Unexpected waypoint count\n" 199 in 200 check_coords (); 201 [%expect {| 202 Edge cases parsed successfully 203 Waypoints: 3 204 Tracks: 1 205 South pole coords: -90.0, -180.0 206 North pole coords: 90.0, 180.000000 207 Null island coords: 0.0, 0.0 |}] 208 | Error _ -> 209 Printf.printf "Parse error\n"; 210 [%expect.unreachable] 211 212let%expect_test "test validation" = 213 let content = read_test_file "comprehensive.gpx" in 214 match parse_string content with 215 | Ok gpx -> 216 let validation = validate_gpx gpx in 217 Printf.printf "Is valid: %b\n" validation.is_valid; 218 Printf.printf "Issue count: %d\n" (List.length validation.issues); 219 [%expect {| 220 Is valid: true 221 Issue count: 0 |}] 222 | Error _ -> 223 Printf.printf "Parse error\n"; 224 [%expect.unreachable] 225 226let%expect_test "round-trip test" = 227 let content = read_test_file "simple_waypoints.gpx" in 228 match parse_string content with 229 | Ok gpx -> 230 (match write_string gpx with 231 | Ok xml_output -> 232 (match parse_string xml_output with 233 | Ok gpx2 -> 234 Printf.printf "Round-trip successful\n"; 235 Printf.printf "Original waypoints: %d\n" (List.length gpx.waypoints); 236 Printf.printf "Round-trip waypoints: %d\n" (List.length gpx2.waypoints); 237 Printf.printf "Creators match: %b\n" (gpx.creator = gpx2.creator); 238 [%expect {| 239 Round-trip successful 240 Original waypoints: 3 241 Round-trip waypoints: 3 242 Creators match: true |}] 243 | Error _ -> 244 Printf.printf "Round-trip parse failed\n"; 245 [%expect.unreachable]) 246 | Error _ -> 247 Printf.printf "Write failed\n"; 248 [%expect.unreachable]) 249 | Error _ -> 250 Printf.printf "Initial parse failed\n"; 251 [%expect.unreachable]