#![allow(clippy::expect_used, clippy::panic)]
use super::*;
const SAMPLE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
<flight-plan xmlns="http://www8.garmin.com/xmlschemas/FlightPlan/v1">
<created>20260612T04:31:24Z</created>
<aircraft>
<aircraft-tailnumber>N123AB</aircraft-tailnumber>
</aircraft>
<flight-data>
<etd-zulu>20260612T03:20:00Z</etd-zulu>
<altitude-ft>40000</altitude-ft>
</flight-data>
<waypoint-table>
<waypoint>
<identifier>KEWR</identifier>
<type>AIRPORT</type>
<lat>40.692481</lat>
<lon>-74.168688</lon>
<altitude-ft></altitude-ft>
</waypoint>
<waypoint>
<identifier>PSB</identifier>
<type>VOR</type>
<lat>40.916258</lat>
<lon>-77.992717</lon>
</waypoint>
<waypoint>
<identifier>KAAO</identifier>
<type>AIRPORT</type>
<lat>37.746111</lat>
<lon>-97.221111</lon>
</waypoint>
</waypoint-table>
<route>
<route-name>KEWR TO KAAO</route-name>
<flight-plan-index>1</flight-plan-index>
<route-point>
<waypoint-identifier>KEWR</waypoint-identifier>
<waypoint-type>AIRPORT</waypoint-type>
</route-point>
<route-point>
<waypoint-identifier>PSB</waypoint-identifier>
<waypoint-type>VOR</waypoint-type>
</route-point>
<route-point>
<waypoint-identifier>KAAO</waypoint-identifier>
<waypoint-type>AIRPORT</waypoint-type>
</route-point>
</route>
</flight-plan>
"#;
fn to_utf16le(text: &str) -> Vec<u8> {
let mut bytes = vec![0xFF, 0xFE];
for unit in text.encode_utf16() {
bytes.extend_from_slice(&unit.to_le_bytes());
}
bytes
}
#[test]
fn parses_the_foreflight_dialect_from_utf16() {
let plan = FlightPlan::from_fpl_bytes(&to_utf16le(SAMPLE)).expect("parses");
assert_eq!(plan.name.as_deref(), Some("KEWR TO KAAO"));
assert_eq!(plan.aircraft_tail.as_deref(), Some("N123AB"));
assert_eq!(plan.cruise_altitude_ft, Some(40000));
assert_eq!(plan.etd, Some("2026-06-12T03:20:00Z".parse().expect("etd")));
assert_eq!(plan.departure(), Some("KEWR"));
assert_eq!(plan.destination(), Some("KAAO"));
assert_eq!(plan.route.len(), 3);
assert_eq!(plan.route[1].kind, WaypointType::Vor);
assert!((plan.route[1].position.lat - 40.916258).abs() < 1e-6);
}
#[test]
fn parses_utf8_input_too() {
let plan = FlightPlan::from_fpl_bytes(SAMPLE.as_bytes()).expect("parses utf-8");
assert_eq!(plan.route.len(), 3);
}
#[test]
fn export_round_trips_through_a_reparse() {
let plan = FlightPlan::from_fpl_bytes(SAMPLE.as_bytes()).expect("parses");
let xml = plan.to_fpl_string();
let again = FlightPlan::from_fpl_bytes(xml.as_bytes()).expect("reparses our own output");
assert_eq!(plan, again);
}
#[test]
fn a_route_point_not_in_the_table_is_a_hard_error() {
let broken = SAMPLE.replace(
"<waypoint-identifier>PSB</waypoint-identifier>",
"<waypoint-identifier>GHOST</waypoint-identifier>",
);
let error = FlightPlan::from_fpl_bytes(broken.as_bytes()).expect_err("must reject");
assert!(matches!(
error,
FplError::UnknownRoutePoint { identifier } if identifier == "GHOST"
));
}
#[test]
fn bridges_to_a_route_request_carrying_etd_and_altitude() {
let plan = FlightPlan::from_fpl_bytes(SAMPLE.as_bytes()).expect("parses");
let request = plan.to_route_briefing_request(25.0, aerocontext_core::FlightRules::Ifr);
assert_eq!(request.idents(), vec!["KEWR", "PSB", "KAAO"]);
assert_eq!(request.cruise_altitude_ft, Some(40000));
assert_eq!(request.departure_at, plan.etd);
assert!(!request.segment_bboxes(300.0).is_empty());
}
#[test]
fn expands_to_a_corridor_without_a_snapshot() {
let plan = FlightPlan::from_fpl_bytes(SAMPLE.as_bytes()).expect("parses");
let route = plan.to_expanded_route();
let corridor = crate::Corridor::around_route(&route, 25.0).expect("corridor builds");
assert!(corridor.contains(aerocontext_core::GeoPoint {
lat: 40.8,
lon: -76.0,
}));
}
#[test]
fn country_code_round_trips_when_present() {
let with_country = SAMPLE.replace(
"<type>VOR</type>\n <lat>40.916258</lat>",
"<type>VOR</type>\n <country-code>US</country-code>\n <lat>40.916258</lat>",
);
let plan = FlightPlan::from_fpl_bytes(with_country.as_bytes()).expect("parses");
assert_eq!(plan.route[1].country_code.as_deref(), Some("US"));
let again = FlightPlan::from_fpl_bytes(plan.to_fpl_string().as_bytes()).expect("reparses");
assert_eq!(again.route[1].country_code.as_deref(), Some("US"));
assert_eq!(plan, again);
}