blocks.rs 5.72 KB
Newer Older
Ryan Olson's avatar
Ryan Olson committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Block lifecycle helpers and TestBlockBuilder for tests.

use super::config::DEFAULT_TEST_BLOCK_SIZE;
use crate::BlockId;
use crate::blocks::{
    Block, BlockMetadata,
    state::{Registered, Reset, Staged},
};
use crate::pools::SequenceHash;
use crate::registry::BlockRegistry;

use super::token_blocks::create_test_token_block;

// ============================================================================
// Block lifecycle helpers
// ============================================================================

/// Create a staged (completed but not registered) block.
pub(crate) fn create_staged_block<T: BlockMetadata + std::fmt::Debug>(
    id: BlockId,
    tokens: &[u32],
) -> Block<T, Staged> {
    let token_block = create_test_token_block(tokens, tokens.len() as u32);
    let block: Block<T, Reset> = Block::new(id, tokens.len());
    block.complete(&token_block).expect("Should complete")
}

/// Create a registered block with a new ephemeral registry.
///
/// Returns both the registered block and its sequence hash.
pub(crate) fn create_registered_block<T: BlockMetadata + std::fmt::Debug>(
    id: BlockId,
    tokens: &[u32],
) -> (Block<T, Registered>, SequenceHash) {
    let staged = create_staged_block::<T>(id, tokens);
    let seq_hash = staged.sequence_hash();
    let registry = BlockRegistry::new();
    let handle = registry.register_sequence_hash(seq_hash);
    (staged.register_with_handle(handle), seq_hash)
}

/// Create a reset block with the given ID and block size.
pub(crate) fn create_reset_block<T: BlockMetadata>(
    id: BlockId,
    block_size: usize,
) -> Block<T, Reset> {
    Block::new(id, block_size)
}

/// Create multiple reset blocks with sequential IDs starting from 0.
pub(crate) fn create_reset_blocks<T: BlockMetadata>(
    count: usize,
    block_size: usize,
) -> Vec<Block<T, Reset>> {
    (0..count as BlockId)
        .map(|id| Block::new(id, block_size))
        .collect()
}

// ============================================================================
// Generic TestBlockBuilder<T>
// ============================================================================

/// Enhanced test block builder for consistent block creation.
///
/// Provides a fluent API for creating test blocks with explicit configuration
/// of all parameters, reducing the likelihood of block size mismatches in tests.
pub(crate) struct TestBlockBuilder<T: BlockMetadata> {
    id: BlockId,
    block_size: usize,
    tokens: Option<Vec<u32>>,
    _phantom: std::marker::PhantomData<T>,
}

impl<T: BlockMetadata> TestBlockBuilder<T> {
    /// Create a new test block builder with the given ID.
    ///
    /// Uses the default test block size (4) but allows override.
    pub(crate) fn new(id: BlockId) -> Self {
        Self {
            id,
            block_size: DEFAULT_TEST_BLOCK_SIZE,
            tokens: None,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Set the block size for this test block.
    ///
    /// The block size must be a power of 2 between 1 and 1024.
    pub(crate) fn with_block_size(mut self, size: usize) -> Self {
        use super::config::validate_test_block_size;
        assert!(
            validate_test_block_size(size),
            "Invalid test block size: {}. Must be power of 2 between 1 and 1024",
            size
        );
        self.block_size = size;
        self
    }

    /// Set specific tokens for this test block.
    ///
    /// If not specified, tokens will be auto-generated to match the block size.
    /// The length of the tokens vector should match the configured block size.
    pub(crate) fn with_tokens(mut self, tokens: Vec<u32>) -> Self {
        self.tokens = Some(tokens);
        self
    }

    /// Fill with sequential tokens starting from the given value.
    ///
    /// Generates tokens [start, start+1, start+2, ...] up to block_size.
    /// This is mutually exclusive with `with_tokens()` - the last one called wins.
    pub(crate) fn fill_iota(mut self, start: u32) -> Self {
        let tokens: Vec<u32> = (start..start + self.block_size as u32).collect();
        self.tokens = Some(tokens);
        self
    }

    /// Build a Staged (Complete) state block.
    ///
    /// This will auto-generate tokens if none were provided, ensuring they
    /// match the configured block size.
    pub(crate) fn build_staged(self) -> Block<T, Staged>
    where
        T: std::fmt::Debug,
    {
        use super::config::generate_test_tokens;

        // Auto-generate tokens if not provided
        let tokens = self
            .tokens
            .unwrap_or_else(|| generate_test_tokens(self.id as u32 * 100, self.block_size));

        // Validate token count matches block size
        assert_eq!(
            tokens.len(),
            self.block_size,
            "Token count {} doesn't match block size {}",
            tokens.len(),
            self.block_size
        );

        let token_block = create_test_token_block(&tokens, self.block_size as u32);
        Block::new(self.id, self.block_size)
            .complete(&token_block)
            .expect("Block size should match token block size")
    }

    /// Build a Registered state block with a specific registry.
    ///
    /// Creates a staged block and registers it with the provided registry.
    pub(crate) fn build_registered_with_registry(
        self,
        registry: &BlockRegistry,
    ) -> (Block<T, Registered>, SequenceHash)
    where
        T: std::fmt::Debug,
    {
        let staged = self.build_staged();
        let seq_hash = staged.sequence_hash();
        let handle = registry.register_sequence_hash(seq_hash);
        (staged.register_with_handle(handle), seq_hash)
    }
}