adaptive_pipeline_domain/value_objects/
worker_count.rs

1// /////////////////////////////////////////////////////////////////////////////
2// Adaptive Pipeline
3// Copyright (c) 2025 Michael Gardner, A Bit of Help, Inc.
4// SPDX-License-Identifier: BSD-3-Clause
5// See LICENSE file in the project root.
6// /////////////////////////////////////////////////////////////////////////////
7
8//! # Worker Count Value Object - Parallel Processing Optimization Infrastructure
9//!
10//! This module provides a comprehensive worker count value object that
11//! implements adaptive parallel processing optimization, resource-aware worker
12//! allocation, and performance-optimized concurrency management for the
13//! adaptive pipeline system's parallel processing infrastructure.
14//!
15//! ## Overview
16//!
17//! The worker count system provides:
18//!
19//! - **Adaptive Parallel Processing**: Dynamic worker allocation based on file
20//!   characteristics
21//! - **Resource-Aware Optimization**: System resource consideration for optimal
22//!   performance
23//! - **Performance-Optimized Concurrency**: Empirically-validated worker count
24//!   strategies
25//! - **Cross-Platform Compatibility**: Consistent representation across
26//!   languages and systems
27//! - **Serialization**: Comprehensive serialization across storage backends and
28//!   APIs
29//! - **Validation**: Comprehensive worker count validation and constraint
30//!   enforcement
31//!
32//! ## Key Features
33//!
34//! ### 1. Adaptive Parallel Processing
35//!
36//! Dynamic worker allocation with comprehensive optimization:
37//!
38//! - **File Size Optimization**: Empirically-validated strategies for different
39//!   file sizes
40//! - **System Resource Awareness**: CPU core consideration for optimal
41//!   performance
42//! - **Processing Type Adaptation**: CPU-intensive vs I/O-intensive processing
43//!   optimization
44//! - **Performance Validation**: Benchmark-driven optimization strategies
45//!
46//! ### 2. Resource-Aware Optimization
47//!
48//! System resource consideration for optimal performance:
49//!
50//! - **CPU Core Management**: Optimal worker allocation based on available
51//!   cores
52//! - **Memory Consideration**: Worker count limits to prevent resource
53//!   exhaustion
54//! - **Oversubscription Control**: Balanced oversubscription for optimal
55//!   throughput
56//! - **Resource Validation**: System capability validation and constraint
57//!   enforcement
58//!
59//! ### 3. Cross-Platform Compatibility
60//!
61//! Consistent worker count management across platforms:
62//!
63//! - **JSON Serialization**: Standard JSON representation
64//! - **Database Storage**: Optimized database storage patterns
65//! - **API Integration**: RESTful API compatibility
66//! - **Multi-Language**: Consistent interface across languages
67//!
68//! ## Usage Examples
69//!
70//! ### Basic Worker Count Creation and Optimization
71
72//!
73//! ### System Resource-Aware Optimization
74
75//!
76//! ### Worker Count Validation and Suitability
77
78//!
79//! ### Performance Strategy and Description
80
81//!
82//! ### Conversion and Integration
83
84//!
85//! ## Worker Count Features
86//!
87//! ### Adaptive Optimization Strategies
88//!
89//! Worker count optimization based on empirical benchmark results:
90//!
91//! - **Tiny Files** (< 1MB): 1-2 workers (minimize overhead)
92//! - **Small Files** (1-50MB): 6-14 workers (aggressive parallelism, +102%
93//!   performance gain)
94//! - **Medium Files** (50-500MB): 5-12 workers (balanced approach)
95//! - **Large Files** (500MB-2GB): 8-12 workers (moderate parallelism)
96//! - **Huge Files** (> 2GB): 3-6 workers (conservative, +76% performance gain)
97//!
98//! ### Resource-Aware Features
99//!
100//! - **CPU Core Consideration**: Optimal worker allocation based on available
101//!   cores
102//! - **Memory Management**: Worker count limits to prevent resource exhaustion
103//! - **Oversubscription Control**: Balanced oversubscription for optimal
104//!   throughput
105//! - **System Validation**: Comprehensive system capability validation
106//!
107//! ### Performance Optimization
108//!
109//! - **Empirical Validation**: Benchmark-driven optimization strategies
110//! - **Processing Type Adaptation**: CPU-intensive vs I/O-intensive
111//!   optimization
112//! - **Throughput Balancing**: Optimal balance between throughput and
113//!   coordination overhead
114//! - **Suitability Checking**: Validation of worker count suitability for
115//!   specific workloads
116//!
117//! ## Performance Characteristics
118//!
119//! - **Creation Time**: ~1μs for new worker count creation with bounds checking
120//! - **Optimization Time**: ~5μs for file size-based optimization calculation
121//! - **Validation Time**: ~3μs for comprehensive user input validation
122//! - **Suitability Check**: ~2μs for worker count suitability validation
123//! - **Memory Usage**: ~8 bytes for worker count storage (single usize)
124//! - **Thread Safety**: Immutable access patterns are thread-safe
125//!
126//! ## Cross-Platform Compatibility
127//!
128//! - **Rust**: `WorkerCount` newtype wrapper with full optimization
129//! - **Go**: `WorkerCount` struct with equivalent interface
130//! - **JSON**: Numeric representation for API compatibility
131//! - **Database**: INTEGER column with validation constraints
132
133use serde::{Deserialize, Serialize};
134use std::fmt;
135
136/// Worker count value object for adaptive parallel processing optimization
137///
138/// This value object provides adaptive parallel processing optimization with
139/// resource-aware worker allocation, performance-optimized concurrency
140/// management, and empirically-validated optimization strategies. It implements
141/// Domain-Driven Design (DDD) value object patterns with comprehensive parallel
142/// processing support.
143///
144/// # Key Features
145///
146/// - **Adaptive Optimization**: Dynamic worker allocation based on file
147///   characteristics and system resources
148/// - **Resource-Aware Processing**: System resource consideration for optimal
149///   performance
150/// - **Performance-Optimized**: Empirically-validated strategies for different
151///   workload types
152/// - **Cross-Platform**: Consistent representation across languages and storage
153///   systems
154/// - **Validation**: Comprehensive worker count validation and constraint
155///   enforcement
156/// - **Serialization**: Full serialization support for storage and API
157///   integration
158///
159/// # Benefits Over Raw Numbers
160///
161/// - **Type Safety**: `WorkerCount` cannot be confused with other numeric types
162/// - **Domain Semantics**: Clear intent in function signatures and parallel
163///   processing business logic
164/// - **Optimization Logic**: Comprehensive optimization strategies and
165///   validation rules
166/// - **Future Evolution**: Extensible for worker-specific methods and
167///   performance features
168///
169/// # Design Principles
170///
171/// - **Adaptive**: Adjusts based on file characteristics and system resources
172/// - **Resource-Aware**: Considers system capabilities and prevents resource
173///   exhaustion
174/// - **Performance-Optimized**: Balances throughput vs coordination overhead
175/// - **Bounded**: Enforces minimum and maximum limits for reliable operation
176///
177/// # Optimization Strategies
178///
179/// Based on comprehensive benchmark results across file sizes from 5MB to 2GB:
180///
181/// - **Small Files** (≤50MB): Increased worker allocation by 2-3x (up to 102%
182///   performance gain)
183/// - **Medium Files** (100-500MB): Balanced approach with slight adjustments
184/// - **Large Files** (≥2GB): Reduced worker allocation by 70% (up to 76%
185///   performance gain)
186///
187/// # Use Cases
188///
189/// - **Parallel Processing Optimization**: Optimize worker allocation for
190///   parallel processing tasks
191/// - **Resource Management**: Manage system resources with optimal worker
192///   allocation
193/// - **Performance Tuning**: Fine-tune performance with empirically-validated
194///   strategies
195/// - **Workload Adaptation**: Adapt worker allocation to different workload
196///   characteristics
197///
198/// # Usage Examples
199///
200///
201/// # Cross-Language Mapping
202///
203/// - **Rust**: `WorkerCount` newtype wrapper with full optimization
204/// - **Go**: `WorkerCount` struct with equivalent interface
205/// - **JSON**: Numeric representation for API compatibility
206/// - **Database**: INTEGER column with validation constraints
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
208pub struct WorkerCount {
209    count: usize,
210}
211
212impl WorkerCount {
213    /// Minimum number of workers (always at least 1)
214    pub const MIN_WORKERS: usize = 1;
215
216    /// Maximum number of workers (prevent resource exhaustion)
217    pub const MAX_WORKERS: usize = 32;
218
219    /// Default worker count for fallback scenarios
220    pub const DEFAULT_WORKERS: usize = 4;
221
222    /// Creates a new WorkerCount with the specified number of workers
223    ///
224    /// # Purpose
225    /// Creates a type-safe worker count with automatic bounds checking.
226    /// Ensures worker count is always within valid operational range.
227    ///
228    /// # Why
229    /// Bounded worker counts provide:
230    /// - Prevention of resource exhaustion (max limit)
231    /// - Guaranteed minimum parallelism (min limit)
232    /// - Type-safe concurrency configuration
233    /// - Consistent operational behavior
234    ///
235    /// # Arguments
236    /// * `count` - Number of workers (will be clamped to 1-32 range)
237    ///
238    /// # Returns
239    /// `WorkerCount` with value clamped to [`MIN_WORKERS`, `MAX_WORKERS`]
240    ///
241    /// # Examples
242    pub fn new(count: usize) -> Self {
243        Self {
244            count: count.clamp(Self::MIN_WORKERS, Self::MAX_WORKERS),
245        }
246    }
247
248    /// Returns the number of workers
249    pub fn count(&self) -> usize {
250        self.count
251    }
252
253    /// Returns the number of workers (alias for test framework compatibility)
254    pub fn value(&self) -> usize {
255        self.count
256    }
257
258    /// Calculates the optimal worker count based on file size
259    ///
260    /// # Purpose
261    /// Provides empirically-optimized worker allocation based on comprehensive
262    /// benchmark results. Maximizes throughput while minimizing coordination
263    /// overhead.
264    ///
265    /// # Why
266    /// File size-based optimization provides:
267    /// - Up to 102% performance improvement for small files (5-10MB)
268    /// - Up to 76% performance improvement for large files (2GB+)
269    /// - Reduced coordination overhead for optimal throughput
270    /// - Evidence-based strategy from real benchmark data
271    ///
272    /// # Empirical Optimization Results
273    /// Based on comprehensive benchmarks across 5MB to 2GB files:
274    /// - **Small files** (≤50MB): Increased worker allocation by 2-3x (up to
275    ///   102% performance gain)
276    /// - **Medium files** (100-500MB): Balanced approach with slight
277    ///   adjustments
278    /// - **Large files** (≥2GB): Reduced worker allocation by 70% (up to 76%
279    ///   performance gain)
280    ///
281    /// # Strategy (Benchmark-Optimized)
282    /// - **Tiny files** (< 1MB): 1-2 workers (minimize overhead)
283    /// - **Small files** (1-50MB): 6-14 workers (aggressive parallelism)
284    /// - **Medium files** (50-500MB): 5-12 workers (balanced approach)
285    /// - **Large files** (500MB-2GB): 8-12 workers (moderate parallelism)
286    /// - **Huge files** (> 2GB): 3-6 workers (conservative strategy)
287    ///
288    /// # Arguments
289    /// * `file_size` - Size of the file in bytes
290    ///
291    /// # Returns
292    /// Optimal `WorkerCount` for the given file size (empirically validated)
293    ///
294    /// # Examples
295    pub fn optimal_for_file_size(file_size: u64) -> Self {
296        let optimal_count = match file_size {
297            // Tiny files: Minimize overhead, single-threaded or minimal parallelism
298            0..=1_048_576 => {
299                if file_size < 64_000 {
300                    1
301                } else {
302                    2
303                }
304            }
305
306            // Small files: Aggressive parallelism based on benchmark results
307            // 5MB: 9 workers optimal (vs 3 adaptive = +102% performance)
308            // 10MB: 14 workers optimal (vs 4 adaptive = +97% performance)
309            1_048_577..=52_428_800 => {
310                // 1MB to 50MB
311                let size_mb = (file_size as f64) / 1_048_576.0;
312                if size_mb <= 5.0 {
313                    9 // Optimal for 5MB files
314                } else if size_mb <= 10.0 {
315                    (9.0 + (size_mb - 5.0) * 1.0).round() as usize // 9-14 workers
316                } else {
317                    (14.0 - (size_mb - 10.0) * 0.2).round() as usize // 14 down to ~6 workers
318                }
319            }
320
321            // Medium files: Balanced approach with benchmark adjustments
322            // 50MB: 5 workers optimal (vs 6 adaptive = +70% performance)
323            // 100MB: 8 workers optimal (chunk size was the issue, not workers)
324            52_428_801..=524_288_000 => {
325                // 50MB to 500MB
326                let size_mb = (file_size as f64) / 1_048_576.0;
327                if size_mb <= 100.0 {
328                    (5.0 + (size_mb - 50.0) * 0.06).round() as usize // 5-8 workers
329                } else {
330                    (8.0 + (size_mb - 100.0) * 0.01).round() as usize // 8-12 workers
331                }
332            }
333
334            // Large files: Moderate parallelism to avoid coordination overhead
335            524_288_001..=2_147_483_648 => {
336                // 500MB to 2GB
337                let size_gb = (file_size as f64) / 1_073_741_824.0;
338                (8.0 + size_gb * 2.0).round() as usize // 8-12 workers
339            }
340
341            // Huge files: Conservative approach based on 2GB benchmark results
342            // 2GB: 3 workers optimal (vs 14 adaptive = +76% performance)
343            _ => {
344                let size_gb = (file_size as f64) / 1_073_741_824.0;
345                if size_gb <= 4.0 {
346                    3 // Optimal for 2GB files
347                } else {
348                    (3.0 + (size_gb - 2.0) * 0.5).round() as usize // 3-6 workers max
349                }
350            }
351        };
352
353        Self::new(optimal_count)
354    }
355
356    /// Calculates optimal worker count considering both file size and system
357    /// resources
358    ///
359    /// This method combines file size optimization with system resource
360    /// awareness to prevent over-subscription of CPU cores.
361    ///
362    /// # Arguments
363    /// * `file_size` - Size of the file in bytes
364    /// * `available_cores` - Number of available CPU cores
365    ///
366    /// # Returns
367    /// Optimal WorkerCount considering both file size and system resources
368    pub fn optimal_for_file_and_system(file_size: u64, available_cores: usize) -> Self {
369        let file_optimal = Self::optimal_for_file_size(file_size);
370        let system_limit = (available_cores * 2).max(Self::MIN_WORKERS); // Allow 2x oversubscription
371
372        Self::new(file_optimal.count().min(system_limit))
373    }
374
375    /// Calculates optimal worker count with processing complexity consideration
376    ///
377    /// Different processing types have different CPU intensity:
378    /// - Compression: CPU-intensive, benefits from more workers
379    /// - Encryption: CPU-intensive, benefits from more workers
380    /// - I/O operations: Less CPU-intensive, fewer workers needed
381    ///
382    /// # Arguments
383    /// * `file_size` - Size of the file in bytes
384    /// * `available_cores` - Number of available CPU cores
385    /// * `is_cpu_intensive` - Whether the processing is CPU-intensive
386    ///
387    /// # Returns
388    /// Optimal WorkerCount considering processing complexity
389    pub fn optimal_for_processing_type(file_size: u64, available_cores: usize, is_cpu_intensive: bool) -> Self {
390        let base_optimal = Self::optimal_for_file_and_system(file_size, available_cores);
391
392        if is_cpu_intensive {
393            // CPU-intensive operations benefit from more workers up to core count
394            let cpu_optimal = available_cores.min(Self::MAX_WORKERS);
395            Self::new(base_optimal.count().max(cpu_optimal))
396        } else {
397            // I/O-intensive operations need fewer workers to avoid contention
398            Self::new(((base_optimal.count() * 3) / 4).max(Self::MIN_WORKERS))
399        }
400    }
401
402    /// Returns the default worker count based on system capabilities
403    ///
404    /// This is used as a fallback when file size or other parameters are
405    /// unknown.
406    ///
407    /// # Returns
408    /// Default WorkerCount based on available CPU cores
409    pub fn default_for_system() -> Self {
410        let available_cores = std::thread::available_parallelism()
411            .map(|n| n.get())
412            .unwrap_or(Self::DEFAULT_WORKERS);
413
414        Self::new(available_cores.min(Self::MAX_WORKERS))
415    }
416
417    /// Checks if this worker count is suitable for the given file size
418    ///
419    /// # Arguments
420    /// * `file_size` - Size of the file in bytes
421    ///
422    /// # Returns
423    /// True if the worker count is reasonable for the file size
424    pub fn is_suitable_for_file_size(&self, file_size: u64) -> bool {
425        let optimal = Self::optimal_for_file_size(file_size);
426        let difference = self.count.abs_diff(optimal.count);
427
428        // Allow up to 50% deviation from optimal
429        difference <= (optimal.count / 2).max(1)
430    }
431
432    /// Returns a description of the worker count strategy for the given file
433    /// size
434    ///
435    /// # Arguments
436    /// * `file_size` - Size of the file in bytes
437    ///
438    /// # Returns
439    /// Human-readable description of the strategy
440    pub fn strategy_description(file_size: u64) -> &'static str {
441        match file_size {
442            0..=1_048_576 => "Minimal parallelism (tiny files)",
443            1_048_577..=10_485_760 => "Light parallelism (small files)",
444            10_485_761..=104_857_600 => "Balanced parallelism (medium files)",
445            104_857_601..=1_073_741_824 => "High parallelism (large files)",
446            _ => "Maximum throughput (huge files)",
447        }
448    }
449
450    /// Validates user-provided worker count with sanity checks
451    ///
452    /// # Arguments
453    /// * `user_count` - User-specified worker count
454    /// * `available_cores` - Number of available CPU cores
455    /// * `file_size` - Size of file being processed
456    ///
457    /// # Returns
458    /// * `Ok(usize)` - Validated worker count (may be adjusted)
459    /// * `Err(String)` - Error message explaining why input is invalid
460    pub fn validate_user_input(user_count: usize, available_cores: usize, file_size: u64) -> Result<usize, String> {
461        // Sanity check: minimum 1 worker
462        if user_count == 0 {
463            return Err("Worker count must be at least 1".to_string());
464        }
465
466        // Sanity check: don't exceed reasonable limits
467        if user_count > Self::MAX_WORKERS {
468            return Err(format!(
469                "Worker count {} exceeds maximum {}",
470                user_count,
471                Self::MAX_WORKERS
472            ));
473        }
474
475        // Warning for excessive oversubscription (more than 4x cores)
476        let max_reasonable = available_cores * 4;
477        if user_count > max_reasonable {
478            return Err(format!(
479                "Worker count {} may cause excessive oversubscription ({}x cores). Consider {} or less",
480                user_count,
481                user_count / available_cores.max(1),
482                max_reasonable
483            ));
484        }
485
486        // Warning for tiny files with many workers (inefficient)
487        if file_size < 1_048_576 && user_count > 2 {
488            return Err(format!(
489                "Worker count {} is excessive for tiny file ({} bytes). Consider 1-2 workers",
490                user_count, file_size
491            ));
492        }
493
494        // All checks passed
495        Ok(user_count)
496    }
497}
498
499impl Default for WorkerCount {
500    fn default() -> Self {
501        Self::default_for_system()
502    }
503}
504
505impl fmt::Display for WorkerCount {
506    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507        write!(f, "{} workers", self.count)
508    }
509}
510
511impl From<usize> for WorkerCount {
512    fn from(count: usize) -> Self {
513        Self::new(count)
514    }
515}
516
517impl From<WorkerCount> for usize {
518    fn from(worker_count: WorkerCount) -> Self {
519        worker_count.count
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    use super::*;
526
527    /// Tests worker count boundary validation and constraint enforcement.
528    ///
529    /// This test validates that worker count values are properly bounded
530    /// within the defined minimum and maximum limits and that boundary
531    /// values are handled correctly.
532    ///
533    /// # Test Coverage
534    ///
535    /// - Minimum worker count constraint (0 -> MIN_WORKERS)
536    /// - Maximum worker count constraint (100 -> MAX_WORKERS)
537    /// - Valid worker count within bounds
538    /// - Boundary value handling
539    /// - Constraint enforcement
540    ///
541    /// # Test Scenario
542    ///
543    /// Tests worker count creation with values below minimum, above
544    /// maximum, and within valid range to verify constraint enforcement.
545    ///
546    /// # Domain Concerns
547    ///
548    /// - Resource allocation constraints
549    /// - System resource protection
550    /// - Performance optimization boundaries
551    /// - Safe parallelism limits
552    ///
553    /// # Assertions
554    ///
555    /// - Zero workers is clamped to minimum
556    /// - Excessive workers is clamped to maximum
557    /// - Valid worker count is preserved
558    /// - Boundary constraints are enforced
559    #[test]
560    fn test_worker_count_bounds() {
561        assert_eq!(WorkerCount::new(0).count(), WorkerCount::MIN_WORKERS);
562        assert_eq!(WorkerCount::new(100).count(), WorkerCount::MAX_WORKERS);
563        assert_eq!(WorkerCount::new(8).count(), 8);
564    }
565
566    /// Tests optimal worker count calculation based on file size.
567    ///
568    /// This test validates that the optimal worker count algorithm
569    /// provides appropriate parallelism levels for different file
570    /// sizes with empirically optimized values.
571    ///
572    /// # Test Coverage
573    ///
574    /// - Tiny file optimization (minimal workers)
575    /// - Small file optimization (aggressive parallelism)
576    /// - Medium file optimization (balanced parallelism)
577    /// - Large file optimization (moderate parallelism)
578    /// - Huge file optimization (conservative parallelism)
579    /// - Very huge file optimization (scaled parallelism)
580    ///
581    /// # Test Scenario
582    ///
583    /// Tests worker count optimization across different file size
584    /// categories to verify appropriate parallelism strategies.
585    ///
586    /// # Domain Concerns
587    ///
588    /// - File size-based optimization
589    /// - Parallelism strategy selection
590    /// - Performance optimization
591    /// - Resource efficiency
592    ///
593    /// # Assertions
594    ///
595    /// - Tiny files use minimal workers (1)
596    /// - Small files use aggressive parallelism (9)
597    /// - Medium files use balanced parallelism (8)
598    /// - Large files use moderate parallelism (12)
599    /// - Huge files use conservative parallelism (3)
600    /// - Very huge files use scaled parallelism (5)
601    #[test]
602    fn test_optimal_for_file_size() {
603        // Tiny files should use minimal workers
604        let tiny = WorkerCount::optimal_for_file_size(1000);
605        assert_eq!(tiny.count(), 1);
606
607        // Small files should use aggressive parallelism (empirically optimized)
608        let small = WorkerCount::optimal_for_file_size(5 * 1024 * 1024); // 5MB
609        assert_eq!(small.count(), 9); // Empirically optimal for 5MB files
610
611        // Medium files should use balanced parallelism
612        let medium = WorkerCount::optimal_for_file_size(100 * 1024 * 1024); // 100MB
613        assert_eq!(medium.count(), 8); // Based on algorithm: 5 + (100-50)*0.06 = 8
614
615        // Large files should use moderate parallelism
616        let large = WorkerCount::optimal_for_file_size(500 * 1024 * 1024); // 500MB
617        assert_eq!(large.count(), 12); // Based on algorithm: 8 + (500-100)*0.01 = 12
618
619        // Huge files should use conservative parallelism (empirically optimized)
620        let huge = WorkerCount::optimal_for_file_size(3 * 1024 * 1024 * 1024); // 3GB
621        assert_eq!(huge.count(), 3); // Empirically optimal for huge files
622
623        // Very huge files should still be conservative
624        let very_huge = WorkerCount::optimal_for_file_size(5 * 1024 * 1024 * 1024); // 5GB
625        assert_eq!(very_huge.count(), 5); // Based on algorithm: 3 + (5-2)*0.5 =
626                                          // 4.5 rounded to 5
627    }
628
629    /// Tests optimal worker count considering both file size and system
630    /// resources.
631    ///
632    /// This test validates that the optimization algorithm considers
633    /// both file characteristics and system capabilities to determine
634    /// the most appropriate worker count.
635    ///
636    /// # Test Coverage
637    ///
638    /// - System-constrained optimization (limited cores)
639    /// - File-optimized parallelism (many cores)
640    /// - Core count consideration
641    /// - Oversubscription limits
642    /// - Balanced resource allocation
643    ///
644    /// # Test Scenario
645    ///
646    /// Tests worker count optimization with different core counts
647    /// to verify system resource consideration in optimization.
648    ///
649    /// # Domain Concerns
650    ///
651    /// - System resource awareness
652    /// - Hardware capability consideration
653    /// - Balanced optimization strategy
654    /// - Performance vs resource trade-offs
655    ///
656    /// # Assertions
657    ///
658    /// - Limited cores constrain worker count
659    /// - Many cores enable file-optimized parallelism
660    /// - System resources are considered
661    /// - Oversubscription is controlled
662    #[test]
663    fn test_optimal_for_file_and_system() {
664        let file_size = 100 * 1024 * 1024; // 100MB
665
666        // With limited cores, should be constrained by system
667        let limited = WorkerCount::optimal_for_file_and_system(file_size, 2);
668        assert!(limited.count() <= 4); // 2 cores * 2 oversubscription
669
670        // With many cores, should be optimized for file size
671        let many_cores = WorkerCount::optimal_for_file_and_system(file_size, 32);
672        assert!(many_cores.count() >= 4);
673    }
674
675    /// Tests worker count optimization based on processing type
676    /// characteristics.
677    ///
678    /// This test validates that the optimization algorithm adjusts
679    /// worker count based on whether the processing is CPU-intensive
680    /// or I/O-intensive for optimal performance.
681    ///
682    /// # Test Coverage
683    ///
684    /// - CPU-intensive processing optimization
685    /// - I/O-intensive processing optimization
686    /// - Processing type differentiation
687    /// - Worker count adjustment strategy
688    /// - Performance characteristic consideration
689    ///
690    /// # Test Scenario
691    ///
692    /// Tests worker count optimization for both CPU-intensive and
693    /// I/O-intensive processing to verify appropriate strategies.
694    ///
695    /// # Domain Concerns
696    ///
697    /// - Processing type awareness
698    /// - CPU vs I/O optimization
699    /// - Performance characteristic adaptation
700    /// - Resource utilization efficiency
701    ///
702    /// # Assertions
703    ///
704    /// - CPU-intensive uses more workers
705    /// - I/O-intensive uses fewer workers
706    /// - Processing type affects optimization
707    /// - Worker count is appropriately adjusted
708    #[test]
709    fn test_processing_type_optimization() {
710        let file_size = 50 * 1024 * 1024; // 50MB
711        let cores = 8;
712
713        let cpu_intensive = WorkerCount::optimal_for_processing_type(file_size, cores, true);
714        let io_intensive = WorkerCount::optimal_for_processing_type(file_size, cores, false);
715
716        // CPU-intensive should use more workers
717        assert!(cpu_intensive.count() >= io_intensive.count());
718    }
719
720    /// Tests worker count suitability validation for file sizes.
721    ///
722    /// This test validates that the suitability check correctly
723    /// determines whether a given worker count is appropriate
724    /// for a specific file size.
725    ///
726    /// # Test Coverage
727    ///
728    /// - Optimal worker count suitability
729    /// - Close worker count suitability
730    /// - Unsuitable worker count detection
731    /// - Suitability threshold validation
732    /// - Performance appropriateness check
733    ///
734    /// # Test Scenario
735    ///
736    /// Tests suitability validation with optimal, close, and
737    /// far worker counts to verify threshold detection.
738    ///
739    /// # Domain Concerns
740    ///
741    /// - Performance suitability validation
742    /// - Worker count appropriateness
743    /// - Optimization threshold detection
744    /// - Resource allocation validation
745    ///
746    /// # Assertions
747    ///
748    /// - Optimal worker count is suitable
749    /// - Close worker count is suitable
750    /// - Far worker count is unsuitable
751    /// - Suitability thresholds work correctly
752    #[test]
753    fn test_suitability_check() {
754        let file_size = 10 * 1024 * 1024; // 10MB
755        let optimal = WorkerCount::optimal_for_file_size(file_size);
756
757        // Optimal should be suitable
758        assert!(optimal.is_suitable_for_file_size(file_size));
759
760        // Slightly different should still be suitable
761        let close = WorkerCount::new(optimal.count() + 1);
762        assert!(close.is_suitable_for_file_size(file_size));
763
764        // Very different should not be suitable
765        let far = WorkerCount::new(optimal.count() * 3);
766        assert!(!far.is_suitable_for_file_size(file_size));
767    }
768
769    /// Tests strategy description generation for different file sizes.
770    ///
771    /// This test validates that appropriate strategy descriptions
772    /// are generated for different file size categories to help
773    /// users understand the parallelism approach.
774    ///
775    /// # Test Coverage
776    ///
777    /// - Minimal parallelism description (tiny files)
778    /// - Light parallelism description (small files)
779    /// - Balanced parallelism description (medium files)
780    /// - High parallelism description (large files)
781    /// - Maximum throughput description (huge files)
782    ///
783    /// # Test Scenario
784    ///
785    /// Tests strategy description generation across different file
786    /// size categories to verify appropriate descriptions.
787    ///
788    /// # Domain Concerns
789    ///
790    /// - Strategy communication and clarity
791    /// - User understanding and transparency
792    /// - Parallelism strategy explanation
793    /// - Performance approach description
794    ///
795    /// # Assertions
796    ///
797    /// - Tiny files get minimal parallelism description
798    /// - Small files get light parallelism description
799    /// - Medium files get balanced parallelism description
800    /// - Large files get high parallelism description
801    /// - Huge files get maximum throughput description
802    #[test]
803    fn test_strategy_descriptions() {
804        assert_eq!(
805            WorkerCount::strategy_description(500),
806            "Minimal parallelism (tiny files)"
807        );
808        assert_eq!(
809            WorkerCount::strategy_description(5 * 1024 * 1024),
810            "Light parallelism (small files)"
811        );
812        assert_eq!(
813            WorkerCount::strategy_description(50 * 1024 * 1024),
814            "Balanced parallelism (medium files)"
815        );
816        assert_eq!(
817            WorkerCount::strategy_description(500 * 1024 * 1024),
818            "High parallelism (large files)"
819        );
820        assert_eq!(
821            WorkerCount::strategy_description(5 * 1024 * 1024 * 1024),
822            "Maximum throughput (huge files)"
823        );
824    }
825
826    /// Tests display formatting and type conversions for worker count.
827    ///
828    /// This test validates that worker count provides proper display
829    /// formatting and supports conversions to and from primitive
830    /// types for interoperability.
831    ///
832    /// # Test Coverage
833    ///
834    /// - Display formatting with unit suffix
835    /// - Conversion from usize to WorkerCount
836    /// - Conversion from WorkerCount to usize
837    /// - Type interoperability
838    /// - String representation
839    ///
840    /// # Test Scenario
841    ///
842    /// Tests display formatting and bidirectional conversions
843    /// between WorkerCount and primitive types.
844    ///
845    /// # Domain Concerns
846    ///
847    /// - User-friendly display formatting
848    /// - Type system interoperability
849    /// - Conversion safety and correctness
850    /// - API usability
851    ///
852    /// # Assertions
853    ///
854    /// - Display format includes "workers" suffix
855    /// - Conversion from usize preserves value
856    /// - Conversion to usize preserves value
857    /// - Type conversions are bidirectional
858    #[test]
859    fn test_display_and_conversions() {
860        let worker_count = WorkerCount::new(8);
861        assert_eq!(format!("{}", worker_count), "8 workers");
862
863        let from_usize: WorkerCount = (6).into();
864        assert_eq!(from_usize.count(), 6);
865
866        let to_usize: usize = worker_count.into();
867        assert_eq!(to_usize, 8);
868    }
869}