clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! CPU feature detection and fallback tests
//!
//! Tests CPU feature detection, fallback mechanisms, and target architecture handling.

#[cfg(test)]
mod cpu_feature_tests {
    extern crate alloc;
    use crate::simd::dispatch::*;
    use alloc::vec::Vec;

    #[test]
    fn test_cpu_feature_detection_consistency() {
        // Debug AVX detection
        // Force display of AVX detection info by panicking
        let avx2_available = is_avx2_available();
        let has_avx = crate::cpuid::has_avx();
        let has_avx2 = crate::cpuid::has_avx2();
        let is_avx_available = is_avx_available();
        let is_os_avx_supported = is_os_avx_supported();

        let cpuid1 = crate::cpuid::cpuid(1, 0);
        let cpuid7 = crate::cpuid::cpuid(7, 0);

        // Print AVX detection info to stderr
        eprintln!("=== AVX Detection Info ===");
        eprintln!("AVX2 available: {}", avx2_available);
        eprintln!("has_avx(): {}", has_avx);
        eprintln!("has_avx2(): {}", has_avx2);
        eprintln!("is_avx_available(): {}", is_avx_available);
        eprintln!("is_os_avx_supported(): {}", is_os_avx_supported);
        eprintln!("CPUID(1).ECX: 0x{:08X} ({:032b})", cpuid1.ecx, cpuid1.ecx);
        eprintln!("CPUID(7).EBX: 0x{:08X} ({:032b})", cpuid7.ebx, cpuid7.ebx);
        eprintln!("AVX bit (ECX[28]): {}", (cpuid1.ecx & (1 << 28)) != 0);
        eprintln!("AVX2 bit (EBX[5]): {}", (cpuid7.ebx & (1 << 5)) != 0);

        // Test that CPU feature detection gives consistent results
        let avx2_1 = is_avx2_available();
        let avx2_2 = is_avx2_available();
        let avx2_3 = is_avx2_available();

        assert_eq!(avx2_1, avx2_2);
        assert_eq!(avx2_2, avx2_3);

        let avx512_1 = is_avx512_available();
        let avx512_2 = is_avx512_available();
        let avx512_3 = is_avx512_available();

        assert_eq!(avx512_1, avx512_2);
        assert_eq!(avx512_2, avx512_3);

        // CPU feature detection results:
        // AVX2 available: {}
        // AVX-512 available: {}
        // (Print statements removed for no_std compatibility)

        // Debug output
        println!("=== AVX Detection Results ===");
        println!("AVX2 available: {}", avx2_1);
        println!("AVX-512 available: {}", avx512_1);

        let cpuid1 = crate::cpuid::cpuid(1, 0);
        let cpuid7 = crate::cpuid::cpuid(7, 0);
        println!("CPUID(1).ECX: 0x{:08X}", cpuid1.ecx);
        println!("CPUID(7).EBX: 0x{:08X}", cpuid7.ebx);
        println!("AVX bit set: {}", (cpuid1.ecx & (1 << 28)) != 0);
        println!("AVX2 bit set: {}", (cpuid7.ebx & (1 << 5)) != 0);
    }

    #[test]
    fn test_cpu_feature_detection_no_panics() {
        // Test that CPU feature detection functions don't panic
        // even if called many times rapidly
        for _ in 0..100 {
            let _avx2 = is_avx2_available();
            let _avx512 = is_avx512_available();
        }
    }

    #[test]
    fn test_fallback_mechanism() {
        // Test that the main SIMD function always works regardless of CPU features
        let mut data = [0x123456789ABCDEF0u64; 16];
        let original = data;

        // Should not panic and should modify data
        clock_mix_avx2(&mut data);
        assert_ne!(data, original);

        // Should match scalar result
        let mut scalar_data = original;
        crate::simd::scalar::scalar_clock_mix(&mut scalar_data);
        assert_eq!(data, scalar_data);
    }

    #[test]
    fn test_simd_dispatch_logic() {
        // Test that SIMD dispatch chooses appropriate implementation

        // Create test data
        let mut data1 = [0xFEDCBA9876543210u64; 16];
        let mut data2 = [0xFEDCBA9876543210u64; 16];
        let mut data3 = [0xFEDCBA9876543210u64; 16];

        // All should produce the same result
        crate::simd::scalar::scalar_clock_mix(&mut data1);
        clock_mix_avx2(&mut data2);

        // Force scalar path by calling directly
        #[cfg(not(feature = "simd"))]
        {
            crate::simd::scalar::scalar_clock_mix(&mut data3);
        }
        #[cfg(feature = "simd")]
        {
            // With SIMD feature enabled, it should dispatch appropriately
            clock_mix_avx2(&mut data3);
        }

        assert_eq!(data1, data2);
        assert_eq!(data2, data3);
    }

    #[test]
    fn test_feature_availability_logic() {
        // Test logical consistency of feature availability
        let avx2_available = is_avx2_available();
        let avx512_available = is_avx512_available();

        // AVX-512 requires AVX2, so if AVX-512 is available, AVX2 should be too
        if avx512_available {
            assert!(
                avx2_available,
                "AVX-512 availability implies AVX2 availability"
            );
        }

        // Test doesn't imply anything about AVX2 if AVX-512 is not available
    }

    #[test]
    fn test_cpu_feature_independence() {
        // Test that operations work correctly regardless of CPU features
        let test_data = Vec::from([
            [0u64; 16],
            [u64::MAX; 16],
            [1u64; 16],
            [0xAAAAAAAAAAAAAAAAu64; 16],
            [0x5555555555555555u64; 16],
        ]);

        for (i, data) in test_data.into_iter().enumerate() {
            let mut simd_result = data;
            let mut scalar_result = data;

            clock_mix_avx2(&mut simd_result);
            crate::simd::scalar::scalar_clock_mix(&mut scalar_result);

            assert_eq!(
                simd_result, scalar_result,
                "CPU feature independence test failed for pattern {}",
                i
            );
        }
    }

    #[test]
    fn test_target_architecture_handling() {
        // Test that the code compiles and works on different target architectures
        let mut data = [0xDEADBEEFu64; 16];

        // Should work on all supported architectures
        clock_mix_avx2(&mut data);

        // Verify the result is not all zeros (unless input was such that it produces zeros)
        let is_all_zeros = data.iter().all(|&x| x == 0);
        let input_all_zeros = [0xDEADBEEFu64; 16].iter().all(|&x| x == 0);
        if !input_all_zeros {
            assert!(
                !is_all_zeros,
                "Output should not be all zeros for non-zero input"
            );
        }

        // Should match scalar
        let mut scalar_data = [0xDEADBEEFu64; 16];
        crate::simd::scalar::scalar_clock_mix(&mut scalar_data);
        assert_eq!(data, scalar_data);
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_compile_time_feature_gates() {
        // Test that compile-time feature gates work correctly
        let mut data = [0xCAFEBABEu64; 16];

        // clock_mix_avx2 should always be available
        clock_mix_avx2(&mut data);

        // Test that the function signature is correct
        assert_eq!(data.len(), 16);

        // Test with different feature combinations
        #[cfg(feature = "simd")]
        {
            // With SIMD feature, should use SIMD path
            // (Print statement removed for no_std compatibility)
        }

        #[cfg(not(feature = "simd"))]
        {
            // Without SIMD feature, should use scalar path
            // (Print statement removed for no_std compatibility)
        }
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_avx2_target_feature_safety() {
        // Test that AVX2 target features are used safely
        if !is_avx2_available() {
            // AVX2 not available, skipping AVX2 safety test
            // (Print statement removed for no_std compatibility)
            return;
        }

        let mut data = [0x123456789ABCDEF0u64; 16];
        let original = data;

        // This should not panic on systems with AVX2
        clock_mix_avx2(&mut data);
        assert_ne!(data, original);

        // Should match scalar
        let mut scalar_data = original;
        crate::simd::scalar::scalar_clock_mix(&mut scalar_data);
        assert_eq!(data, scalar_data);
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_avx512_target_feature_safety() {
        // Test that AVX-512 target features are used safely
        if !is_avx512_available() {
            // AVX-512 not available, skipping AVX-512 safety test
            // (Print statement removed for no_std compatibility)
            return;
        }

        let mut data = [0xFEDCBA9876543210u64; 16];
        let original = data;

        // This should not panic on systems with AVX-512
        clock_mix_avx2(&mut data);
        assert_ne!(data, original);

        // Should match scalar
        let mut scalar_data = original;
        crate::simd::scalar::scalar_clock_mix(&mut scalar_data);
        assert_eq!(data, scalar_data);
    }

    #[test]
    fn test_cpu_feature_detection_portability() {
        // Test that feature detection is portable across different systems
        // This mainly tests that the functions don't crash

        // Call feature detection multiple times
        for _ in 0..10 {
            let avx2 = is_avx2_available();
            let avx512 = is_avx512_available();

            // Results should be boolean
            assert!(avx2 == true || avx2 == false);
            assert!(avx512 == true || avx512 == false);
        }

        // Test that results are stable within a single run
        let avx2_first = is_avx2_available();
        let avx512_first = is_avx512_available();

        #[cfg(feature = "std")]
        std::thread::sleep(std::time::Duration::from_millis(1));

        let avx2_second = is_avx2_available();
        let avx512_second = is_avx512_available();

        assert_eq!(avx2_first, avx2_second);
        assert_eq!(avx512_first, avx512_second);
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_fallback_performance_characteristics() {
        // Test that fallback to scalar is reasonably performant
        use std::time::{Duration, Instant};

        let iterations = 1000;
        let mut data = [0xAAAAAAAAAAAAAAAAu64; 16];

        let start = Instant::now();
        for _ in 0..iterations {
            let mut test_data = data;
            clock_mix_avx2(&mut test_data);
        }
        let elapsed = start.elapsed();

        // Fallback performance test:
        // {} iterations took {:?}
        // Average time per operation: {:?}
        // (Print statements removed for no_std compatibility)

        // Should complete in reasonable time
        assert!(
            elapsed < Duration::from_secs(5),
            "Fallback should complete {} iterations in less than 5 seconds",
            iterations
        );
    }

    #[test]
    fn test_architecture_specific_behavior() {
        // Test behavior specific to different architectures
        let mut data = [0x5555555555555555u64; 16];

        // Should work on all supported architectures
        clock_mix_avx2(&mut data);

        // Verify the result is not all zeros (unless input was such that it produces zeros)
        let is_all_zeros = data.iter().all(|&x| x == 0);
        let input_all_zeros = [0x5555555555555555u64; 16].iter().all(|&x| x == 0);
        if !input_all_zeros {
            assert!(
                !is_all_zeros,
                "Output should not be all zeros for non-zero input"
            );
        }
    }

    #[test]
    fn test_cpuid_instruction_safety() {
        // Test that CPUID instruction usage is safe
        // This is mainly a smoke test to ensure no crashes

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // On x86/x86_64, CPUID should be available and safe to call
            let avx2 = is_avx2_available();
            let avx512 = is_avx512_available();

            // Should not have panicked
            assert!(avx2 == true || avx2 == false);
            assert!(avx512 == true || avx512 == false);
        }

        #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
        {
            // On other architectures, should return false
            assert!(!is_avx2_available());
            assert!(!is_avx512_available());
        }
    }
}