#[macro_export]
macro_rules! assert_series_equal {
($left:expr, $right:expr $(, $options:expr)?) => {
{
#[allow(unused_assignments)]
#[allow(unused_mut)]
let mut options = $crate::asserts::SeriesEqualOptions::default();
$(options = $options;)?
match $crate::asserts::assert_series_equal($left, $right, options) {
Ok(_) => {},
Err(e) => panic!("{}", e),
}
}
};
}
#[cfg(test)]
mod tests {
use polars_core::prelude::*;
#[test]
fn test_series_equal_options() {
let options = crate::asserts::SeriesEqualOptions::default();
assert!(options.check_dtypes);
assert!(options.check_names);
assert!(options.check_order);
assert!(options.check_exact);
assert_eq!(options.rel_tol, 1e-5);
assert_eq!(options.abs_tol, 1e-8);
assert!(!options.categorical_as_str);
}
#[test]
#[should_panic(expected = "length mismatch")]
fn test_series_length_mismatch() {
let s1 = Series::new("".into(), &[1, 2]);
let s2 = Series::new("".into(), &[1, 2, 3]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "name mismatch")]
fn test_series_names_mismatch() {
let s1 = Series::new("s1".into(), &[1, 2, 3]);
let s2 = Series::new("s2".into(), &[1, 2, 3]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_check_names_false() {
let s1 = Series::new("s1".into(), &[1, 2, 3]);
let s2 = Series::new("s2".into(), &[1, 2, 3]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_names(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "dtype mismatch")]
fn test_series_dtype_mismatch() {
let s1 = Series::new("".into(), &[1, 2, 3]);
let s2 = Series::new("".into(), &["1", "2", "3"]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_check_dtypes_false() {
let s1 = Series::new("s1".into(), &[1, 2, 3]);
let s2 = Series::new("s1".into(), &[1.0, 2.0, 3.0]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_dtypes(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_value_mismatch_int() {
let s1 = Series::new("".into(), &[1, 2, 3]);
let s2 = Series::new("".into(), &[2, 3, 4]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_values_match_int() {
let s1 = Series::new("".into(), &[1, 2, 3]);
let s2 = Series::new("".into(), &[1, 2, 3]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_value_mismatch_str() {
let s1 = Series::new("".into(), &["foo", "bar"]);
let s2 = Series::new("".into(), &["moo", "car"]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_values_match_str() {
let s1 = Series::new("".into(), &["foo", "bar"]);
let s2 = Series::new("".into(), &["foo", "bar"]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_values_mismatch_float() {
let s1 = Series::new("".into(), &[1.1, 2.2, 3.3]);
let s2 = Series::new("".into(), &[2.2, 3.3, 4.4]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_values_match_float() {
let s1 = Series::new("".into(), &[1.1, 2.2, 3.3]);
let s2 = Series::new("".into(), &[1.1, 2.2, 3.3]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "values not within tolerance")]
fn test_series_float_exceeded_tol() {
let s1 = Series::new("".into(), &[1.0, 2.2, 3.3]);
let s2 = Series::new("".into(), &[1.00012, 2.200025, 3.300035]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_float_within_tol() {
let s1 = Series::new("".into(), &[1.0, 2.0, 3.0]);
let s2 = Series::new("".into(), &[1.000005, 2.000015, 3.000025]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_float_exact_tolerance_boundary() {
let s1 = Series::new("".into(), &[1.0, 2.0, 3.0]);
let s2 = Series::new("".into(), &[1.0, 2.0 + 1e-5, 3.0]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_float_custom_rel_tol() {
let s1 = Series::new("".into(), &[10.0, 100.0, 1000.0]);
let s2 = Series::new("".into(), &[10.05, 100.1, 1000.2]);
let options = crate::asserts::SeriesEqualOptions::default()
.with_check_exact(false)
.with_rel_tol(0.01);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "values not within tolerance")]
fn test_series_float_custom_abs_tol() {
let s1 = Series::new("".into(), &[0.001, 0.01, 0.1]);
let s2 = Series::new("".into(), &[0.001, 0.02, 0.1]);
let options = crate::asserts::SeriesEqualOptions::default()
.with_check_exact(false)
.with_abs_tol(0.005);
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_empty_equal() {
let s1 = Series::default();
let s2 = Series::default();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_nan_equal() {
let s1 = Series::new("".into(), &[f64::NAN, f64::NAN, f64::NAN]);
let s2 = Series::new("".into(), &[f64::NAN, f64::NAN, f64::NAN]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_null_equal() {
let s1 = Series::new("".into(), &[None::<i32>, None::<i32>, None::<i32>]);
let s2 = Series::new("".into(), &[None::<i32>, None::<i32>, None::<i32>]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_infinity_values_mismatch() {
let s1 = Series::new("".into(), &[1.0, f64::INFINITY, 3.0]);
let s2 = Series::new("".into(), &[1.0, f64::NEG_INFINITY, 3.0]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_infinity_values_match() {
let s1 = Series::new("".into(), &[1.0, f64::INFINITY, f64::NEG_INFINITY]);
let s2 = Series::new("".into(), &[1.0, f64::INFINITY, f64::NEG_INFINITY]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "null value mismatch")]
fn test_series_check_exact_false_null() {
let s1 = Series::new("".into(), &[Some(1.0), None::<f64>, Some(3.0)]);
let s2 = Series::new("".into(), &[Some(1.0), Some(2.0), Some(3.0)]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "nan value mismatch")]
fn test_series_check_exact_false_nan() {
let s1 = Series::new("".into(), &[1.0, f64::NAN, 3.0]);
let s2 = Series::new("".into(), &[1.0, 2.0, 3.0]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_exact(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_sorting_unequal() {
let s1 = Series::new("".into(), &[Some(1), Some(2), Some(3), None::<i32>]);
let s2 = Series::new("".into(), &[Some(2), None::<i32>, Some(3), Some(1)]);
let options = crate::asserts::SeriesEqualOptions::default();
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_sorting_equal() {
let s1 = Series::new("".into(), &[Some(1), Some(2), Some(3), None::<i32>]);
let s2 = Series::new("".into(), &[Some(2), None::<i32>, Some(3), Some(1)]);
let options = crate::asserts::SeriesEqualOptions::default().with_check_order(false);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_categorical_mismatch() {
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let s2 = Series::new("".into(), &["apple", "orange", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_categorical_match() {
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let s2 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_categorical_str_mismatch() {
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let s2 = Series::new("".into(), &["apple", "orange", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let options = crate::asserts::SeriesEqualOptions::default().with_categorical_as_str(true);
assert_series_equal!(&s1, &s2, options);
}
#[test]
fn test_series_categorical_str_match() {
let s1 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let s2 = Series::new("".into(), &["apple", "banana", "cherry"])
.cast(&DataType::from_categories(Categories::global()))
.unwrap();
let options = crate::asserts::SeriesEqualOptions::default().with_categorical_as_str(true);
assert_series_equal!(&s1, &s2, options);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_list_values_int_mismatch() {
let s1 = Series::new(
"".into(),
&[
[1, 2, 3].iter().collect::<Series>(),
[4, 5, 6].iter().collect::<Series>(),
[7, 8, 9].iter().collect::<Series>(),
],
);
let s2 = Series::new(
"".into(),
&[
[0, 2, 3].iter().collect::<Series>(),
[4, 7, 6].iter().collect::<Series>(),
[7, 8, 10].iter().collect::<Series>(),
],
);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_list_values_int_match() {
let s1 = Series::new(
"".into(),
&[
[1, 2, 3].iter().collect::<Series>(),
[4, 5, 6].iter().collect::<Series>(),
[7, 8, 9].iter().collect::<Series>(),
],
);
let s2 = Series::new(
"".into(),
&[
[1, 2, 3].iter().collect::<Series>(),
[4, 5, 6].iter().collect::<Series>(),
[7, 8, 9].iter().collect::<Series>(),
],
);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "nested value mismatch")]
fn test_series_list_values_float_mismatch() {
let s1 = Series::new(
"".into(),
&[
[1.1, 2.0, 3.0].iter().collect::<Series>(),
[4.0, 5.0, 6.0].iter().collect::<Series>(),
[7.0, 8.0, 9.0].iter().collect::<Series>(),
],
);
let s2 = Series::new(
"".into(),
&[
[0.5, 2.0, 3.0].iter().collect::<Series>(),
[4.0, 7.5, 6.0].iter().collect::<Series>(),
[7.0, 8.0, 10.2].iter().collect::<Series>(),
],
);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_list_values_float_match() {
let s1 = Series::new(
"".into(),
&[
[1.1, 2.0, 3.0].iter().collect::<Series>(),
[4.0, 5.0, 6.0].iter().collect::<Series>(),
[7.0, 8.0, 9.0].iter().collect::<Series>(),
],
);
let s2 = Series::new(
"".into(),
&[
[1.1, 2.0, 3.0].iter().collect::<Series>(),
[4.0, 5.0, 6.0].iter().collect::<Series>(),
[7.0, 8.0, 9.0].iter().collect::<Series>(),
],
);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_struct_values_str_mismatch() {
let field1 = Series::new("field1".into(), &["a", "d", "g"]);
let field2 = Series::new("field2".into(), &["b", "e", "h"]);
let s1_fields = [field1.clone(), field2];
let s1_struct =
StructChunked::from_series("".into(), field1.len(), s1_fields.iter()).unwrap();
let s1 = s1_struct.into_series();
let field1_alt = Series::new("field1".into(), &["a", "DIFFERENT", "g"]);
let field2_alt = Series::new("field2".into(), &["b", "e", "h"]);
let s2_fields = [field1_alt.clone(), field2_alt];
let s2_struct =
StructChunked::from_series("".into(), field1_alt.len(), s2_fields.iter()).unwrap();
let s2 = s2_struct.into_series();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_struct_values_str_match() {
let field1 = Series::new("field1".into(), &["a", "d", "g"]);
let field2 = Series::new("field2".into(), &["b", "e", "h"]);
let s1_fields = [field1.clone(), field2.clone()];
let s1_struct =
StructChunked::from_series("".into(), field1.len(), s1_fields.iter()).unwrap();
let s1 = s1_struct.into_series();
let s2_fields = [field1.clone(), field2];
let s2_struct =
StructChunked::from_series("".into(), field1.len(), s2_fields.iter()).unwrap();
let s2 = s2_struct.into_series();
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_struct_values_mixed_mismatch() {
let id = Series::new("id".into(), &[1, 2, 3]);
let value = Series::new("value".into(), &["a", "b", "c"]);
let active = Series::new("active".into(), &[true, false, true]);
let s1_fields = [id.clone(), value.clone(), active.clone()];
let s1_struct = StructChunked::from_series("".into(), id.len(), s1_fields.iter()).unwrap();
let s1 = s1_struct.into_series();
let id_alt = Series::new("id".into(), &[1, 99, 3]);
let s2_fields = [id_alt, value, active];
let s2_struct = StructChunked::from_series("".into(), id.len(), s2_fields.iter()).unwrap();
let s2 = s2_struct.into_series();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_struct_values_mixed_match() {
let id = Series::new("id".into(), &[1, 2, 3]);
let value = Series::new("value".into(), &["a", "b", "c"]);
let active = Series::new("active".into(), &[true, false, true]);
let s1_fields = [id.clone(), value.clone(), active.clone()];
let s1_struct = StructChunked::from_series("".into(), id.len(), s1_fields.iter()).unwrap();
let s1 = s1_struct.into_series();
let s2_fields = [id.clone(), value, active];
let s2_struct = StructChunked::from_series("".into(), id.len(), s2_fields.iter()).unwrap();
let s2 = s2_struct.into_series();
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "nested value mismatch")]
fn test_deeply_nested_list_float_mismatch() {
let inner_list_1 = Series::new("inner".into(), &[1.0, 2.0]);
let outer_list_1 = Series::new("outer".into(), &[inner_list_1]);
let s1 = Series::new("nested".into(), &[outer_list_1]);
let inner_list_2 = Series::new("inner".into(), &[1.0, 3.0]);
let outer_list_2 = Series::new("outer".into(), &[inner_list_2]);
let s2 = Series::new("nested".into(), &[outer_list_2]);
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_deeply_nested_list_float_match() {
let inner_list_1 = Series::new("".into(), &[1.0, 2.0]);
let outer_list_1 = Series::new("".into(), &[inner_list_1]);
let s1 = Series::new("".into(), &[outer_list_1]);
let inner_list_2 = Series::new("".into(), &[1.0, 2.0]);
let outer_list_2 = Series::new("".into(), &[inner_list_2]);
let s2 = Series::new("".into(), &[outer_list_2]);
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_datetime_values_mismatch() {
let dt1: i64 = 1672567200000000000;
let dt2: i64 = 1672653600000000000;
let dt3: i64 = 1672657200000000000;
let s1 = Series::new("".into(), &[dt1, dt2])
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
.unwrap();
let s2 = Series::new("".into(), &[dt1, dt3])
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_datetime_values_match() {
let dt1: i64 = 1672567200000000000;
let dt2: i64 = 1672653600000000000;
let s1 = Series::new("".into(), &[dt1, dt2])
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
.unwrap();
let s2 = Series::new("".into(), &[dt1, dt2])
.cast(&DataType::Datetime(TimeUnit::Nanoseconds, None))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_decimal_values_mismatch() {
let s1 = Series::new("".into(), &[1, 2])
.cast(&DataType::Decimal(10, 2))
.unwrap();
let s2 = Series::new("".into(), &[1, 3])
.cast(&DataType::Decimal(10, 2))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_decimal_values_match() {
let s1 = Series::new("".into(), &[1, 2])
.cast(&DataType::Decimal(10, 2))
.unwrap();
let s2 = Series::new("".into(), &[1, 2])
.cast(&DataType::Decimal(10, 2))
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
#[should_panic(expected = "exact value mismatch")]
fn test_series_binary_values_mismatch() {
let s1 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
.cast(&DataType::Binary)
.unwrap();
let s2 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 7]])
.cast(&DataType::Binary)
.unwrap();
assert_series_equal!(&s1, &s2);
}
#[test]
fn test_series_binary_values_match() {
let s1 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
.cast(&DataType::Binary)
.unwrap();
let s2 = Series::new("".into(), &[vec![1u8, 2, 3], vec![4, 5, 6]])
.cast(&DataType::Binary)
.unwrap();
assert_series_equal!(&s1, &s2);
}
}