#![cfg(all(feature = "ffi", feature = "serde"))]
use super::*;
use crate::ast::{BinaryOp, Direction, IntegralBounds, MathConstant, NumberSet, SetOp, UnaryOp};
use crate::{ExprKind, Expression};
#[test]
fn test_json_integer() {
let expr = parse_text("42").unwrap();
let json = expression_to_json(&expr).unwrap();
assert_eq!(json, r#"{"kind":"Integer","value":42}"#);
}
#[test]
fn test_json_float() {
let expr = parse_text("3.14").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Float"),
"expected Float variant, got: {json}"
);
}
#[test]
fn test_json_variable() {
let expr = parse_text("x").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Variable"),
"expected Variable variant, got: {json}"
);
assert!(
json.contains('"'),
"expected quoted string value, got: {json}"
);
}
#[test]
fn test_json_constant_pi() {
let expr = parse_text("pi").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Constant"), "expected Constant, got: {json}");
assert!(json.contains("Pi"), "expected Pi, got: {json}");
}
#[test]
fn test_json_constant_e() {
let expr = Expression::constant(MathConstant::E);
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Constant"), "expected Constant, got: {json}");
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "E");
}
#[test]
fn test_json_constant_i() {
let expr = Expression::constant(MathConstant::I);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "I");
}
#[test]
fn test_json_constant_j() {
let expr = Expression::constant(MathConstant::J);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "J");
}
#[test]
fn test_json_constant_k() {
let expr = Expression::constant(MathConstant::K);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "K");
}
#[test]
fn test_json_constant_infinity() {
let expr = Expression::constant(MathConstant::Infinity);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "Infinity");
}
#[test]
fn test_json_constant_neg_infinity() {
let expr = Expression::constant(MathConstant::NegInfinity);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "NegInfinity");
}
#[test]
fn test_json_constant_nan() {
let expr = Expression::constant(MathConstant::NaN);
let json = expression_to_json(&expr).unwrap();
let val: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(val["value"]["kind"], "NaN");
}
fn assert_binary_op(input: &str, expected_op: &str) {
let expr = parse_text(input).unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Binary"), "expected Binary, got: {json}");
assert!(
json.contains(expected_op),
"expected op {expected_op}, got: {json}"
);
}
#[test]
fn test_json_binary_add() {
assert_binary_op("x + y", "Add");
}
#[test]
fn test_json_binary_sub() {
assert_binary_op("x - y", "Sub");
}
#[test]
fn test_json_binary_mul() {
assert_binary_op("x * y", "Mul");
}
#[test]
fn test_json_binary_div() {
assert_binary_op("x / y", "Div");
}
#[test]
fn test_json_binary_pow() {
assert_binary_op("x^2", "Pow");
}
#[test]
fn test_json_binary_mod() {
assert_binary_op("x % y", "Mod");
}
#[test]
fn test_json_binary_plus_minus() {
let expr: Expression = ExprKind::Binary {
op: BinaryOp::PlusMinus,
left: Box::new(Expression::variable("x")),
right: Box::new(Expression::integer(1)),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("PlusMinus"),
"expected PlusMinus, got: {json}"
);
}
#[test]
fn test_json_unary_neg() {
let expr = parse_text("-x").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Neg") || json.contains("Unary"),
"expected negation, got: {json}"
);
}
#[test]
fn test_json_unary_factorial() {
let expr: Expression = ExprKind::Unary {
op: UnaryOp::Factorial,
operand: Box::new(Expression::variable("n")),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Factorial"),
"expected Factorial, got: {json}"
);
}
#[test]
fn test_json_function_call() {
let expr = parse_text("sin(x)").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Function"), "expected Function, got: {json}");
assert!(json.contains("sin"), "expected sin, got: {json}");
}
#[test]
fn test_json_function_multi_arg() {
let expr = parse_text("max(a, b)").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Function"), "expected Function, got: {json}");
assert!(json.contains("max"), "expected max, got: {json}");
}
#[test]
fn test_json_derivative_round_trip() {
let expr: Expression = ExprKind::Derivative {
expr: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(Expression::variable("x")),
right: Box::new(Expression::integer(2)),
}
.into(),
),
var: "x".to_string(),
order: 1,
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Derivative"),
"expected Derivative, got: {json}"
);
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_partial_derivative_round_trip() {
let expr: Expression = ExprKind::PartialDerivative {
expr: Box::new(Expression::variable("f")),
var: "x".to_string(),
order: 2,
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("PartialDerivative"),
"expected PartialDerivative, got: {json}"
);
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_integral_indefinite_round_trip() {
let expr: Expression = ExprKind::Integral {
integrand: Box::new(Expression::variable("x")),
var: "x".to_string(),
bounds: None,
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Integral"), "expected Integral, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_integral_definite_round_trip() {
let expr: Expression = ExprKind::Integral {
integrand: Box::new(
ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(Expression::variable("x")),
right: Box::new(Expression::integer(2)),
}
.into(),
),
var: "x".to_string(),
bounds: Some(IntegralBounds {
lower: Box::new(Expression::integer(0)),
upper: Box::new(Expression::integer(1)),
}),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("bounds"),
"expected bounds field, got: {json}"
);
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_limit_round_trip() {
let expr: Expression = ExprKind::Limit {
expr: Box::new(
ExprKind::Binary {
op: BinaryOp::Div,
left: Box::new(
ExprKind::Function {
name: "sin".to_string(),
args: vec![Expression::variable("x")],
}
.into(),
),
right: Box::new(Expression::variable("x")),
}
.into(),
),
var: "x".to_string(),
to: Box::new(Expression::integer(0)),
direction: Direction::Both,
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Limit"), "expected Limit, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_sum_round_trip() {
let expr: Expression = ExprKind::Sum {
index: "i".to_string(),
lower: Box::new(Expression::integer(1)),
upper: Box::new(Expression::variable("n")),
body: Box::new(Expression::variable("i")),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Sum"), "expected Sum, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_product_round_trip() {
let expr: Expression = ExprKind::Product {
index: "k".to_string(),
lower: Box::new(Expression::integer(1)),
upper: Box::new(Expression::variable("n")),
body: Box::new(Expression::variable("k")),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Product"), "expected Product, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_vector_round_trip() {
let expr: Expression = ExprKind::Vector(vec![
Expression::integer(1),
Expression::integer(2),
Expression::integer(3),
])
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Vector"), "expected Vector, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_matrix_round_trip() {
let expr: Expression = ExprKind::Matrix(vec![
vec![Expression::integer(1), Expression::integer(0)],
vec![Expression::integer(0), Expression::integer(1)],
])
.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Matrix"), "expected Matrix, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_set_operation_round_trip() {
let expr: Expression = ExprKind::SetOperation {
op: SetOp::Union,
left: Box::new(Expression::number_set(NumberSet::Real)),
right: Box::new(Expression::number_set(NumberSet::Integer)),
}
.into();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("SetOperation"),
"expected SetOperation, got: {json}"
);
assert!(json.contains("Union"), "expected Union, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_empty_set_round_trip() {
let expr: Expression = ExprKind::EmptySet.into();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("EmptySet"), "expected EmptySet, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_equation_round_trip() {
let expr = parse_text("x = 5").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(json.contains("Equation"), "expected Equation, got: {json}");
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_inequality_round_trip() {
let expr = parse_text("x < 5").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("Inequality"),
"expected Inequality, got: {json}"
);
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_nested_expression() {
let expr = parse_text("sin(x)^2 + cos(x)^2").unwrap();
let json = expression_to_json(&expr).unwrap();
assert!(
json.contains("sin"),
"expected sin in nested expr, got: {json}"
);
assert!(
json.contains("cos"),
"expected cos in nested expr, got: {json}"
);
}
#[test]
fn test_json_deeply_nested_round_trip() {
let expr: Expression = ExprKind::Binary {
op: BinaryOp::Pow,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Mul,
left: Box::new(
ExprKind::Binary {
op: BinaryOp::Add,
left: Box::new(Expression::variable("x")),
right: Box::new(Expression::integer(1)),
}
.into(),
),
right: Box::new(
ExprKind::Binary {
op: BinaryOp::Sub,
left: Box::new(Expression::variable("y")),
right: Box::new(Expression::integer(2)),
}
.into(),
),
}
.into(),
),
right: Box::new(Expression::integer(3)),
}
.into();
let json = expression_to_json(&expr).unwrap();
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_pretty_is_multiline() {
let expr = parse_text("x + y").unwrap();
let pretty = expression_to_json_pretty(&expr).unwrap();
assert!(pretty.contains('\n'), "pretty JSON should be multi-line");
}
#[test]
fn test_json_pretty_contains_same_data() {
let expr = parse_text("x + y").unwrap();
let compact = expression_to_json(&expr).unwrap();
let pretty = expression_to_json_pretty(&expr).unwrap();
let compact_val: serde_json::Value = serde_json::from_str(&compact).unwrap();
let pretty_val: serde_json::Value = serde_json::from_str(&pretty).unwrap();
assert_eq!(compact_val, pretty_val);
}
#[test]
fn test_json_round_trip() {
let expr = parse_text("2 * x + 3").unwrap();
let json = expression_to_json(&expr).unwrap();
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_round_trip_function() {
let expr = parse_text("sin(x)").unwrap();
let json = expression_to_json(&expr).unwrap();
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_round_trip_nested() {
let expr = parse_text("sin(x)^2 + cos(x)^2").unwrap();
let json = expression_to_json(&expr).unwrap();
let restored: Expression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_json_deserialize_invalid_json_returns_error() {
let result: Result<Expression, _> = serde_json::from_str("not valid json");
assert!(result.is_err(), "deserializing invalid JSON should fail");
}
#[test]
fn test_json_deserialize_unknown_variant_returns_error() {
let result: Result<Expression, _> = serde_json::from_str(r#"{"UnknownVariant": 42}"#);
assert!(result.is_err(), "deserializing unknown variant should fail");
}