Skip to content

Commit e43dbbf

Browse files
committed
add tests
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent b55d26a commit e43dbbf

File tree

2 files changed

+228
-8
lines changed

2 files changed

+228
-8
lines changed

vortex-vector/src/primitive/generic_mut.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use crate::{PVector, VectorMutOps, VectorOps};
1515
/// `T` is expected to be bound by [`NativePType`], which templates an internal [`BufferMut<T>`]
1616
/// that stores the elements of the vector.
1717
///
18-
/// `PVectorMut<T>` is the primary way to construct primitive vectors. It provides efficient methods
19-
/// for building vectors incrementally before converting them to an immutable [`PVector<T>`] using
20-
/// the [`freeze`](crate::VectorMutOps::freeze) method.
18+
/// [`PVectorMut<T>`] is the primary way to construct primitive vectors. It provides efficient
19+
/// methods for building vectors incrementally before converting them to an immutable [`PVector<T>`]
20+
/// using the [`freeze`](crate::VectorMutOps::freeze) method.
2121
///
2222
/// # Examples
2323
///

vortex-vector/src/primitive/pvector_impl.rs

Lines changed: 225 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,17 @@ impl<T: NativePType> PVectorMut<T> {
4545
})
4646
}
4747

48-
/// Gets a nullable element at the given index.
48+
/// Gets a nullable element at the given index, **WITHOUT** bounds checking.
4949
///
5050
/// If the element at the given index is null, returns `None`. Otherwise, returns `Some(x)`,
5151
/// where `x: T`.
5252
///
53+
/// Note that this `get` method is different from the standard library [`slice::get`], which
54+
/// returns `None` if the index is out of bounds. This method will panic if the index is out of
55+
/// bounds, and return `None` if the elements is null.
56+
///
57+
/// If you want bounds checking, use [`get_checked()`](Self::get_checked) instead.
58+
///
5359
/// # Panics
5460
///
5561
/// Panics if the index is out of bounds.
@@ -130,23 +136,23 @@ impl<T: NativePType> PVectorMut<T> {
130136
/// Returns an immutable slice over the internal mutable buffer with elements of type `T`.
131137
///
132138
/// Note that this slice may contain garbage data where the [`validity()`] mask from the frozen
133-
/// [`PVector`] type states that an element is invalid.
139+
/// [`PVector`](crate::PVector) type states that an element is invalid.
134140
///
135141
/// The caller should check the frozen [`validity()`] before performing any operations.
136142
///
137-
/// [`validity()`]: PVector::validity
143+
/// [`validity()`]: crate::PVector::validity
138144
pub fn as_slice(&self) -> &[T] {
139145
self.elements.as_slice()
140146
}
141147

142148
/// Returns a mutable slice over the internal mutable buffer with elements of type `T`.
143149
///
144150
/// Note that this slice may contain garbage data where the [`validity()`] mask from the frozen
145-
/// [`PVector`] type states that an element is invalid.
151+
/// [`PVector`](crate::PVector) type states that an element is invalid.
146152
///
147153
/// The caller should check the frozen [`validity()`] before performing any operations.
148154
///
149-
/// [`validity()`]: PVector::validity
155+
/// [`validity()`]: crate::PVector::validity
150156
pub fn as_mut_slice(&mut self) -> &mut [T] {
151157
self.elements.as_mut_slice()
152158
}
@@ -287,3 +293,217 @@ impl<T: NativePType> FromIterator<T> for PVectorMut<T> {
287293
vec
288294
}
289295
}
296+
297+
#[cfg(test)]
298+
mod tests {
299+
use super::*;
300+
use crate::VectorOps;
301+
302+
#[test]
303+
fn test_first_last() {
304+
let empty = PVectorMut::<i32>::with_capacity(0);
305+
assert_eq!(empty.first(), None);
306+
assert_eq!(empty.last(), None);
307+
308+
let single = PVectorMut::from_iter([Some(42)]);
309+
assert_eq!(single.first(), Some(Some(42)));
310+
assert_eq!(single.last(), Some(Some(42)));
311+
312+
let mixed = PVectorMut::from_iter([Some(1), None, Some(3), None, Some(5)]);
313+
assert_eq!(mixed.first(), Some(Some(1)));
314+
assert_eq!(mixed.last(), Some(Some(5)));
315+
316+
let null_first = PVectorMut::from_iter([None, Some(2i32)]);
317+
assert_eq!(null_first.first(), Some(None));
318+
assert_eq!(null_first.last(), Some(Some(2)));
319+
}
320+
321+
#[test]
322+
fn test_get_methods() {
323+
let vec = PVectorMut::from_iter([Some(1), None, Some(3), None, Some(5)]);
324+
325+
// Test get_checked - bounds and nulls.
326+
assert_eq!(vec.get_checked(0), Some(Some(1)));
327+
assert_eq!(vec.get_checked(1), Some(None));
328+
assert_eq!(vec.get_checked(5), None); // Out of bounds.
329+
330+
// Test get - nulls.
331+
assert_eq!(vec.get(0), Some(1));
332+
assert_eq!(vec.get(1), None);
333+
assert_eq!(vec.get(2), Some(3));
334+
335+
// Test get_unchecked.
336+
unsafe {
337+
assert_eq!(vec.get_unchecked(0), 1);
338+
assert_eq!(vec.get_unchecked(2), 3);
339+
// Note: get_unchecked(1) would return default value but is safe.
340+
}
341+
342+
// Also test PVector methods.
343+
let frozen = vec.freeze();
344+
assert_eq!(frozen.get_checked(0), Some(Some(1)));
345+
assert_eq!(frozen.get(1), None);
346+
unsafe {
347+
assert_eq!(frozen.get_unchecked(2), 3);
348+
}
349+
}
350+
351+
#[test]
352+
#[should_panic(expected = "index out of bounds")]
353+
fn test_get_panic() {
354+
let vec = PVectorMut::from_iter([Some(1), Some(2)]);
355+
let _ = vec.get(10);
356+
}
357+
358+
#[test]
359+
fn test_push_variants() {
360+
let mut vec = PVectorMut::<i32>::with_capacity(10);
361+
vec.push(1);
362+
vec.push_opt(None);
363+
vec.push_opt(Some(3));
364+
365+
assert_eq!(vec.len(), 3);
366+
assert_eq!(vec.get(0), Some(1));
367+
assert_eq!(vec.get(1), None);
368+
assert_eq!(vec.get(2), Some(3));
369+
370+
// Test push_unchecked with pre-reserved capacity.
371+
vec.reserve(1);
372+
unsafe {
373+
vec.push_unchecked(4);
374+
}
375+
assert_eq!(vec.get(3), Some(4));
376+
}
377+
378+
#[test]
379+
fn test_resize_operations() {
380+
let mut vec = PVectorMut::from_iter([1i32, 2, 3]);
381+
382+
// Grow with valid values.
383+
vec.resize(5, Some(99));
384+
assert_eq!(vec.len(), 5);
385+
assert_eq!(vec.get(3), Some(99));
386+
assert_eq!(vec.get(4), Some(99));
387+
388+
// Grow with nulls.
389+
vec.resize(7, None);
390+
assert_eq!(vec.get(5), None);
391+
assert_eq!(vec.get(6), None);
392+
393+
// Shrink.
394+
vec.resize(2, Some(0));
395+
assert_eq!(vec.len(), 2);
396+
assert_eq!(vec.get(0), Some(1));
397+
assert_eq!(vec.get(1), Some(2));
398+
}
399+
400+
#[test]
401+
fn test_clear_truncate() {
402+
let mut vec = PVectorMut::from_iter([Some(1), None, Some(3), None, Some(5)]);
403+
let cap = vec.capacity();
404+
405+
vec.truncate(3);
406+
assert_eq!(vec.len(), 3);
407+
assert_eq!(vec.last(), Some(Some(3)));
408+
assert!(vec.capacity() >= cap); // Capacity preserved.
409+
410+
vec.truncate(10); // Truncate beyond length - no-op.
411+
assert_eq!(vec.len(), 3);
412+
413+
vec.clear();
414+
assert_eq!(vec.len(), 0);
415+
assert!(vec.capacity() >= cap); // Capacity still preserved.
416+
assert_eq!(vec.first(), None);
417+
}
418+
419+
#[test]
420+
fn test_slice_access() {
421+
let mut vec = PVectorMut::from_iter([Some(1i32), None, Some(3)]);
422+
let slice = vec.as_slice();
423+
assert_eq!(slice[0], 1);
424+
assert_eq!(slice[2], 3);
425+
// slice[1] is undefined for null but safe to access.
426+
427+
let mut_slice = vec.as_mut_slice();
428+
mut_slice[0] = 10;
429+
assert_eq!(vec.get(0), Some(10));
430+
431+
let frozen = vec.freeze();
432+
assert_eq!(frozen.as_slice()[0], 10);
433+
}
434+
435+
#[test]
436+
fn test_from_iter_variants() {
437+
// FromIterator<T> - all non-null.
438+
let vec1 = PVectorMut::from_iter([1i32, 2, 3]);
439+
assert_eq!(vec1.len(), 3);
440+
assert!(vec1.freeze().validity().all_true());
441+
442+
// FromIterator<Option<T>> - mixed null/non-null.
443+
let vec2 = PVectorMut::from_iter([Some(1i32), None, Some(3)]);
444+
assert_eq!(vec2.len(), 3);
445+
assert_eq!(vec2.freeze().validity().true_count(), 2);
446+
447+
// Empty iterators.
448+
let empty1 = PVectorMut::from_iter::<[i32; 0]>([]);
449+
let empty2 = PVectorMut::<i32>::from_iter(std::iter::empty::<Option<i32>>());
450+
assert_eq!(empty1.len(), 0);
451+
assert_eq!(empty2.len(), 0);
452+
}
453+
454+
#[test]
455+
fn test_extend_operations() {
456+
let mut vec = PVectorMut::from_iter([1i32, 2]);
457+
458+
// Extend<T> - all non-null.
459+
vec.extend([3, 4]);
460+
assert_eq!(vec.len(), 4);
461+
assert_eq!(vec.get(3), Some(4));
462+
463+
// Extend<Option<T>> - mixed null/non-null.
464+
vec.extend([Some(5), None, Some(7)]);
465+
assert_eq!(vec.len(), 7);
466+
assert_eq!(vec.get(5), None);
467+
assert_eq!(vec.get(6), Some(7));
468+
469+
// Extend with iterator that has size hint.
470+
let iter = 8..10;
471+
vec.extend(iter);
472+
assert_eq!(vec.get(8), Some(9));
473+
}
474+
475+
#[test]
476+
fn test_empty_vector_edge_cases() {
477+
let empty = PVectorMut::<i32>::with_capacity(0);
478+
assert_eq!(empty.len(), 0);
479+
assert_eq!(empty.first(), None);
480+
assert_eq!(empty.last(), None);
481+
assert_eq!(empty.get_checked(0), None);
482+
assert_eq!(empty.as_slice().len(), 0);
483+
484+
let mut mutable_empty = PVectorMut::<i32>::with_capacity(0);
485+
mutable_empty.clear(); // No-op on empty.
486+
mutable_empty.truncate(0); // No-op.
487+
mutable_empty.resize(0, None); // No-op.
488+
assert_eq!(mutable_empty.len(), 0);
489+
}
490+
491+
#[test]
492+
fn test_complex_workflow() {
493+
// Integration test combining multiple operations.
494+
let mut vec = PVectorMut::<i32>::with_capacity(2);
495+
vec.extend([1, 2]); // Extend<T>.
496+
vec.push_opt(None);
497+
vec.resize(5, Some(99));
498+
vec.truncate(4);
499+
vec.extend([Some(10), None]); // Extend<Option<T>>.
500+
501+
assert_eq!(vec.len(), 6);
502+
let frozen = vec.freeze();
503+
assert_eq!(frozen.validity().true_count(), 4);
504+
assert_eq!(frozen.get(0), Some(1));
505+
assert_eq!(frozen.get(2), None);
506+
assert_eq!(frozen.get(3), Some(99));
507+
assert_eq!(frozen.get(5), None);
508+
}
509+
}

0 commit comments

Comments
 (0)