#[cfg(test)]
mod tests {
use std::f32;
use std::f64;
use std::sync::Arc;
use vortex_buffer::ByteBuffer;
use crate::dtype::DType;
use crate::dtype::DecimalDType;
use crate::dtype::Nullability;
use crate::dtype::PType;
use crate::dtype::i256;
use crate::proto::scalar as pb;
use crate::scalar::DecimalValue;
use crate::scalar::Scalar;
use crate::scalar::tests::SESSION;
#[test]
fn test_primitive_scalar_to_scalar_value_round_trip() {
let values: Vec<Scalar> = vec![
Scalar::primitive(42i8, Nullability::NonNullable),
Scalar::primitive(1000i16, Nullability::NonNullable),
Scalar::primitive(100000i32, Nullability::NonNullable),
Scalar::primitive(10000000000i64, Nullability::NonNullable),
Scalar::primitive(200u8, Nullability::NonNullable),
Scalar::primitive(50000u16, Nullability::NonNullable),
Scalar::primitive(4000000000u32, Nullability::NonNullable),
Scalar::primitive(18446744073709551615u64, Nullability::NonNullable),
Scalar::primitive(f32::consts::PI, Nullability::NonNullable),
Scalar::primitive(f64::consts::E, Nullability::NonNullable),
];
for scalar in values {
let value = scalar.value().cloned();
let dtype = scalar.dtype().clone();
let reconstructed = Scalar::new(dtype, value);
assert_eq!(scalar.dtype(), reconstructed.dtype());
assert_eq!(scalar, reconstructed);
}
}
#[test]
fn test_null_scalar_type_preservation() {
let cases = [
(
Scalar::null_native::<i8>(),
DType::Primitive(PType::I8, Nullability::Nullable),
),
(
Scalar::null_native::<i16>(),
DType::Primitive(PType::I16, Nullability::Nullable),
),
(
Scalar::null_native::<i32>(),
DType::Primitive(PType::I32, Nullability::Nullable),
),
(
Scalar::null_native::<i64>(),
DType::Primitive(PType::I64, Nullability::Nullable),
),
(
Scalar::null_native::<u8>(),
DType::Primitive(PType::U8, Nullability::Nullable),
),
(
Scalar::null_native::<u16>(),
DType::Primitive(PType::U16, Nullability::Nullable),
),
(
Scalar::null_native::<u32>(),
DType::Primitive(PType::U32, Nullability::Nullable),
),
(
Scalar::null_native::<u64>(),
DType::Primitive(PType::U64, Nullability::Nullable),
),
(
Scalar::null_native::<f32>(),
DType::Primitive(PType::F32, Nullability::Nullable),
),
(
Scalar::null_native::<f64>(),
DType::Primitive(PType::F64, Nullability::Nullable),
),
(
Scalar::null_native::<bool>(),
DType::Bool(Nullability::Nullable),
),
(
Scalar::null_native::<String>(),
DType::Utf8(Nullability::Nullable),
),
];
for (scalar, expected_dtype) in cases {
assert!(scalar.is_null());
assert_eq!(scalar.dtype(), &expected_dtype);
let reconstructed = Scalar::new(expected_dtype.clone(), scalar.value().cloned());
assert!(reconstructed.is_null());
assert_eq!(reconstructed.dtype(), &expected_dtype);
}
}
#[test]
fn test_from_try_from_consistency() {
let value_i32 = 42i32;
let scalar_i32 = Scalar::from(value_i32);
let extracted_i32: i32 = i32::try_from(&scalar_i32).unwrap();
assert_eq!(value_i32, extracted_i32);
let value_u64 = 1000000u64;
let scalar_u64 = Scalar::from(value_u64);
let extracted_u64: u64 = u64::try_from(&scalar_u64).unwrap();
assert_eq!(value_u64, extracted_u64);
let value_bool = true;
let scalar_bool = Scalar::from(value_bool);
let extracted_bool: bool = bool::try_from(&scalar_bool).unwrap();
assert_eq!(value_bool, extracted_bool);
let value_str = "test string";
let scalar_str = Scalar::from(value_str);
let extracted_str: String = String::try_from(&scalar_str).unwrap();
assert_eq!(value_str, extracted_str);
}
#[test]
fn test_option_conversions() {
let some_value = Some(42i32);
let scalar_some = Scalar::from(some_value);
let extracted_some: Option<i32> = Option::try_from(&scalar_some).unwrap();
assert_eq!(some_value, extracted_some);
let none_value: Option<i32> = None;
let scalar_none = Scalar::from(none_value);
let extracted_none: Option<i32> = Option::try_from(&scalar_none).unwrap();
assert_eq!(none_value, extracted_none);
}
#[test]
fn test_decimal_scalar_round_trip() {
let decimal_dtype = DecimalDType::new(10, 2);
let decimal_values = vec![
DecimalValue::I8(100),
DecimalValue::I16(10000),
DecimalValue::I32(1000000),
DecimalValue::I64(10000000),
DecimalValue::I128(100000000),
DecimalValue::I256(i256::from_i128(1000000000)),
];
for value in decimal_values {
let scalar = Scalar::decimal(value, decimal_dtype, Nullability::NonNullable);
let decimal_specialized = scalar.as_decimal();
match decimal_specialized.decimal_value() {
Some(extracted) => assert_eq!(extracted, value),
None => panic!("Expected decimal value, got None"),
}
let scalar_value = scalar.value().cloned();
let dtype = scalar.dtype().clone();
let reconstructed = Scalar::new(dtype, scalar_value);
assert_eq!(scalar.dtype(), reconstructed.dtype());
assert_eq!(scalar, reconstructed);
}
}
#[test]
fn test_protobuf_edge_cases() {
let empty_list = Scalar::list(
Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
vec![],
Nullability::NonNullable,
);
let pb_empty = pb::Scalar::from(&empty_list);
let round_tripped = Scalar::from_proto(&pb_empty, &SESSION).unwrap();
assert_eq!(empty_list.dtype(), round_tripped.dtype());
assert_eq!(empty_list, round_tripped);
let inner_dtype = Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable));
let outer_dtype = Arc::new(DType::List(
Arc::clone(&inner_dtype),
Nullability::NonNullable,
));
let inner_list1 = Scalar::list(
inner_dtype,
vec![
Scalar::primitive(1i32, Nullability::NonNullable),
Scalar::primitive(2i32, Nullability::NonNullable),
],
Nullability::NonNullable,
);
let nested_list = Scalar::list(outer_dtype, vec![inner_list1], Nullability::NonNullable);
let pb_nested = pb::Scalar::from(&nested_list);
let round_tripped_nested = Scalar::from_proto(&pb_nested, &SESSION).unwrap();
assert_eq!(nested_list.dtype(), round_tripped_nested.dtype());
assert_eq!(nested_list, round_tripped_nested);
let large_binary = vec![42u8; 10000];
let binary_scalar = Scalar::binary(large_binary.clone(), Nullability::NonNullable);
let pb_binary = pb::Scalar::from(&binary_scalar);
let round_tripped_binary = Scalar::from_proto(&pb_binary, &SESSION).unwrap();
assert_eq!(binary_scalar.dtype(), round_tripped_binary.dtype());
assert_eq!(binary_scalar, round_tripped_binary);
let extracted: ByteBuffer = ByteBuffer::try_from(&round_tripped_binary).unwrap();
assert_eq!(extracted.as_slice(), &large_binary);
}
#[test]
fn test_protobuf_fixed_size_list_round_trip() {
let fsl = Scalar::fixed_size_list(
Arc::new(DType::Primitive(PType::U8, Nullability::NonNullable)),
(0u8..16)
.map(|i| Scalar::primitive(i, Nullability::NonNullable))
.collect(),
Nullability::NonNullable,
);
let pb_fsl = pb::Scalar::from(&fsl);
let round_tripped = Scalar::from_proto(&pb_fsl, &SESSION).unwrap();
assert_eq!(fsl.dtype(), round_tripped.dtype());
assert_eq!(fsl, round_tripped);
}
#[test]
fn test_nullability_preservation() {
let nullable_scalar = Scalar::primitive(42i32, Nullability::Nullable);
let non_nullable_scalar = Scalar::primitive(42i32, Nullability::NonNullable);
assert_ne!(nullable_scalar.dtype(), non_nullable_scalar.dtype());
let pb_nullable = pb::Scalar::from(&nullable_scalar);
let pb_non_nullable = pb::Scalar::from(&non_nullable_scalar);
let recovered_nullable = Scalar::from_proto(&pb_nullable, &SESSION).unwrap();
let recovered_non_nullable = Scalar::from_proto(&pb_non_nullable, &SESSION).unwrap();
assert_eq!(nullable_scalar.dtype(), recovered_nullable.dtype());
assert_eq!(non_nullable_scalar.dtype(), recovered_non_nullable.dtype());
assert_ne!(recovered_nullable.dtype(), recovered_non_nullable.dtype());
assert_eq!(
recovered_nullable.as_primitive().typed_value::<i32>(),
Some(42)
);
assert_eq!(
recovered_non_nullable.as_primitive().typed_value::<i32>(),
Some(42)
);
}
#[test]
fn test_usize_conversions() {
let value_usize = 12345usize;
let scalar_usize = Scalar::from(value_usize);
let extracted_usize: usize = usize::try_from(&scalar_usize).unwrap();
assert_eq!(value_usize, extracted_usize);
}
#[test]
fn test_conversion_errors() {
let string_scalar = Scalar::utf8("not a number".to_string(), Nullability::NonNullable);
let result: Result<i32, _> = i32::try_from(&string_scalar);
assert!(result.is_err());
let int_scalar = Scalar::primitive(42i32, Nullability::NonNullable);
assert!(int_scalar.as_list_opt().is_none());
let bool_scalar = Scalar::bool(true, Nullability::NonNullable);
assert!(bool_scalar.as_decimal_opt().is_none());
}
}