1use crate::harness::{CategoryResult, TestContext, TestResult};
7use std::collections::HashSet;
8use std::time::Instant;
9use xlog_core::{ScalarType, Schema};
10
11pub fn run_all(ctx: &TestContext) -> CategoryResult {
13 let mut results = CategoryResult::new("c14_integer");
14 let start = Instant::now();
15
16 results.add_result(test_i64_overflow_boundaries(ctx));
17 results.add_result(test_u64_overflow_boundaries(ctx));
18 results.add_result(test_u32_full_range(ctx));
19 results.add_result(test_i64_signed_comparison(ctx));
20 results.add_result(test_integer_wraparound_keys(ctx));
21
22 results.set_duration(start.elapsed());
23 results
24}
25
26fn test_i64_overflow_boundaries(ctx: &TestContext) -> TestResult {
31 let start = Instant::now();
32 let schema = Schema::new(vec![("val".to_string(), ScalarType::I64)]);
33
34 let data: Vec<i64> = vec![
36 i64::MIN,
37 i64::MAX,
38 0,
39 -1,
40 1,
41 i64::MIN + 1,
42 i64::MAX - 1,
43 -2,
44 2,
45 i64::MIN / 2,
46 i64::MAX / 2,
47 -i64::MAX, 100,
49 -100,
50 1000000,
51 -1000000,
52 ];
53
54 let buffer = match ctx
55 .provider
56 .create_buffer_from_slice::<i64>(&data, schema.clone())
57 {
58 Ok(buf) => buf,
59 Err(e) => {
60 return TestResult::error(
61 "test_i64_overflow_boundaries",
62 start.elapsed(),
63 format!("Failed to create buffer: {}", e),
64 )
65 }
66 };
67
68 let sorted = match ctx.provider.sort(&buffer, &[0]) {
70 Ok(s) => s,
71 Err(e) => {
72 return TestResult::error(
73 "test_i64_overflow_boundaries",
74 start.elapsed(),
75 format!("Sort failed: {}", e),
76 )
77 }
78 };
79
80 let sorted_data = match ctx.provider.download_column::<i64>(&sorted, 0) {
82 Ok(d) => d,
83 Err(e) => {
84 return TestResult::error(
85 "test_i64_overflow_boundaries",
86 start.elapsed(),
87 format!("Failed to download sorted column: {}", e),
88 )
89 }
90 };
91
92 if sorted_data.len() != data.len() {
94 return TestResult::error(
95 "test_i64_overflow_boundaries",
96 start.elapsed(),
97 format!(
98 "Sort returned {} rows, expected {}",
99 sorted_data.len(),
100 data.len()
101 ),
102 );
103 }
104
105 for i in 1..sorted_data.len() {
107 if sorted_data[i] < sorted_data[i - 1] {
108 return TestResult::error(
109 "test_i64_overflow_boundaries",
110 start.elapsed(),
111 format!(
112 "Sort order incorrect at index {}: {} should be >= {}",
113 i,
114 sorted_data[i],
115 sorted_data[i - 1]
116 ),
117 );
118 }
119 }
120
121 if sorted_data[0] != i64::MIN {
123 return TestResult::error(
124 "test_i64_overflow_boundaries",
125 start.elapsed(),
126 format!(
127 "First element should be i64::MIN ({}), got {}",
128 i64::MIN,
129 sorted_data[0]
130 ),
131 );
132 }
133
134 if sorted_data[sorted_data.len() - 1] != i64::MAX {
136 return TestResult::error(
137 "test_i64_overflow_boundaries",
138 start.elapsed(),
139 format!(
140 "Last element should be i64::MAX ({}), got {}",
141 i64::MAX,
142 sorted_data[sorted_data.len() - 1]
143 ),
144 );
145 }
146
147 let original_set: HashSet<i64> = data.iter().copied().collect();
149 let sorted_set: HashSet<i64> = sorted_data.iter().copied().collect();
150 if original_set != sorted_set {
151 return TestResult::error(
152 "test_i64_overflow_boundaries",
153 start.elapsed(),
154 "Some values were lost or changed during sort".to_string(),
155 );
156 }
157
158 let mask: Vec<u8> = data.iter().map(|&v| if v < 0 { 1 } else { 0 }).collect();
160 let filtered = match ctx.provider.filter_by_mask(&buffer, &mask) {
161 Ok(f) => f,
162 Err(e) => {
163 return TestResult::error(
164 "test_i64_overflow_boundaries",
165 start.elapsed(),
166 format!("Filter failed: {}", e),
167 )
168 }
169 };
170
171 let filtered_data = match ctx.provider.download_column::<i64>(&filtered, 0) {
172 Ok(d) => d,
173 Err(e) => {
174 return TestResult::error(
175 "test_i64_overflow_boundaries",
176 start.elapsed(),
177 format!("Failed to download filtered column: {}", e),
178 )
179 }
180 };
181
182 let expected_negative_count = data.iter().filter(|&&v| v < 0).count();
184 if filtered_data.len() != expected_negative_count {
185 return TestResult::error(
186 "test_i64_overflow_boundaries",
187 start.elapsed(),
188 format!(
189 "Filter returned {} rows, expected {}",
190 filtered_data.len(),
191 expected_negative_count
192 ),
193 );
194 }
195
196 if !filtered_data.contains(&i64::MIN) {
198 return TestResult::error(
199 "test_i64_overflow_boundaries",
200 start.elapsed(),
201 "Filtered negative values should include i64::MIN".to_string(),
202 );
203 }
204
205 if let Err(e) = ctx.sync_and_check() {
206 return TestResult::error(
207 "test_i64_overflow_boundaries",
208 start.elapsed(),
209 format!("Sync failed: {}", e),
210 );
211 }
212
213 TestResult::passed("test_i64_overflow_boundaries", start.elapsed())
214}
215
216fn test_u64_overflow_boundaries(ctx: &TestContext) -> TestResult {
220 let start = Instant::now();
221 let schema = Schema::new(vec![("val".to_string(), ScalarType::U64)]);
222
223 let data: Vec<u64> = vec![
225 u64::MIN, u64::MAX,
227 1,
228 u64::MAX - 1,
229 u64::MAX / 2,
230 u64::MAX / 4,
231 u64::MAX / 4 * 3,
232 100,
233 1000,
234 1_000_000,
235 1_000_000_000,
236 1_000_000_000_000,
237 0x8000_0000_0000_0000, 0xFFFF_FFFF_0000_0000, 0x0000_0000_FFFF_FFFF, 0xAAAA_AAAA_AAAA_AAAA, ];
243
244 let buffer = match ctx
245 .provider
246 .create_buffer_from_slice::<u64>(&data, schema.clone())
247 {
248 Ok(buf) => buf,
249 Err(e) => {
250 return TestResult::error(
251 "test_u64_overflow_boundaries",
252 start.elapsed(),
253 format!("Failed to create buffer: {}", e),
254 )
255 }
256 };
257
258 let sorted = match ctx.provider.sort(&buffer, &[0]) {
260 Ok(s) => s,
261 Err(e) => {
262 return TestResult::error(
263 "test_u64_overflow_boundaries",
264 start.elapsed(),
265 format!("Sort failed: {}", e),
266 )
267 }
268 };
269
270 let sorted_data = match ctx.provider.download_column::<u64>(&sorted, 0) {
272 Ok(d) => d,
273 Err(e) => {
274 return TestResult::error(
275 "test_u64_overflow_boundaries",
276 start.elapsed(),
277 format!("Failed to download sorted column: {}", e),
278 )
279 }
280 };
281
282 if sorted_data.len() != data.len() {
284 return TestResult::error(
285 "test_u64_overflow_boundaries",
286 start.elapsed(),
287 format!(
288 "Sort returned {} rows, expected {}",
289 sorted_data.len(),
290 data.len()
291 ),
292 );
293 }
294
295 for i in 1..sorted_data.len() {
297 if sorted_data[i] < sorted_data[i - 1] {
298 return TestResult::error(
299 "test_u64_overflow_boundaries",
300 start.elapsed(),
301 format!(
302 "Sort order incorrect at index {}: {} should be >= {}",
303 i,
304 sorted_data[i],
305 sorted_data[i - 1]
306 ),
307 );
308 }
309 }
310
311 if sorted_data[0] != u64::MIN {
313 return TestResult::error(
314 "test_u64_overflow_boundaries",
315 start.elapsed(),
316 format!("First element should be 0, got {}", sorted_data[0]),
317 );
318 }
319
320 if sorted_data[sorted_data.len() - 1] != u64::MAX {
322 return TestResult::error(
323 "test_u64_overflow_boundaries",
324 start.elapsed(),
325 format!(
326 "Last element should be u64::MAX ({}), got {}",
327 u64::MAX,
328 sorted_data[sorted_data.len() - 1]
329 ),
330 );
331 }
332
333 let original_set: HashSet<u64> = data.iter().copied().collect();
335 let sorted_set: HashSet<u64> = sorted_data.iter().copied().collect();
336 if original_set != sorted_set {
337 return TestResult::error(
338 "test_u64_overflow_boundaries",
339 start.elapsed(),
340 "Some values were lost or changed during sort".to_string(),
341 );
342 }
343
344 let high_bit_value = 0x8000_0000_0000_0000u64;
346 let high_bit_pos = sorted_data.iter().position(|&v| v == high_bit_value);
347 if let Some(pos) = high_bit_pos {
348 for i in 0..pos {
350 if sorted_data[i] >= high_bit_value {
351 return TestResult::error(
352 "test_u64_overflow_boundaries",
353 start.elapsed(),
354 format!(
355 "Value {} at position {} should be less than {} at position {}",
356 sorted_data[i], i, high_bit_value, pos
357 ),
358 );
359 }
360 }
361 }
362
363 if let Err(e) = ctx.sync_and_check() {
364 return TestResult::error(
365 "test_u64_overflow_boundaries",
366 start.elapsed(),
367 format!("Sync failed: {}", e),
368 );
369 }
370
371 TestResult::passed("test_u64_overflow_boundaries", start.elapsed())
372}
373
374fn test_u32_full_range(ctx: &TestContext) -> TestResult {
379 let start = Instant::now();
380 let schema = Schema::new(vec![("val".to_string(), ScalarType::U32)]);
381
382 let mut data: Vec<u32> = vec![
384 u32::MIN, u32::MAX,
386 1,
387 u32::MAX - 1,
388 u32::MAX / 2,
389 u32::MAX / 4,
390 u32::MAX / 4 * 3,
391 0xFF,
393 0xFF00,
394 0xFF_0000,
395 0xFF00_0000,
396 0x8000_0000, 0xFFFF_0000, 0x0000_FFFF, 0xAAAA_AAAA, 0x5555_5555, ];
403
404 for i in 0..16 {
406 data.push((u32::MAX as u64 * i / 16) as u32);
407 }
408
409 let buffer = match ctx
410 .provider
411 .create_buffer_from_slice::<u32>(&data, schema.clone())
412 {
413 Ok(buf) => buf,
414 Err(e) => {
415 return TestResult::error(
416 "test_u32_full_range",
417 start.elapsed(),
418 format!("Failed to create buffer: {}", e),
419 )
420 }
421 };
422
423 let sorted = match ctx.provider.sort(&buffer, &[0]) {
425 Ok(s) => s,
426 Err(e) => {
427 return TestResult::error(
428 "test_u32_full_range",
429 start.elapsed(),
430 format!("Sort failed: {}", e),
431 )
432 }
433 };
434
435 let sorted_data = match ctx.provider.download_column::<u32>(&sorted, 0) {
437 Ok(d) => d,
438 Err(e) => {
439 return TestResult::error(
440 "test_u32_full_range",
441 start.elapsed(),
442 format!("Failed to download sorted column: {}", e),
443 )
444 }
445 };
446
447 if sorted_data.len() != data.len() {
449 return TestResult::error(
450 "test_u32_full_range",
451 start.elapsed(),
452 format!(
453 "Sort returned {} rows, expected {}",
454 sorted_data.len(),
455 data.len()
456 ),
457 );
458 }
459
460 for i in 1..sorted_data.len() {
462 if sorted_data[i] < sorted_data[i - 1] {
463 return TestResult::error(
464 "test_u32_full_range",
465 start.elapsed(),
466 format!(
467 "Sort order incorrect at index {}: {} should be >= {}",
468 i,
469 sorted_data[i],
470 sorted_data[i - 1]
471 ),
472 );
473 }
474 }
475
476 if sorted_data[0] != 0 {
478 return TestResult::error(
479 "test_u32_full_range",
480 start.elapsed(),
481 format!("First element should be 0, got {}", sorted_data[0]),
482 );
483 }
484
485 if sorted_data[sorted_data.len() - 1] != u32::MAX {
487 return TestResult::error(
488 "test_u32_full_range",
489 start.elapsed(),
490 format!(
491 "Last element should be u32::MAX ({}), got {}",
492 u32::MAX,
493 sorted_data[sorted_data.len() - 1]
494 ),
495 );
496 }
497
498 let threshold = u32::MAX / 2;
500 let mask: Vec<u8> = data
501 .iter()
502 .map(|&v| if v >= threshold { 1 } else { 0 })
503 .collect();
504 let filtered = match ctx.provider.filter_by_mask(&buffer, &mask) {
505 Ok(f) => f,
506 Err(e) => {
507 return TestResult::error(
508 "test_u32_full_range",
509 start.elapsed(),
510 format!("Filter failed: {}", e),
511 )
512 }
513 };
514
515 let filtered_data = match ctx.provider.download_column::<u32>(&filtered, 0) {
516 Ok(d) => d,
517 Err(e) => {
518 return TestResult::error(
519 "test_u32_full_range",
520 start.elapsed(),
521 format!("Failed to download filtered column: {}", e),
522 )
523 }
524 };
525
526 for &val in &filtered_data {
528 if val < threshold {
529 return TestResult::error(
530 "test_u32_full_range",
531 start.elapsed(),
532 format!(
533 "Filtered value {} should be >= threshold {}",
534 val, threshold
535 ),
536 );
537 }
538 }
539
540 if let Err(e) = ctx.sync_and_check() {
541 return TestResult::error(
542 "test_u32_full_range",
543 start.elapsed(),
544 format!("Sync failed: {}", e),
545 );
546 }
547
548 TestResult::passed("test_u32_full_range", start.elapsed())
549}
550
551fn test_i64_signed_comparison(ctx: &TestContext) -> TestResult {
556 let start = Instant::now();
557 let schema = Schema::new(vec![("val".to_string(), ScalarType::I64)]);
558
559 let data: Vec<i64> = vec![
562 -1, -2, -1000,
565 -1_000_000,
566 0,
567 1,
568 2,
569 1000,
570 1_000_000,
571 i64::MIN, i64::MAX, ];
574
575 let buffer = match ctx
576 .provider
577 .create_buffer_from_slice::<i64>(&data, schema.clone())
578 {
579 Ok(buf) => buf,
580 Err(e) => {
581 return TestResult::error(
582 "test_i64_signed_comparison",
583 start.elapsed(),
584 format!("Failed to create buffer: {}", e),
585 )
586 }
587 };
588
589 let sorted = match ctx.provider.sort(&buffer, &[0]) {
591 Ok(s) => s,
592 Err(e) => {
593 return TestResult::error(
594 "test_i64_signed_comparison",
595 start.elapsed(),
596 format!("Sort failed: {}", e),
597 )
598 }
599 };
600
601 let sorted_data = match ctx.provider.download_column::<i64>(&sorted, 0) {
603 Ok(d) => d,
604 Err(e) => {
605 return TestResult::error(
606 "test_i64_signed_comparison",
607 start.elapsed(),
608 format!("Failed to download sorted column: {}", e),
609 )
610 }
611 };
612
613 for i in 1..sorted_data.len() {
615 if sorted_data[i] < sorted_data[i - 1] {
616 return TestResult::error(
617 "test_i64_signed_comparison",
618 start.elapsed(),
619 format!(
620 "Signed sort order incorrect at index {}: {} should be >= {}",
621 i,
622 sorted_data[i],
623 sorted_data[i - 1]
624 ),
625 );
626 }
627 }
628
629 let mut expected = data.clone();
632 expected.sort();
633
634 if sorted_data != expected {
635 return TestResult::error(
636 "test_i64_signed_comparison",
637 start.elapsed(),
638 format!(
639 "Sort order doesn't match expected signed order.\nGot: {:?}\nExpected: {:?}",
640 sorted_data, expected
641 ),
642 );
643 }
644
645 let first_non_negative_idx = sorted_data.iter().position(|&v| v >= 0);
647 if let Some(idx) = first_non_negative_idx {
648 for i in 0..idx {
650 if sorted_data[i] >= 0 {
651 return TestResult::error(
652 "test_i64_signed_comparison",
653 start.elapsed(),
654 format!(
655 "Negative values should come before positive: found {} at index {}",
656 sorted_data[i], i
657 ),
658 );
659 }
660 }
661 }
662
663 if sorted_data[0] != i64::MIN {
665 return TestResult::error(
666 "test_i64_signed_comparison",
667 start.elapsed(),
668 format!(
669 "First element should be i64::MIN ({}), got {} - may be treated as unsigned",
670 i64::MIN,
671 sorted_data[0]
672 ),
673 );
674 }
675
676 if sorted_data[sorted_data.len() - 1] != i64::MAX {
678 return TestResult::error(
679 "test_i64_signed_comparison",
680 start.elapsed(),
681 format!(
682 "Last element should be i64::MAX ({}), got {}",
683 i64::MAX,
684 sorted_data[sorted_data.len() - 1]
685 ),
686 );
687 }
688
689 if let Err(e) = ctx.sync_and_check() {
690 return TestResult::error(
691 "test_i64_signed_comparison",
692 start.elapsed(),
693 format!("Sync failed: {}", e),
694 );
695 }
696
697 TestResult::passed("test_i64_signed_comparison", start.elapsed())
698}
699
700fn test_integer_wraparound_keys(ctx: &TestContext) -> TestResult {
705 let start = Instant::now();
706
707 let left_schema = Schema::new(vec![
708 ("key".to_string(), ScalarType::U32),
709 ("lval".to_string(), ScalarType::U32),
710 ]);
711 let right_schema = Schema::new(vec![
712 ("key".to_string(), ScalarType::U32),
713 ("rval".to_string(), ScalarType::U32),
714 ]);
715
716 let left_keys: Vec<u32> = vec![
718 u32::MAX,
719 u32::MAX - 1,
720 u32::MAX - 2,
721 0,
722 1,
723 2,
724 u32::MAX / 2,
725 u32::MAX / 2 + 1,
726 0x8000_0000,
728 0x8000_0001,
729 0xFFFF_FFFF,
730 0xFFFF_FFFE,
731 ];
732 let left_vals: Vec<u32> = left_keys.iter().map(|&k| k.wrapping_add(100)).collect();
733
734 let right_keys: Vec<u32> = vec![u32::MAX, u32::MAX - 1, 0, 1, u32::MAX / 2, 0x8000_0000];
736 let right_vals: Vec<u32> = right_keys.iter().map(|&k| k.wrapping_mul(10)).collect();
737
738 let left_buffer = match ctx
739 .provider
740 .create_buffer_from_u32_columns(&[&left_keys, &left_vals], left_schema.clone())
741 {
742 Ok(buf) => buf,
743 Err(e) => {
744 return TestResult::error(
745 "test_integer_wraparound_keys",
746 start.elapsed(),
747 format!("Failed to create left buffer: {}", e),
748 )
749 }
750 };
751
752 let right_buffer = match ctx
753 .provider
754 .create_buffer_from_u32_columns(&[&right_keys, &right_vals], right_schema.clone())
755 {
756 Ok(buf) => buf,
757 Err(e) => {
758 return TestResult::error(
759 "test_integer_wraparound_keys",
760 start.elapsed(),
761 format!("Failed to create right buffer: {}", e),
762 )
763 }
764 };
765
766 let joined = match ctx
768 .provider
769 .hash_join(&left_buffer, &right_buffer, &[0], &[0])
770 {
771 Ok(j) => j,
772 Err(e) => {
773 return TestResult::error(
774 "test_integer_wraparound_keys",
775 start.elapsed(),
776 format!("Hash join failed: {}", e),
777 )
778 }
779 };
780
781 let right_key_set: HashSet<u32> = right_keys.iter().copied().collect();
783 let expected_matches = left_keys
784 .iter()
785 .filter(|k| right_key_set.contains(k))
786 .count();
787
788 if ctx.device_row_count(&joined) != expected_matches as u64 {
789 return TestResult::error(
790 "test_integer_wraparound_keys",
791 start.elapsed(),
792 format!(
793 "Join returned {} rows, expected {}",
794 ctx.device_row_count(&joined),
795 expected_matches
796 ),
797 );
798 }
799
800 let joined_keys = match ctx.provider.download_column::<u32>(&joined, 0) {
802 Ok(d) => d,
803 Err(e) => {
804 return TestResult::error(
805 "test_integer_wraparound_keys",
806 start.elapsed(),
807 format!("Failed to download joined keys: {}", e),
808 )
809 }
810 };
811
812 let joined_lvals = match ctx.provider.download_column::<u32>(&joined, 1) {
813 Ok(d) => d,
814 Err(e) => {
815 return TestResult::error(
816 "test_integer_wraparound_keys",
817 start.elapsed(),
818 format!("Failed to download joined lvals: {}", e),
819 )
820 }
821 };
822
823 let joined_rvals = match ctx.provider.download_column::<u32>(&joined, 2) {
824 Ok(d) => d,
825 Err(e) => {
826 return TestResult::error(
827 "test_integer_wraparound_keys",
828 start.elapsed(),
829 format!("Failed to download joined rvals: {}", e),
830 )
831 }
832 };
833
834 for i in 0..ctx.device_row_count(&joined) as usize {
836 let key = joined_keys[i];
837 let lval = joined_lvals[i];
838 let rval = joined_rvals[i];
839
840 let expected_lval = key.wrapping_add(100);
842 if lval != expected_lval {
843 return TestResult::error(
844 "test_integer_wraparound_keys",
845 start.elapsed(),
846 format!(
847 "Row {}: lval {} doesn't match expected {} for key {}",
848 i, lval, expected_lval, key
849 ),
850 );
851 }
852
853 let expected_rval = key.wrapping_mul(10);
855 if rval != expected_rval {
856 return TestResult::error(
857 "test_integer_wraparound_keys",
858 start.elapsed(),
859 format!(
860 "Row {}: rval {} doesn't match expected {} for key {}",
861 i, rval, expected_rval, key
862 ),
863 );
864 }
865
866 if !right_key_set.contains(&key) {
868 return TestResult::error(
869 "test_integer_wraparound_keys",
870 start.elapsed(),
871 format!("Row {}: key {} is not in right table", i, key),
872 );
873 }
874 }
875
876 let joined_key_set: HashSet<u32> = joined_keys.iter().copied().collect();
878 let left_key_set: HashSet<u32> = left_keys.iter().copied().collect();
879 for &rkey in &right_keys {
880 if left_key_set.contains(&rkey) && !joined_key_set.contains(&rkey) {
881 return TestResult::error(
882 "test_integer_wraparound_keys",
883 start.elapsed(),
884 format!("Key {} should appear in join result but doesn't", rkey),
885 );
886 }
887 }
888
889 let critical_keys = [u32::MAX, 0, 0x8000_0000];
891 for &ckey in &critical_keys {
892 if left_key_set.contains(&ckey) && right_key_set.contains(&ckey) {
893 if !joined_key_set.contains(&ckey) {
894 return TestResult::error(
895 "test_integer_wraparound_keys",
896 start.elapsed(),
897 format!(
898 "Critical key {} (0x{:08X}) missing from join result",
899 ckey, ckey
900 ),
901 );
902 }
903 }
904 }
905
906 if let Err(e) = ctx.sync_and_check() {
907 return TestResult::error(
908 "test_integer_wraparound_keys",
909 start.elapsed(),
910 format!("Sync failed: {}", e),
911 );
912 }
913
914 TestResult::passed("test_integer_wraparound_keys", start.elapsed())
915}