use super::*;
use quickcheck_macros::quickcheck;
use vitaminc_aead::{Float, TimeUnit};
use vitaminc_normalize::normalize;
use crate::NullOrder;
use OrderTarget as T;
fn term(target: OrderTarget, ty: Option<Logical>, v: Physical<'_>) -> OrderPlaintext {
order(target, ty, v).expect("valid test value")
}
fn bytes(target: OrderTarget, ty: Option<Logical>, v: Physical<'_>) -> Vec<u8> {
term(target, ty, v).bytes().risky_ref().clone()
}
fn int_bytes(target: OrderTarget, i: Int) -> Vec<u8> {
bytes(target, None, Physical::Int(i))
}
fn every_width(v: u8) -> [Int; 10] {
[
Int::I8(v as i8),
Int::U8(v),
Int::I16(v.into()),
Int::U16(v.into()),
Int::I32(v.into()),
Int::U32(v.into()),
Int::I64(v.into()),
Int::U64(v.into()),
Int::I128(v.into()),
Int::U128(v.into()),
]
}
const INT_TARGETS: [(OrderTarget, &str, usize); 8] = [
(T::I16, domain::INT16, 2),
(T::U16, domain::UINT16, 2),
(T::I32, domain::INT32, 4),
(T::U32, domain::UINT32, 4),
(T::I64, domain::INT64, 8),
(T::U64, domain::UINT64, 8),
(T::I128, domain::INT128, 16),
(T::U128, domain::UINT128, 16),
];
#[test]
fn every_integer_target_has_its_own_domain_and_width_whatever_the_value_arrives_as() {
for (target, expected_domain, width) in INT_TARGETS {
let first = term(target, None, Physical::Int(Int::I8(5)));
assert_eq!(first.domain(), expected_domain, "{target:?}");
assert_eq!(first.bytes().risky_ref().len(), width, "{target:?}");
for value in every_width(5) {
assert_eq!(
int_bytes(target, value),
first.bytes().risky_ref().clone(),
"{value:?} as {target:?}"
);
}
}
}
#[test]
fn narrowest_width_bigints_compare_in_one_column() {
let as_i32 = |i: Int| term(T::I32, None, Physical::Int(i));
let (hundred, two_hundred, three_hundred) = (
as_i32(Int::I8(100)),
as_i32(Int::U8(200)),
as_i32(Int::I16(300)),
);
for t in [&hundred, &two_hundred, &three_hundred] {
assert_eq!(t.domain(), domain::INT32);
}
assert!(hundred.bytes().risky_ref() < two_hundred.bytes().risky_ref());
assert!(two_hundred.bytes().risky_ref() < three_hundred.bytes().risky_ref());
}
#[test]
fn an_integer_outside_the_target_is_refused_not_truncated() {
let refused = |target, i| {
assert_eq!(
order(target, None, Physical::Int(i)).err(),
Some(NormalizeError::OutOfRange),
"{i:?} as {target:?}"
);
};
refused(T::I32, Int::I64(i64::from(i32::MAX) + 1));
refused(T::I32, Int::I64(i64::from(i32::MIN) - 1));
refused(T::I32, Int::U32(u32::MAX));
refused(T::U32, Int::I8(-1));
refused(T::U16, Int::U32(65_536));
refused(T::I16, Int::U16(32_768));
refused(T::I64, Int::U64(u64::MAX));
refused(T::I128, Int::U128(u128::MAX));
refused(T::U128, Int::I128(-1));
for (target, i) in [
(T::I32, Int::I64(i32::MAX.into())),
(T::I32, Int::I64(i32::MIN.into())),
(T::U16, Int::U32(65_535)),
(T::I16, Int::I128(-32_768)),
(T::U128, Int::U128(u128::MAX)),
(T::I128, Int::I128(i128::MIN)),
] {
assert!(
order(target, None, Physical::Int(i)).is_ok(),
"{i:?} as {target:?}"
);
}
}
#[test]
fn integers_are_big_endian_with_the_sign_bit_flipped() {
assert_eq!(int_bytes(T::I16, Int::I16(-1)), [0x7f, 0xff]);
assert_eq!(int_bytes(T::I16, Int::I16(0)), [0x80, 0x00]);
assert_eq!(int_bytes(T::I16, Int::I16(i16::MIN)), [0x00, 0x00]);
assert_eq!(int_bytes(T::I16, Int::I16(i16::MAX)), [0xff, 0xff]);
assert_eq!(int_bytes(T::U16, Int::U16(0)), [0x00, 0x00]);
assert_eq!(int_bytes(T::U16, Int::U16(255)), [0x00, 0xff]);
assert_eq!(int_bytes(T::I16, Int::I8(-1)), [0x7f, 0xff]);
assert_eq!(int_bytes(T::U16, Int::U8(255)), [0x00, 0xff]);
assert_eq!(int_bytes(T::I32, Int::I32(-2)), [0x7f, 0xff, 0xff, 0xfe]);
assert_eq!(int_bytes(T::I32, Int::I8(-2)), [0x7f, 0xff, 0xff, 0xfe]);
assert_eq!(int_bytes(T::U32, Int::U32(1)), [0, 0, 0, 1]);
assert_eq!(int_bytes(T::I64, Int::I64(i64::MIN)), [0; 8]);
let mut max = [0xff; 16];
max[0] = 0x7f;
assert_eq!(int_bytes(T::U128, Int::U128(u128::MAX)), [0xff; 16]);
assert_eq!(int_bytes(T::I128, Int::I128(i128::MAX)), [0xff; 16]);
assert_eq!(int_bytes(T::I128, Int::I128(-1)), max);
}
#[test]
fn a_float_is_its_equality_bytes_and_an_f32_widens_exactly() {
let f64_bytes = |f: f64| bytes(T::F64, None, Physical::Float(Float::F64(f)));
let f32_bytes = |f: f32| bytes(T::F64, None, Physical::Float(Float::F32(f)));
assert_eq!(
term(T::F64, None, Physical::Float(Float::F32(1.5))).domain(),
domain::FLOAT64
);
for f in [0.0, -0.0, 1.5, -2.25, f64::NAN, f64::INFINITY, f64::MIN] {
assert_eq!(
f64_bytes(f),
normalize(None, Physical::Float(Float::F64(f)))
.expect("float")
.bytes
.risky_ref()
.clone()
);
}
assert_eq!(f64_bytes(-0.0), f64_bytes(0.0));
assert_eq!(f64_bytes(f64::NAN), f64_bytes(-f64::NAN));
for f in [0.0f32, -0.0, 1.5, -1.5, f32::INFINITY, f32::MIN_POSITIVE] {
assert_eq!(f32_bytes(f), f64_bytes(f64::from(f)), "{f}");
}
assert_eq!(f32_bytes(f32::NAN), f64_bytes(f64::NAN));
}
#[test]
fn logical_types_order_by_their_equality_bytes() {
let cases = [
(T::Date, Logical::Date, Int::I32(19_000), domain::DATE, 4),
(
T::Timestamp,
Logical::Timestamp(TimeUnit::Nanos),
Int::I64(1_700_000_000_123_456_789),
domain::TIMESTAMP,
12,
),
(
T::Decimal,
Logical::Decimal { scale: 2 },
Int::I64(150),
domain::DECIMAL,
14,
),
];
for (target, ty, value, expected_domain, width) in cases {
let ordered = term(target, Some(ty), Physical::Int(value));
let matched = normalize(Some(ty), Physical::Int(value)).expect("term");
assert_eq!(ordered.domain(), expected_domain);
assert_eq!(ordered.bytes().risky_ref().len(), width);
assert_eq!(ordered.bytes().risky_ref(), matched.bytes.risky_ref());
}
let id = [9u8; 16];
let uuid = term(T::Uuid, Some(Logical::Uuid), Physical::Bytes(&id));
assert_eq!(uuid.domain(), domain::UUID);
assert_eq!(uuid.bytes().risky_ref(), &id);
let raw = term(T::Bytes, None, Physical::Bytes(b"abc"));
assert_eq!(raw.domain(), domain::BYTES);
assert_eq!(raw.bytes().risky_ref(), b"\x01abc");
assert_eq!(
bytes(
T::Timestamp,
Some(Logical::Timestamp(TimeUnit::Millis)),
Physical::Int(Int::I64(1_700_000_000_123))
),
bytes(
T::Timestamp,
Some(Logical::Timestamp(TimeUnit::Micros)),
Physical::Int(Int::I64(1_700_000_000_123_000))
)
);
}
#[test]
fn text_folds_accents_and_case_but_equality_does_not() {
let ordered = term(T::Text, None, Physical::Text("École"));
assert_eq!(ordered.domain(), domain::TEXT);
assert_eq!(ordered.bytes().risky_ref(), b"\x01ecole");
assert_eq!(bytes(T::Text, None, Physical::Text("ecole")), b"\x01ecole");
assert_ne!(
normalize(None, Physical::Text("École"))
.expect("text")
.bytes
.risky_ref(),
normalize(None, Physical::Text("ecole"))
.expect("text")
.bytes
.risky_ref()
);
assert_eq!(
order(T::Text, None, Physical::Text("\u{378}")).expect_err("refused"),
NormalizeError::UnassignedCodePoint
);
}
#[test]
fn text_and_bytes_start_with_the_marker_so_no_plaintext_is_empty() {
assert_eq!(VARIABLE_WIDTH_MARKER, 0x01);
assert_eq!(bytes(T::Text, None, Physical::Text("")), [0x01]);
assert_eq!(bytes(T::Bytes, None, Physical::Bytes(&[])), [0x01]);
assert_eq!(bytes(T::Text, None, Physical::Text("a")), [0x01, b'a']);
assert_eq!(
bytes(T::Bytes, None, Physical::Bytes(&[0xAB])),
[0x01, 0xAB]
);
assert_eq!(
text("").expect("empty text encodes").risky_ref(),
&[VARIABLE_WIDTH_MARKER]
);
assert!(bytes(T::Text, None, Physical::Text("")) < bytes(T::Text, None, Physical::Text("a")));
assert!(
bytes(T::Bytes, None, Physical::Bytes(&[])) < bytes(T::Bytes, None, Physical::Bytes(&[0]))
);
assert_eq!(int_bytes(T::U16, Int::U16(1)), [0, 1]);
for target in OrderTarget::ALL {
if let Some(width) = target.width() {
assert!(width >= 1, "{target:?}");
}
}
}
#[test]
fn scalars_with_no_order_are_refused_with_the_reason() {
for target in OrderTarget::ALL {
for v in [Physical::Null, Physical::Bool(true), Physical::Bool(false)] {
assert_eq!(
order(*target, None, v).expect_err("refused"),
NormalizeError::NoDomain,
"{target:?}"
);
}
assert_eq!(
order(*target, Some(Logical::Undefined), Physical::Null).expect_err("refused"),
NormalizeError::NoDomain
);
}
let unknown = Logical::from_wire(200, 1).expect("unknown");
assert_eq!(
order(T::I16, Some(unknown), Physical::Int(Int::I8(1))).expect_err("refused"),
NormalizeError::NoDomain
);
assert_eq!(
order(T::Uuid, Some(Logical::Uuid), Physical::Bytes(&[0; 3])).expect_err("refused"),
NormalizeError::Invalid
);
assert_eq!(
order(T::Date, Some(Logical::Date), Physical::Text("2024-01-01")).expect_err("refused"),
NormalizeError::Invalid
);
assert_eq!(
order(
T::Decimal,
Some(Logical::Decimal { scale: 29 }),
Physical::Int(Int::I8(1))
)
.expect_err("refused"),
NormalizeError::Invalid
);
assert_eq!(
order(T::I16, Some(Logical::Undefined), Physical::Int(Int::I8(1))).expect_err("refused"),
NormalizeError::Invalid
);
}
#[test]
fn a_value_of_another_kind_than_the_target_is_a_mismatch() {
let day = (Some(Logical::Date), Physical::Int(Int::I32(1)));
let one = (None, Physical::Int(Int::I32(1)));
for (target, (ty, v)) in [
(T::I32, (None, Physical::Text("1"))),
(T::I32, (None, Physical::Float(Float::F64(1.0)))),
(T::I32, day),
(T::F64, one),
(T::Date, one),
(T::Decimal, one),
(T::Timestamp, day),
(T::Text, (None, Physical::Bytes(b"a"))),
(T::Bytes, (None, Physical::Text("a"))),
(T::Uuid, (None, Physical::Bytes(&[0; 16]))),
(T::Bytes, (Some(Logical::Uuid), Physical::Bytes(&[0; 16]))),
] {
assert_eq!(
order(target, ty, v).expect_err("refused"),
NormalizeError::Mismatch,
"{ty:?} {v:?} as {target:?}"
);
}
}
#[test]
fn every_domain_is_distinct_and_listed_once() {
let mut all = domain::ALL.to_vec();
all.sort_unstable();
all.dedup();
assert_eq!(all.len(), domain::ALL.len());
assert_eq!(
domain::ALL.len(),
41,
"fifteen plain labels and two policies' thirteen"
);
let by_target: Vec<_> = OrderTarget::ALL.iter().map(|t| t.domain()).collect();
assert_eq!(by_target, domain::ALL[..15]);
let fixed: Vec<_> = OrderTarget::ALL
.iter()
.filter(|t| t.width().is_some())
.collect();
assert_eq!(fixed.len(), 13);
for (nulls, part, listed) in [
(NullOrder::First, "/nulls-first/v1", &domain::ALL[15..28]),
(NullOrder::Last, "/nulls-last/v1", &domain::ALL[28..]),
] {
let by_target: Vec<_> = fixed
.iter()
.map(|t| t.domain_with_nulls(nulls).expect("a fixed-width target"))
.collect();
assert_eq!(by_target, listed, "{nulls:?}");
for (target, label) in fixed.iter().zip(&by_target) {
let plain = target.domain().strip_suffix("/v1").expect("a version");
assert_eq!(
*label,
format!("{plain}{part}"),
"{target:?} {nulls:?}: the label is the plain one with the policy"
);
}
}
assert_eq!(T::Text.domain_with_nulls(NullOrder::First), None);
assert_eq!(T::Bytes.domain_with_nulls(NullOrder::Last), None);
assert_eq!(
domain::ALL,
[
"vitaminc/ore/value/int16/v1",
"vitaminc/ore/value/uint16/v1",
"vitaminc/ore/value/int32/v1",
"vitaminc/ore/value/uint32/v1",
"vitaminc/ore/value/int64/v1",
"vitaminc/ore/value/uint64/v1",
"vitaminc/ore/value/int128/v1",
"vitaminc/ore/value/uint128/v1",
"vitaminc/ore/value/float64/v1",
"vitaminc/ore/value/date/v1",
"vitaminc/ore/value/timestamp-micros/v1",
"vitaminc/ore/value/decimal/v1",
"vitaminc/ore/value/text-folded/unicode-16/v1",
"vitaminc/ore/value/bytes/v1",
"vitaminc/ore/value/uuid/v1",
"vitaminc/ore/value/int16/nulls-first/v1",
"vitaminc/ore/value/uint16/nulls-first/v1",
"vitaminc/ore/value/int32/nulls-first/v1",
"vitaminc/ore/value/uint32/nulls-first/v1",
"vitaminc/ore/value/int64/nulls-first/v1",
"vitaminc/ore/value/uint64/nulls-first/v1",
"vitaminc/ore/value/int128/nulls-first/v1",
"vitaminc/ore/value/uint128/nulls-first/v1",
"vitaminc/ore/value/float64/nulls-first/v1",
"vitaminc/ore/value/date/nulls-first/v1",
"vitaminc/ore/value/timestamp-micros/nulls-first/v1",
"vitaminc/ore/value/decimal/nulls-first/v1",
"vitaminc/ore/value/uuid/nulls-first/v1",
"vitaminc/ore/value/int16/nulls-last/v1",
"vitaminc/ore/value/uint16/nulls-last/v1",
"vitaminc/ore/value/int32/nulls-last/v1",
"vitaminc/ore/value/uint32/nulls-last/v1",
"vitaminc/ore/value/int64/nulls-last/v1",
"vitaminc/ore/value/uint64/nulls-last/v1",
"vitaminc/ore/value/int128/nulls-last/v1",
"vitaminc/ore/value/uint128/nulls-last/v1",
"vitaminc/ore/value/float64/nulls-last/v1",
"vitaminc/ore/value/date/nulls-last/v1",
"vitaminc/ore/value/timestamp-micros/nulls-last/v1",
"vitaminc/ore/value/decimal/nulls-last/v1",
"vitaminc/ore/value/uuid/nulls-last/v1",
],
"every label, in the order `domain::ALL` promises"
);
for d in domain::ALL {
assert!(!vitaminc_normalize::domain::ALL.contains(d), "{d}");
assert!(d.starts_with("vitaminc/ore/value/"), "{d}");
}
}
#[test]
fn the_order_normalizer_is_order_at_its_target() {
let by_sink = Order::new(T::I32)
.normalize(None, Physical::Int(Int::I16(-3)))
.expect("term");
let direct = order(T::I32, None, Physical::Int(Int::I16(-3))).expect("term");
assert_eq!(by_sink.domain(), domain::INT32);
assert_eq!(by_sink.bytes().risky_ref(), direct.bytes().risky_ref());
}
#[quickcheck]
fn integer_byte_order_is_value_order(a: i64, b: i64) -> bool {
use std::cmp::Ordering;
let ordered = |t: OrderTarget, x: Int, y: Int, expected: Ordering| {
(int_bytes(t, x), int_bytes(t, y), expected)
};
let wide = |v: i64| i128::from(v) * 3;
let rows = [
ordered(
T::I16,
Int::I16(a as i16),
Int::I16(b as i16),
(a as i16).cmp(&(b as i16)),
),
ordered(
T::I32,
Int::I32(a as i32),
Int::I32(b as i32),
(a as i32).cmp(&(b as i32)),
),
ordered(T::I64, Int::I64(a), Int::I64(b), a.cmp(&b)),
ordered(
T::I128,
Int::I128(wide(a)),
Int::I128(wide(b)),
wide(a).cmp(&wide(b)),
),
ordered(
T::U16,
Int::U16(a as u16),
Int::U16(b as u16),
(a as u16).cmp(&(b as u16)),
),
ordered(
T::U32,
Int::U32(a as u32),
Int::U32(b as u32),
(a as u32).cmp(&(b as u32)),
),
ordered(
T::U64,
Int::U64(a as u64),
Int::U64(b as u64),
(a as u64).cmp(&(b as u64)),
),
ordered(
T::U128,
Int::U128(a as u128),
Int::U128(b as u128),
(a as u128).cmp(&(b as u128)),
),
];
rows.iter().all(|(x, y, expected)| x.cmp(y) == *expected)
}
#[quickcheck]
fn values_of_any_width_order_as_numbers_in_a_wide_target(a: i8, b: u64) -> bool {
let (x, y) = (
int_bytes(T::I128, Int::I8(a)),
int_bytes(T::I128, Int::U64(b)),
);
x.cmp(&y) == i128::from(a).cmp(&i128::from(b))
}
#[quickcheck]
fn every_8_bit_value_orders_as_a_number_in_an_i16_target(a: i8, b: u8) -> bool {
let (x, y) = (int_bytes(T::I16, Int::I8(a)), int_bytes(T::I16, Int::U8(b)));
x.cmp(&y) == i16::from(a).cmp(&i16::from(b))
&& int_bytes(T::I16, Int::U8(b)) == int_bytes(T::I16, Int::I16(i16::from(b)))
}
#[quickcheck]
fn float_byte_order_is_value_order(a: f64, b: f64) -> bool {
if a.is_nan() || b.is_nan() {
return true;
}
let f64_bytes = |f: f64| bytes(T::F64, None, Physical::Float(Float::F64(f)));
f64_bytes(a).cmp(&f64_bytes(b)) == a.partial_cmp(&b).expect("not NaN")
}
#[quickcheck]
fn every_nan_is_one_term_above_every_number(payload: u32, x: f32) -> bool {
let f32_bytes = |f: f32| bytes(T::F64, None, Physical::Float(Float::F32(f)));
let nan = f32::from_bits(payload | 0x7f80_0000 | (u32::from(payload & 0x7f_ffff == 0)));
let x = if x.is_nan() { 0.0 } else { x };
f32_bytes(nan) == f32_bytes(f32::NAN) && f32_bytes(nan) > f32_bytes(x)
}
#[quickcheck]
fn date_and_timestamp_byte_order_is_value_order(a: i32, b: i32, x: i64, y: i64) -> bool {
let (a, b) = (a % 95_000_000, b % 95_000_000);
let date = |d: i32| bytes(T::Date, Some(Logical::Date), Physical::Int(Int::I32(d)));
let nanos = |n: i64| {
bytes(
T::Timestamp,
Some(Logical::Timestamp(TimeUnit::Nanos)),
Physical::Int(Int::I64(n)),
)
};
date(a).cmp(&date(b)) == a.cmp(&b)
&& nanos(x).cmp(&nanos(y)) == x.div_euclid(1_000).cmp(&y.div_euclid(1_000))
}
mod oracle {
use super::*;
use orderable_bytes::OrderableBytes;
#[quickcheck]
fn integers_match_orderable_bytes(v: i128, w: u128) -> bool {
let ours = [
int_bytes(T::I16, Int::I16(v as i16)),
int_bytes(T::I32, Int::I32(v as i32)),
int_bytes(T::I64, Int::I64(v as i64)),
int_bytes(T::I128, Int::I128(v)),
int_bytes(T::U16, Int::U16(w as u16)),
int_bytes(T::U32, Int::U32(w as u32)),
int_bytes(T::U64, Int::U64(w as u64)),
int_bytes(T::U128, Int::U128(w)),
];
let frozen = [
(v as i16).to_orderable_bytes().to_vec(),
(v as i32).to_orderable_bytes().to_vec(),
(v as i64).to_orderable_bytes().to_vec(),
v.to_orderable_bytes().to_vec(),
(w as u16).to_orderable_bytes().to_vec(),
(w as u32).to_orderable_bytes().to_vec(),
(w as u64).to_orderable_bytes().to_vec(),
w.to_orderable_bytes().to_vec(),
];
ours == frozen
}
#[quickcheck]
fn floats_match_orderable_bytes(a: f32, b: f64) -> bool {
let (a, b) = (
if a.is_nan() { 1.5 } else { a },
if b.is_nan() { 1.5 } else { b },
);
bytes(T::F64, None, Physical::Float(Float::F32(a))) == f64::from(a).to_orderable_bytes()
&& bytes(T::F64, None, Physical::Float(Float::F64(b))) == b.to_orderable_bytes()
}
#[test]
fn the_one_nan_is_orderable_bytes_quiet_nan() {
assert_eq!(
bytes(T::F64, None, Physical::Float(Float::F64(f64::NAN))),
f64::NAN.to_orderable_bytes()
);
}
}
fn fills_exactly(bytes: &Protected<Vec<u8>>, len: usize) -> bool {
Protected::new(len)
.zip_ref(bytes, |len, bytes: &Vec<u8>| {
bytes.len() == len && bytes.capacity() == len
})
.risky_unwrap()
}
mod given_an_integer_at_any_width {
use super::*;
#[test]
fn returns_a_buffer_with_no_spare_capacity() {
let values = [
(Int::I16(i16::MIN), 2),
(Int::U16(u16::MAX), 2),
(Int::I32(-1), 4),
(Int::U32(u32::MAX), 4),
(Int::I64(i64::MIN), 8),
(Int::U64(1), 8),
(Int::I128(i128::MIN), 16),
(Int::U128(u128::MAX), 16),
];
for (value, width) in values {
assert!(fills_exactly(&int(value), width), "{value:?}");
}
}
}
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
fn placed(target: OrderTarget, nulls: NullOrder, ty: Option<Logical>, v: Physical<'_>) -> String {
let p = order_with_nulls(target, nulls, ty, v).expect("valid test value");
assert_eq!(
p.null_order(),
Some(nulls),
"the plaintext carries its policy"
);
assert_eq!(
Some(p.domain()),
target.domain_with_nulls(nulls),
"the plaintext is under its policy's label"
);
hex(p.bytes().risky_ref())
}
const FIXED_TARGETS: [OrderTarget; 13] = [
T::I16,
T::U16,
T::I32,
T::U32,
T::I64,
T::U64,
T::I128,
T::U128,
T::F64,
T::Date,
T::Timestamp,
T::Decimal,
T::Uuid,
];
#[test]
fn every_null_is_the_extreme_code_of_its_targets_width() {
let first = |target| placed(target, NullOrder::First, None, Physical::Null);
let last = |target| placed(target, NullOrder::Last, None, Physical::Null);
let pinned: [(OrderTarget, &str, &str); 13] = [
(T::I16, "0000", "ffff"),
(T::U16, "0000", "ffff"),
(T::I32, "00000000", "ffffffff"),
(T::U32, "00000000", "ffffffff"),
(T::I64, "0000000000000000", "ffffffffffffffff"),
(T::U64, "0000000000000000", "ffffffffffffffff"),
(
T::I128,
"00000000000000000000000000000000",
"ffffffffffffffffffffffffffffffff",
),
(
T::U128,
"00000000000000000000000000000000",
"ffffffffffffffffffffffffffffffff",
),
(T::F64, "0000000000000000", "ffffffffffffffff"),
(T::Date, "00000000", "ffffffff"),
(
T::Timestamp,
"000000000000000000000000",
"ffffffffffffffffffffffff",
),
(
T::Decimal,
"0000000000000000000000000000",
"ffffffffffffffffffffffffffff",
),
(
T::Uuid,
"00000000000000000000000000000000",
"ffffffffffffffffffffffffffffffff",
),
];
for (target, below, above) in pinned {
assert_eq!(first(target), below, "{target:?}: the nulls-first sentinel");
assert_eq!(last(target), above, "{target:?}: the nulls-last sentinel");
}
let listed: Vec<_> = pinned.iter().map(|(target, ..)| *target).collect();
assert_eq!(listed, FIXED_TARGETS, "every fixed-width target is pinned");
}
#[test]
fn nulls_first_moves_every_integer_code_up_by_one() {
use Int::*;
let first = |target, i| placed(target, NullOrder::First, None, Physical::Int(i));
let rows = [
(
T::I16,
I16(i16::MIN),
"0001",
"8001",
"8002",
I16(i16::MAX - 1),
"ffff",
),
(
T::U16,
U16(0),
"0001",
"0001",
"0002",
U16(u16::MAX - 1),
"ffff",
),
(
T::I32,
I32(i32::MIN),
"00000001",
"80000001",
"80000002",
I32(i32::MAX - 1),
"ffffffff",
),
(
T::U32,
U32(0),
"00000001",
"00000001",
"00000002",
U32(u32::MAX - 1),
"ffffffff",
),
(
T::I64,
I64(i64::MIN),
"0000000000000001",
"8000000000000001",
"8000000000000002",
I64(i64::MAX - 1),
"ffffffffffffffff",
),
(
T::U64,
U64(0),
"0000000000000001",
"0000000000000001",
"0000000000000002",
U64(u64::MAX - 1),
"ffffffffffffffff",
),
(
T::I128,
I128(i128::MIN),
"00000000000000000000000000000001",
"80000000000000000000000000000001",
"80000000000000000000000000000002",
I128(i128::MAX - 1),
"ffffffffffffffffffffffffffffffff",
),
(
T::U128,
U128(0),
"00000000000000000000000000000001",
"00000000000000000000000000000001",
"00000000000000000000000000000002",
U128(u128::MAX - 1),
"ffffffffffffffffffffffffffffffff",
),
];
for (target, min, min_code, zero_code, one_code, below_max, below_max_code) in rows {
assert_eq!(first(target, min), min_code, "{target:?}: the minimum");
assert_eq!(first(target, I8(0)), zero_code, "{target:?}: zero");
assert_eq!(first(target, I8(1)), one_code, "{target:?}: one");
assert_eq!(
first(target, below_max),
below_max_code,
"{target:?}: the value below the maximum"
);
}
assert_eq!(first(T::U16, U16(0x00FF)), "0100", "the shift carries");
assert_eq!(
first(T::U32, U32(0x00FF_FFFF)),
"01000000",
"the shift carries across three bytes"
);
assert_eq!(
first(T::I32, I32(-1)),
"80000000",
"minus one carries across every byte to zero's plain code"
);
assert_eq!(
first(T::I64, I64(-1)),
"8000000000000000",
"minus one carries across every byte to zero's plain code"
);
assert_eq!(
first(T::U128, U64(u64::MAX)),
"00000000000000010000000000000000",
"the shift carries across eight bytes of sixteen"
);
assert_eq!(
first(T::I128, I128(-1)),
"80000000000000000000000000000000",
"minus one carries across all sixteen bytes"
);
}
#[test]
fn nulls_last_keeps_every_integer_code_but_the_maximums() {
for (target, _, _) in INT_TARGETS {
for v in [Int::I8(0), Int::I8(1), Int::U8(200), Int::U16(300)] {
assert_eq!(
placed(target, NullOrder::Last, None, Physical::Int(v)),
hex(&int_bytes(target, v)),
"{target:?} {v:?}: the code is the plain space's"
);
}
}
}
#[test]
fn the_maximum_of_an_integer_or_uuid_target_is_refused_under_both_policies() {
use Int::*;
let maxima = [
(T::I16, I16(i16::MAX)),
(T::U16, U16(u16::MAX)),
(T::I32, I32(i32::MAX)),
(T::U32, U32(u32::MAX)),
(T::I64, I64(i64::MAX)),
(T::U64, U64(u64::MAX)),
(T::I128, I128(i128::MAX)),
(T::U128, U128(u128::MAX)),
];
for nulls in [NullOrder::First, NullOrder::Last] {
for (target, max) in maxima {
assert_eq!(
order_with_nulls(target, nulls, None, Physical::Int(max)).err(),
Some(NormalizeError::Reserved),
"{target:?} {nulls:?}: the maximum is the nulls' code"
);
if let Some(wide) = max.to_i128().map(I128) {
assert_eq!(
order_with_nulls(target, nulls, None, Physical::Int(wide)).err(),
Some(NormalizeError::Reserved),
"{target:?} {nulls:?}: the maximum at another width"
);
}
}
assert_eq!(
order_with_nulls(
T::Uuid,
nulls,
Some(Logical::Uuid),
Physical::Bytes(&[0xFF; 16])
)
.err(),
Some(NormalizeError::Reserved),
"{nulls:?}: the maximum uuid is the nulls' code"
);
}
for (target, max) in maxima {
let width = target.width().expect("fixed width");
assert_eq!(
hex(&int_bytes(target, max)),
"ff".repeat(width),
"{target:?}: the plain space is unchanged"
);
}
}
#[test]
fn the_nil_uuid_moves_up_by_one_under_nulls_first_and_stays_under_nulls_last() {
let uuid =
|nulls, b: &[u8; 16]| placed(T::Uuid, nulls, Some(Logical::Uuid), Physical::Bytes(b));
assert_eq!(
uuid(NullOrder::First, &[0; 16]),
"00000000000000000000000000000001",
"the nil uuid is one above the sentinel"
);
let mut below_max = [0xFF; 16];
below_max[15] = 0xFE;
assert_eq!(
uuid(NullOrder::First, &below_max),
"ffffffffffffffffffffffffffffffff",
"the uuid below the maximum takes the highest code"
);
let mut carries = [0x00; 16];
carries[8..].fill(0xFF);
assert_eq!(
uuid(NullOrder::First, &carries),
"00000000000000010000000000000000",
"a uuid's shift carries across bytes"
);
assert_eq!(
uuid(NullOrder::Last, &[0; 16]),
"00000000000000000000000000000000",
"under nulls-last the nil uuid keeps its bytes"
);
assert_eq!(
uuid(NullOrder::Last, &below_max),
"fffffffffffffffffffffffffffffffe",
"under nulls-last a uuid keeps its bytes"
);
}
#[test]
fn float_date_timestamp_and_decimal_codes_never_reach_a_sentinel() {
use vitaminc_aead::plaintext::kinds::{
MAX_DATE_DAYS, MAX_TIMESTAMP_NANOS, MIN_DATE_DAYS, MIN_TIMESTAMP_NANOS,
};
let mantissa = (1i128 << 96) - 1;
let ns = Some(Logical::Timestamp(TimeUnit::Nanos));
let ends: [(OrderTarget, Option<Logical>, Vec<Physical<'_>>); 4] = [
(
T::F64,
None,
vec![
Physical::Float(Float::F64(f64::NEG_INFINITY)),
Physical::Float(Float::F64(f64::INFINITY)),
Physical::Float(Float::F64(f64::NAN)),
Physical::Float(Float::F64(-f64::NAN)),
Physical::Float(Float::F64(f64::from_bits(u64::MAX))),
Physical::Float(Float::F64(f64::from_bits(0x7FFF_FFFF_FFFF_FFFF))),
Physical::Float(Float::F32(f32::from_bits(u32::MAX))),
Physical::Float(Float::F32(f32::from_bits(0x7FFF_FFFF))),
],
),
(
T::Date,
Some(Logical::Date),
vec![
Physical::Int(Int::I32(MIN_DATE_DAYS)),
Physical::Int(Int::I32(MAX_DATE_DAYS)),
],
),
(
T::Timestamp,
ns,
vec![
Physical::Int(Int::I128(MIN_TIMESTAMP_NANOS)),
Physical::Int(Int::I128(MAX_TIMESTAMP_NANOS)),
],
),
(
T::Decimal,
Some(Logical::Decimal { scale: 0 }),
vec![
Physical::Int(Int::I128(-mantissa)),
Physical::Int(Int::I128(mantissa)),
Physical::Int(Int::I128(0)),
],
),
];
for (target, ty, values) in ends {
let width = target.width().expect("fixed width");
let (floor, ceiling) = (vec![0x00; width], vec![0xFF; width]);
for v in values {
let plain = bytes(target, ty, v);
assert!(
floor < plain && plain < ceiling,
"{target:?} {v:?}: a code reached an extreme"
);
for nulls in [NullOrder::First, NullOrder::Last] {
assert_eq!(
placed(target, nulls, ty, v),
hex(&plain),
"{target:?} {v:?} {nulls:?}: a policy moves nothing here"
);
}
}
}
for m in [-1i128, 1] {
let v = Physical::Int(Int::I128(m));
let code = bytes(T::Decimal, Some(Logical::Decimal { scale: 28 }), v);
assert!(code > vec![0; 14] && code < vec![0xFF; 14], "{m}e-28");
}
}
#[quickcheck]
fn no_float_reaches_a_sentinel(bits: u64) -> bool {
let code = bytes(
T::F64,
None,
Physical::Float(Float::F64(f64::from_bits(bits))),
);
code != [0x00; 8] && code != [0xFF; 8]
}
#[quickcheck]
fn no_nan_reaches_a_sentinel(bits: u64) -> bool {
let nan_or_infinite = f64::from_bits(bits | 0x7FF0_0000_0000_0000);
let code = bytes(T::F64, None, Physical::Float(Float::F64(nan_or_infinite)));
code != [0x00; 8] && code != [0xFF; 8]
}
#[quickcheck]
fn a_policy_keeps_the_order_and_puts_the_null_at_its_end(a: i64, b: i64) -> bool {
let (a, b) = (a.min(i64::MAX - 1), b.min(i64::MAX - 1));
let code = |nulls, v: i64| placed(T::I64, nulls, None, Physical::Int(Int::I64(v)));
let null = |nulls| placed(T::I64, nulls, None, Physical::Null);
let first = code(NullOrder::First, a).cmp(&code(NullOrder::First, b)) == a.cmp(&b)
&& null(NullOrder::First) < code(NullOrder::First, a);
let last = code(NullOrder::Last, a).cmp(&code(NullOrder::Last, b)) == a.cmp(&b)
&& code(NullOrder::Last, a) < null(NullOrder::Last);
first && last
}
#[test]
fn text_and_bytes_take_no_null_policy() {
for nulls in [NullOrder::First, NullOrder::Last] {
for (target, v) in [
(T::Text, Physical::Text("a")),
(T::Text, Physical::Null),
(T::Bytes, Physical::Bytes(b"a")),
(T::Bytes, Physical::Null),
] {
assert_eq!(
order_with_nulls(target, nulls, None, v).err(),
Some(NormalizeError::NoDomain),
"{target:?} {nulls:?}: no label, so no term"
);
}
assert_eq!(
Order::new(T::Text).nulls(nulls).err(),
Some(NormalizeError::NoDomain),
"the normalizer refuses a policy on text"
);
assert_eq!(
Order::new(T::Bytes).nulls(nulls).err(),
Some(NormalizeError::NoDomain)
);
}
}
#[test]
fn a_boolean_and_an_undefined_have_no_term_under_any_policy() {
for target in FIXED_TARGETS {
for nulls in [NullOrder::First, NullOrder::Last] {
assert_eq!(
order_with_nulls(target, nulls, None, Physical::Bool(true)).err(),
Some(NormalizeError::NoDomain),
"{target:?} {nulls:?}: a boolean"
);
assert_eq!(
order_with_nulls(target, nulls, Some(Logical::Undefined), Physical::Null).err(),
Some(NormalizeError::NoDomain),
"{target:?} {nulls:?}: an undefined is not a null"
);
}
}
}
#[test]
fn the_order_normalizer_with_a_policy_gives_every_null_the_sentinel_at_its_path() {
use vitaminc_normalize::{terms_by, Step};
let ages = vec![Some(36i32), None];
let plain = terms_by(Order::new(T::I32), &ages).expect("terms");
assert_eq!(plain.len(), 1, "a plain normalizer skips the null");
let order = Order::new(T::I32)
.nulls(NullOrder::First)
.expect("fixed width");
assert_eq!(order.target(), T::I32);
assert_eq!(order.null_order(), Some(NullOrder::First));
assert_eq!(Order::new(T::I32).null_order(), None);
let placed = terms_by(order, &ages).expect("terms");
assert_eq!(placed.len(), 2, "the null has a term");
assert_eq!(placed[1].path.steps(), [Step::Index(1)]);
assert_eq!(placed[1].normalized.domain(), domain::INT32_NULLS_FIRST);
assert_eq!(placed[1].normalized.bytes().risky_ref(), &[0; 4]);
assert_eq!(
hex(placed[0].normalized.bytes().risky_ref()),
"80000025",
"36 moves up by one"
);
}