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]