## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/realizar/src/gguf/test_helpers.rs

    Line
    Count
    Source
    1
    //! Test helpers for GGUF module testing
    2
    //!
    3
    //! This module contains shared test utilities that are used across
    4
    //! multiple test files in the GGUF module shatter.
    5
    //!
    6
    //! ## Contents
    7
    //!
    8
    //! - `create_test_model_with_config`: Creates a minimal OwnedQuantizedModel for testing
    9
    //! - `create_q4k_test_data`: Creates Q4_K quantized tensor data for testing
    10
    //!
    11
    //! ## Usage
    12
    //!
    13
    //! ```rust,ignore
    14
    //! #[cfg(test)]
    15
    //! use crate::gguf::test_helpers::{create_test_model_with_config, create_q4k_test_data};
    16
    //! ```
    17
    18
    use crate::gguf::{
    19
        GGUFConfig, OwnedQKVWeights, OwnedQuantizedLayer, OwnedQuantizedModel, OwnedQuantizedTensor,
    20
    };
    21
    22
    /// Create a test model with specific configuration
    23
    ///
    24
    /// This helper creates a minimal `OwnedQuantizedModel` with deterministic
    25
    /// test weights for verifying attention, FFN, and other model operations.
    26
    ///
    27
    /// # Arguments
    28
    ///
    29
    /// * `config` - The model configuration to use
    30
    ///
    31
    /// # Returns
    32
    ///
    33
    /// An `OwnedQuantizedModel` with test weights
    34
    115
    pub(crate) fn create_test_model_with_config(config: &GGUFConfig) -> OwnedQuantizedModel {
    35
        // Create minimal model weights for testing
    36
    115
        let vocab_size = config.vocab_size;
    37
    115
        let hidden_dim = config.hidden_dim;
    38
    115
        let intermediate_dim = config.intermediate_dim;
    39
    115
        let kv_dim = config.num_kv_heads * (hidden_dim / config.num_heads);
    40
    41
        // QKV projection: hidden_dim -> hidden_dim + 2*kv_dim (Q + K + V)
    42
    115
        let qkv_out_dim = hidden_dim + 2 * kv_dim;
    43
    115
        let qkv_weight = create_q4k_test_data(hidden_dim, qkv_out_dim);
    44
    45
        // Output projection: hidden_dim -> hidden_dim
    46
    115
        let attn_output_weight = create_q4k_test_data(hidden_dim, hidden_dim);
    47
    48
        // FFN weights
    49
    115
        let ffn_up_weight = create_q4k_test_data(hidden_dim, intermediate_dim);
    50
    115
        let ffn_down_weight = create_q4k_test_data(intermediate_dim, hidden_dim);
    51
    52
        // Layer norm weights
    53
    115
        let attn_norm_weight = vec![1.0f32; hidden_dim];
    54
    55
    115
        let layer = OwnedQuantizedLayer {
    56
    115
            attn_norm_weight,
    57
    115
            attn_norm_bias: None,
    58
    115
            qkv_weight: OwnedQKVWeights::Fused(qkv_weight),
    59
    115
            qkv_bias: None,
    60
    115
            attn_output_weight,
    61
    115
            attn_output_bias: None,
    62
    115
            ffn_up_weight,
    63
    115
            ffn_up_bias: None,
    64
    115
            ffn_down_weight,
    65
    115
            ffn_down_bias: None,
    66
    115
            ffn_gate_weight: None,
    67
    115
            ffn_gate_bias: None,
    68
    115
            ffn_norm_weight: None,
    69
    115
            ffn_norm_bias: None,
    70
    115
        };
    71
    72
    115
        let token_embedding = vec![0.1f32; vocab_size * hidden_dim];
    73
    115
        let output_norm_weight = vec![1.0f32; hidden_dim];
    74
    115
        let lm_head_weight = create_q4k_test_data(hidden_dim, vocab_size);
    75
    76
    115
        OwnedQuantizedModel {
    77
    115
            config: config.clone(),
    78
    115
            token_embedding,
    79
    115
            layers: vec![layer],
    80
    115
            output_norm_weight,
    81
    115
            output_norm_bias: None,
    82
    115
            lm_head_weight,
    83
    115
            lm_head_bias: None,
    84
    115
            #[cfg(feature = "cuda")]
    85
    115
            cuda_executor: None,
    86
    115
            #[cfg(feature = "cuda")]
    87
    115
            cuda_kernel_count: std::sync::atomic::AtomicU64::new(0),
    88
    115
            #[cfg(feature = "cuda")]
    89
    115
            cached_weight_names: std::sync::Mutex::new(std::collections::HashSet::new()),
    90
    115
        }
    91
    115
    }
    92
    93
    /// Create Q4_K test data for given dimensions
    94
    ///
    95
    /// Q4_K uses row-major storage where each row has ceil(in_dim/256) super-blocks.
    96
    /// Each super-block is 144 bytes and covers 256 values.
    97
    ///
    98
    /// # Arguments
    99
    ///
    100
    /// * `in_dim` - Input dimension (number of columns)
    101
    /// * `out_dim` - Output dimension (number of rows)
    102
    ///
    103
    /// # Returns
    104
    ///
    105
    /// An `OwnedQuantizedTensor` with Q4_K quantized test data
    106
    577
    pub(crate) fn create_q4k_test_data(in_dim: usize, out_dim: usize) -> OwnedQuantizedTensor {
    107
        // Row-major storage: each row needs ceil(in_dim/256) super-blocks
    108
    577
        let super_blocks_per_row = in_dim.div_ceil(256);
    109
    577
        let bytes_per_row = super_blocks_per_row * 144;
    110
    577
        let data_size = out_dim * bytes_per_row;
    111
    577
        let mut data = vec![0u8; data_size];
    112
    113
    81.3k
        for row in 0..out_dim577 {
    114
    89.1k
            for sb in 0..super_blocks_per_row81.3k {
    115
    89.1k
                let offset = row * bytes_per_row + sb * 144;
    116
                // d=1.0 in f16 format
    117
    89.1k
                data[offset..offset + 2].copy_from_slice(&0x3C00_u16.to_le_bytes());
    118
                // dmin=0
    119
    89.1k
                data[offset + 2..offset + 4].copy_from_slice(&0x0000_u16.to_le_bytes());
    120
                // Fill scales and quantized values with deterministic test pattern
    121
    12.5M
                for i12.4M in 4..144 {
    122
    12.4M
                    data[offset + i] = ((row + sb + i) % 16) as u8;
    123
    12.4M
                }
    124
            }
    125
        }
    126
    127
    577
        OwnedQuantizedTensor {
    128
    577
            data,
    129
    577
            in_dim,
    130
    577
            out_dim,
    131
    577
            qtype: 12, // Q4_K
    132
    577
        }
    133
    577
    }
    134
    135
    #[cfg(test)]
    136
    mod tests {
    137
        use super::*;
    138
    139
        #[test]
    140
    1
        fn test_create_q4k_test_data_basic() {
    141
    1
            let tensor = create_q4k_test_data(256, 64);
    142
    1
            assert_eq!(tensor.in_dim, 256);
    143
    1
            assert_eq!(tensor.out_dim, 64);
    144
    1
            assert_eq!(tensor.qtype, 12); // Q4_K
    145
            // 1 super-block per row, 144 bytes each, 64 rows
    146
    1
            assert_eq!(tensor.data.len(), 64 * 144);
    147
    1
        }
    148
    149
        #[test]
    150
    1
        fn test_create_q4k_test_data_multi_superblock() {
    151
            // 512 values needs 2 super-blocks per row
    152
    1
            let tensor = create_q4k_test_data(512, 32);
    153
    1
            assert_eq!(tensor.in_dim, 512);
    154
    1
            assert_eq!(tensor.out_dim, 32);
    155
            // 2 super-blocks per row, 144 bytes each, 32 rows
    156
    1
            assert_eq!(tensor.data.len(), 32 * 2 * 144);
    157
    1
        }
    158
    159
        #[test]
    160
    1
        fn test_create_test_model_with_config_basic() {
    161
    1
            let config = GGUFConfig {
    162
    1
                architecture: "test".to_string(),
    163
    1
                hidden_dim: 64,
    164
    1
                intermediate_dim: 128,
    165
    1
                num_heads: 4,
    166
    1
                num_kv_heads: 4,
    167
    1
                num_layers: 1,
    168
    1
                vocab_size: 100,
    169
    1
                rope_theta: 10000.0,
    170
    1
                context_length: 512,
    171
    1
                eps: 1e-5,
    172
    1
                rope_type: 0,
    173
    1
            };
    174
    175
    1
            let model = create_test_model_with_config(&config);
    176
    1
            assert_eq!(model.config.hidden_dim, 64);
    177
    1
            assert_eq!(model.layers.len(), 1);
    178
    1
            assert_eq!(model.token_embedding.len(), 100 * 64);
    179
    1
        }
    180
    }