#[cfg(test)]
mod tests {
use common::MessageWithF64;
use serde_json::{Value, json};
use test_case::test_case;
type Result = anyhow::Result<()>;
#[test_case(MessageWithF64::new(), json!({}))]
#[test_case(MessageWithF64::new().set_singular(0.0), json!({}))]
#[test_case(MessageWithF64::new().set_singular(1.5), json!({"singular": 1.5}))]
#[test_case(MessageWithF64::new().set_singular(f64::INFINITY), json!({"singular": "Infinity"}))]
#[test_case(MessageWithF64::new().set_singular(-f64::INFINITY), json!({"singular": "-Infinity"}); "singular minus inf")]
#[test_case(MessageWithF64::new().set_singular(f64::NAN), json!({"singular": "NaN"}))]
#[test_case(MessageWithF64::new().set_optional(0.0), json!({"optional": 0.0}))]
#[test_case(MessageWithF64::new().set_or_clear_optional(None::<f64>), json!({}))]
#[test_case(MessageWithF64::new().set_optional(1.5), json!({"optional": 1.5}))]
#[test_case(MessageWithF64::new().set_optional(f64::INFINITY), json!({"optional": "Infinity"}))]
#[test_case(MessageWithF64::new().set_optional(-f64::INFINITY), json!({"optional": "-Infinity"}); "optional minus inf")]
#[test_case(MessageWithF64::new().set_optional(f64::NAN), json!({"optional": "NaN"}))]
#[test_case(MessageWithF64::new().set_repeated([0_f64;0]), json!({}))]
#[test_case(MessageWithF64::new().set_repeated([0.0, 1.5, 2.5]), json!({"repeated": [0.0, 1.5, 2.5]}))]
#[test_case(MessageWithF64::new().set_repeated([0.0, f64::NAN, f64::INFINITY]), json!({"repeated": [0.0, "NaN", "Infinity"]}))]
#[test_case(MessageWithF64::new().set_map([("", 0_f64);0]), json!({}))]
#[test_case(MessageWithF64::new().set_map([("a", 0_f64), ("b", 1_f64)]), json!({"map": {"a": 0.0, "b": 1.0}}))]
#[test_case(MessageWithF64::new().set_map([("a", f64::NAN), ("b", f64::INFINITY)]), json!({"map": {"a": "NaN", "b": "Infinity"}}))]
fn test_ser(input: MessageWithF64, want: Value) -> Result {
let got = serde_json::to_value(input)?;
assert_eq!(got, want);
Ok(())
}
#[test_case(MessageWithF64::new(), json!({}))]
#[test_case(MessageWithF64::new().set_singular(0.0), json!({"singular": null}))]
#[test_case(MessageWithF64::new().set_singular(0.0), json!({}))]
#[test_case(MessageWithF64::new().set_singular(1.5), json!({"singular": 1.5}))]
#[test_case(MessageWithF64::new().set_optional(0.0), json!({"optional": 0.0}))]
#[test_case(MessageWithF64::new().set_or_clear_optional(None::<f64>), json!({}))]
#[test_case(MessageWithF64::new().set_optional(1.5), json!({"optional": 1.5}))]
#[test_case(MessageWithF64::new().set_repeated([0_f64;0]), json!({}))]
#[test_case(MessageWithF64::new().set_repeated([0.0, 1.5, 2.5]), json!({"repeated": [0.0, 1.5, 2.5]}))]
#[test_case(MessageWithF64::new().set_repeated([0.0, 1.5, 2.5]), json!({"repeated": [0, 1.5, "2.5"]}))]
#[test_case(MessageWithF64::new().set_map([("", 0_f64);0]), json!({}))]
#[test_case(MessageWithF64::new().set_map([("a", 0_f64), ("b", 1_f64)]), json!({"map": {"a": 0.0, "b": 1.0}}))]
fn test_de(want: MessageWithF64, input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got, want);
Ok(())
}
#[test_case(MessageWithF64::new().set_singular(f64::INFINITY), json!({"singular": "Infinity"}))]
#[test_case(MessageWithF64::new().set_singular(-f64::INFINITY), json!({"singular": "-Infinity"}); "singular minus inf")]
#[test_case(MessageWithF64::new().set_singular(f64::NAN), json!({"singular": "NaN"}))]
#[test_case(MessageWithF64::new().set_optional(f64::INFINITY), json!({"optional": "Infinity"}))]
#[test_case(MessageWithF64::new().set_optional(-f64::INFINITY), json!({"optional": "-Infinity"}); "optional minus inf")]
#[test_case(MessageWithF64::new().set_optional(f64::NAN), json!({"optional": "NaN"}))]
fn test_de_exceptional(want: MessageWithF64, input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(
want.singular.total_cmp(&got.singular),
std::cmp::Ordering::Equal,
"{got:?} != {want:?})"
);
match (&want.optional, &got.optional) {
(None, None) => {}
(Some(l), Some(r)) => {
assert_eq!(
l.total_cmp(r),
std::cmp::Ordering::Equal,
"{got:?} != {want:?})"
);
}
(None, Some(_)) | (Some(_), None) => panic!("mismatched optional {got:?} != {want:?}"),
}
Ok(())
}
#[test_case(r#"{"singular": null}"#)]
#[test_case(r#"{"optional": null}"#)]
#[test_case(r#"{"repeated": null}"#)]
#[test_case(r#"{"map": null}"#)]
fn test_null_is_default(input: &str) -> Result {
let got = serde_json::from_str::<MessageWithF64>(input)?;
assert_eq!(got, MessageWithF64::default());
Ok(())
}
#[test_case(r#"{"singular": 0, "singular": 0}"#)]
#[test_case(r#"{"optional": 0, "optional": 0}"#)]
#[test_case(r#"{"repeated": [], "repeated": []}"#)]
#[test_case(r#"{"map": {}, "map": {}}"#)]
fn reject_duplicate_fields(input: &str) -> Result {
let err = serde_json::from_str::<MessageWithF64>(input).unwrap_err();
assert!(err.is_data(), "{err:?}");
Ok(())
}
#[test_case(json!({"unknown": "test-value"}))]
#[test_case(json!({"unknown": "test-value", "moreUnknown": {"a": 1, "b": 2}}))]
fn test_unknown(input: Value) -> Result {
let deser = serde_json::from_value::<MessageWithF64>(input.clone())?;
let got = serde_json::to_value(deser)?;
assert_eq!(got, input);
Ok(())
}
#[test_case(9876.5, 9876.5)]
#[test_case(f64::INFINITY, "Infinity")]
#[test_case(f64::NEG_INFINITY, "-Infinity")]
#[test_case(f64::NAN, "NaN")]
fn test_singular<T>(input: f64, want: T) -> Result
where
T: serde::ser::Serialize,
{
let msg = MessageWithF64::new().set_singular(input);
let got = serde_json::to_value(&msg)?;
let want = json!({"singular": want});
assert_eq!(want, got);
let roundtrip = serde_json::from_value::<MessageWithF64>(got)?;
assert_float_eq(msg.singular, roundtrip.singular);
Ok(())
}
#[test_case(-1, -1.0)]
#[test_case(-2, -2.0)]
#[test_case(3, 3.0)]
#[test_case(4, 4.0)]
fn test_singular_as_int(input: i64, want: f64) -> Result {
let input = json!({"singular": input});
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got.singular, want);
Ok(())
}
#[test_case("-1", -1.0)]
#[test_case("-2", -2.0)]
#[test_case("3", 3.0)]
#[test_case("4", 4.0)]
fn test_singular_as_string(input: &str, want: f64) -> Result {
let input = json!({"singular": input});
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got.singular, want);
Ok(())
}
#[test_case(json!({}))]
#[test_case(json!({"singular": null}))]
#[test_case(json!({"singular": 0}))]
#[test_case(json!({"singular": 0.0}))]
#[test_case(json!({"singular": 0e0}))]
#[test_case(json!({"singular": "0"}); "0 string")]
#[test_case(json!({"singular": "0.0"}); "0.0 string")]
#[test_case(json!({"singular": "0e0"}); "0e0 string")]
fn test_singular_default(input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got, MessageWithF64::default());
let output = serde_json::to_value(&got)?;
assert_eq!(output, json!({}));
Ok(())
}
#[test_case(9876.5, 9876.5)]
#[test_case(f64::INFINITY, "Infinity")]
#[test_case(f64::NEG_INFINITY, "-Infinity")]
#[test_case(f64::NAN, "NaN")]
fn test_optional<T>(input: f64, want: T) -> Result
where
T: serde::ser::Serialize,
{
let msg = MessageWithF64::new().set_optional(input);
let got = serde_json::to_value(&msg)?;
let want = json!({"optional": want});
assert_eq!(want, got);
let roundtrip = serde_json::from_value::<MessageWithF64>(got)?;
assert_float_eq(msg.optional.unwrap(), roundtrip.optional.unwrap());
Ok(())
}
#[test_case(json!({}))]
#[test_case(json!({"optional": null}))]
fn test_optional_default(input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got, MessageWithF64::default());
let output = serde_json::to_value(&got)?;
assert_eq!(output, json!({}));
Ok(())
}
#[test]
fn test_repeated() -> Result {
let msg = MessageWithF64::new().set_repeated([
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
9876.5_f64,
]);
let got = serde_json::to_value(&msg)?;
let want = json!({"repeated": ["Infinity", "-Infinity", "NaN", 9876.5]});
assert_eq!(want, got);
let roundtrip = serde_json::from_value::<MessageWithF64>(got)?;
for (roundtrip, msg) in roundtrip.repeated.iter().zip(msg.repeated.iter()) {
assert_float_eq(*roundtrip, *msg);
}
Ok(())
}
#[test_case(json!({}))]
#[test_case(json!({"repeated": []}))]
#[test_case(json!({"repeated": null}))]
fn test_repeated_default(input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got, MessageWithF64::default());
let output = serde_json::to_value(&got)?;
assert_eq!(output, json!({}));
Ok(())
}
#[test]
fn test_map() -> Result {
let want = MessageWithF64::new().set_map([
("number", 9876.5),
("inf", f64::INFINITY),
("-inf", f64::NEG_INFINITY),
("nan", f64::NAN),
("int", 1.0),
("str", 2.0),
("str_int", 3.0),
]);
let input = json!({
"map": {
"number": 9876.5,
"inf": "Infinity",
"-inf": "-Infinity",
"nan": "NaN",
"int": 1,
"str": "2.0",
"str_int": "3",
}
});
let got = serde_json::from_value::<MessageWithF64>(input.clone())?;
for (k, v) in want.map.iter() {
let w = got
.map
.get(k)
.unwrap_or_else(|| panic!("missing {k} in got.map"));
assert_float_eq(*v, *w);
}
let want_value = serde_json::to_value(&want)?;
let got_value = serde_json::to_value(&got)?;
assert_eq!(got_value, want_value);
Ok(())
}
#[test_case(json!({}))]
#[test_case(json!({"map": {}}))]
#[test_case(json!({"map": null}))]
fn test_map_default(input: Value) -> Result {
let got = serde_json::from_value::<MessageWithF64>(input)?;
assert_eq!(got, MessageWithF64::default());
let output = serde_json::to_value(&got)?;
assert_eq!(output, json!({}));
Ok(())
}
fn assert_float_eq(left: f64, right: f64) {
if left.is_nan() && right.is_nan() {
return;
}
if left.is_infinite()
&& right.is_infinite()
&& left.is_sign_positive() == right.is_sign_positive()
{
return;
}
assert_eq!(left, right);
}
}