
    // F001: All bricks implement ComputeBrick trait
    #[cfg(feature = "cuda")]
    #[test]
    fn f001_brick_trait_implemented() {
        let _ = RmsNormBrick::new(vec![1.0; 64], 1e-5);
        let _ = QkvBrick::new(64, 64, 64, 64);
        let _ = AttentionBrick::new(8, 2, 64);
        let _ = FlashAttentionBrick::new(8, 2, 64);
        let _ = FfnBrick::new(64, 256);
        let _ = FusedFfnBrick::new(64, 256);
        let _ = RopeBrick::new(64, 8, 10000.0, 0);
        let _ = OProjBrick::new(512, 64);
        let _ = ActivationQuantBrick::new(64);
    }

    // F002: assertions().len() > 0 for all bricks
    #[test]
    #[cfg(feature = "cuda")]
    fn f002_brick_assertions_nonempty() {
        assert!(!RmsNormBrick::new(vec![1.0; 64], 1e-5)
            .assertions()
            .is_empty());
        assert!(!QkvBrick::new(64, 64, 64, 64).assertions().is_empty());
        assert!(!AttentionBrick::new(8, 2, 64).assertions().is_empty());
        assert!(!FlashAttentionBrick::new(8, 2, 64).assertions().is_empty());
        assert!(!FfnBrick::new(64, 256).assertions().is_empty());
        assert!(!FusedFfnBrick::new(64, 256).assertions().is_empty());
        assert!(!RopeBrick::new(64, 8, 10000.0, 0).assertions().is_empty());
        assert!(!OProjBrick::new(512, 64).assertions().is_empty());
        assert!(!ActivationQuantBrick::new(64).assertions().is_empty());
    }

    // F004: budget() returns non-zero value
    #[test]
    #[cfg(feature = "cuda")]
    fn f004_budget_nonzero() {
        assert!(RmsNormBrick::new(vec![1.0; 64], 1e-5).budget().us_per_token > 0.0);
        assert!(QkvBrick::new(64, 64, 64, 64).budget().us_per_token > 0.0);
        assert!(AttentionBrick::new(8, 2, 64).budget().us_per_token > 0.0);
        assert!(FlashAttentionBrick::new(8, 2, 64).budget().us_per_token > 0.0);
        assert!(FfnBrick::new(64, 256).budget().us_per_token > 0.0);
        assert!(FusedFfnBrick::new(64, 256).budget().us_per_token > 0.0);
        assert!(ActivationQuantBrick::new(64).budget().us_per_token > 0.0);
    }

    // F005: name() is unique per brick type
    #[test]
    #[cfg(feature = "cuda")]
    fn f005_brick_names_unique() {
        let names = [
            RmsNormBrick::new(vec![1.0; 64], 1e-5).name(),
            QkvBrick::new(64, 64, 64, 64).name(),
            AttentionBrick::new(8, 2, 64).name(),
            FlashAttentionBrick::new(8, 2, 64).name(),
            FfnBrick::new(64, 256).name(),
            FusedFfnBrick::new(64, 256).name(),
            RopeBrick::new(64, 8, 10000.0, 0).name(),
            OProjBrick::new(512, 64).name(),
            ActivationQuantBrick::new(64).name(),
        ];
        let unique: std::collections::HashSet<_> = names.iter().collect();
        assert_eq!(names.len(), unique.len());
    }

    // F008: TokenResult fields are consistent
    #[test]
    fn f008_token_result_consistent() {
        let budget = TokenBudget::from_latency(100.0);
        let result: TokenResult<Vec<f32>> = TokenResult::new(vec![], 10, 500.0, &budget);

        assert_eq!(result.tokens_processed, 10);
        assert!((result.us_per_token - 50.0).abs() < 0.001);
        assert!((result.tokens_per_sec - 20000.0).abs() < 1.0);
        assert!(result.budget_met); // 50µs < 100µs budget
    }

    // F010: Pipeline bottleneck correctly identified
    #[test]
    fn f010_bottleneck_identification() {
        let timing = LayerTiming {
            attn_norm_us: 1.2,
            qkv_us: 8.5,
            rope_us: 0.8,
            attention_us: 12.3, // Bottleneck
            o_proj_us: 4.1,
            ffn_norm_us: 1.2,
            ffn_us: 15.8, // Actually this is the bottleneck
            total_us: 43.9,
        };

        let (name, us) = timing.bottleneck();
        assert_eq!(name, "ffn");
        assert!((us - 15.8).abs() < 0.001);
    }

    // F021: TokenBudget latency/throughput consistent
    #[test]
    fn f021_budget_math_consistent() {
        let from_latency = TokenBudget::from_latency(50.0);
        let from_throughput = TokenBudget::from_throughput(20000.0);

        assert!((from_latency.tokens_per_sec - 20000.0).abs() < 1.0);
        assert!((from_throughput.us_per_token - 50.0).abs() < 0.001);
    }

    // F022: Budget violation triggers error
    #[test]
    fn f022_budget_enforcement() {
        let budget = TokenBudget::from_latency(10.0);
        assert!(budget.is_met(5.0)); // Under budget
        assert!(budget.is_met(10.0)); // At budget
        assert!(!budget.is_met(15.0)); // Over budget

        assert!(budget.gap_factor(5.0) < 1.0);
        assert!((budget.gap_factor(10.0) - 1.0).abs() < 0.001);
        assert!(budget.gap_factor(15.0) > 1.0);
    }

    // F049: No NaN assertion works
    #[test]
    fn f049_nan_assertion() {
        let assertion = BrickAssertion::no_nan();

        // Should pass
        assert!(assertion.check_f32(&[1.0, 2.0, 3.0], true).is_ok());

        // Should fail
        assert!(assertion.check_f32(&[1.0, f32::NAN, 3.0], true).is_err());
    }

    // Verify RmsNormBrick runs correctly.
    //
    // The 1ms budget from the earlier "more lenient" bump is still tight on
    // contended self-hosted runners — observed `budget exceeded` failures on
    // PR #1688 (run 25911250791) and multiple sibling PRs the same day.
    // RmsNorm on a 4-element vector is microseconds of real compute; under
    // CI load (concurrent cargo builds sharing target dirs + L3) wall time
    // can spike by 50-100×. Bumping to 100ms preserves the budget guardrail
    // (catches genuine perf regressions in the 100s-of-ms range) while
    // eliminating the noise floor crossing.
    #[test]
    fn rmsnorm_brick_runs() {
        let brick = RmsNormBrick::new(vec![1.0; 4], 1e-5)
            .with_budget(TokenBudget::from_latency(100_000.0)); // 100ms budget
        let input = vec![1.0, 2.0, 3.0, 4.0];
        let result = brick.run(&input).expect("should run");

        assert_eq!(result.output.len(), 4);
        assert!(!result.output.iter().any(|x| x.is_nan()));
    }

    // F003: Verify methods callable
    #[test]
    fn f003_verify_methods_callable() {
        let brick = RmsNormBrick::new(vec![1.0; 4], 1e-5);

        // All trait methods must be callable
        let _name = brick.name();
        let _budget = brick.budget();
        let _assertions = brick.assertions();
        let _verification = brick.verify();
        let _can_run = brick.can_run();
    }

    // F006: Budget values realistic (0 < µs < 1000)
    #[test]
    fn f006_budget_values_realistic() {
        let bricks: Vec<Box<dyn ComputeBrick<Output = Vec<f32>>>> =
            vec![Box::new(RmsNormBrick::new(vec![1.0; 896], 1e-5))];

        for brick in &bricks {
            let budget = brick.budget();
            assert!(
                budget.us_per_token > 0.0,
                "Budget must be > 0, got {}",
                budget.us_per_token
            );
            assert!(
                budget.us_per_token < 1000.0,
                "Budget must be < 1000µs, got {}",
                budget.us_per_token
            );
        }
    }

    // F007: Total layer budget = sum of brick budgets
    #[test]
    fn f007_total_layer_budget_is_sum() {
        let layer = TransformerLayerBrick::from_config(0, 896, 14, 2, 4864, 1e-5, 1000000.0, 2);
        let total_budget_us = layer.total_budget_us();

        // Sum individual brick budgets
        let sum = layer.attn_norm.budget().us_per_token
            + layer.qkv.budget().us_per_token
            + layer.rope.budget().us_per_token
            + layer.attention.budget().us_per_token
            + layer.o_proj.budget().us_per_token
            + layer.ffn_norm.budget().us_per_token
            + layer.ffn.budget().us_per_token;

        assert!(
            (total_budget_us - sum).abs() < 0.1,
            "Total {} should equal sum {}",
            total_budget_us,
            sum
        );
    }

    // F011: Timing strictly positive
    #[test]
    fn f011_timing_strictly_positive() {
        // Use larger input (16K elements) to ensure measurable timing
        let dim = 16384;
        let brick = RmsNormBrick::new(vec![1.0; dim], 1e-5)
            .with_budget(TokenBudget::from_latency(100_000.0)); // lenient budget
        let input: Vec<f32> = (0..dim).map(|i| i as f32).collect();
        let result = brick.run(&input).expect("should run");

        // With 16K elements, timing should be measurable (>= 1µs)
        // If still 0, the measurement resolution is insufficient - skip assertion
        if result.us_per_token > 0.0 {
            assert!(
                result.tokens_per_sec > 0.0,
                "Throughput must be positive when timing is positive"
            );
        }
        // Test passes either way - we're verifying no panics/errors occur
    }

    // F012: Layer timing fields match brick count
    #[test]
    fn f012_layer_timing_fields_match() {
        let timing = LayerTiming::default();

        // Layer has 7 bricks, timing struct has 7 component fields + total
        // Count the number of fields that are brick timings
        let brick_timings = [
            timing.attn_norm_us,
            timing.qkv_us,
            timing.rope_us,
            timing.attention_us,
            timing.o_proj_us,
            timing.ffn_norm_us,
            timing.ffn_us,
        ];

        assert_eq!(brick_timings.len(), 7, "Must have 7 brick timing fields");
    }

    // F013: CV calculation correct (stddev / mean * 100)
    #[test]
    fn f013_cv_calculation_correct() {
        // Test data: [10, 10, 10] has stddev=0, CV=0
        let samples = vec![10.0_f64, 10.0, 10.0];
        let mean = samples.iter().sum::<f64>() / samples.len() as f64;
        let variance =
            samples.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / samples.len() as f64;
        let stddev = variance.sqrt();
        let cv = stddev / mean * 100.0;

        assert!(cv.abs() < 0.001, "CV of identical values should be 0");

        // Test data: [5, 10, 15] has mean=10, stddev≈4.08, CV≈40.8%
        let samples = vec![5.0_f64, 10.0, 15.0];
        let mean = samples.iter().sum::<f64>() / samples.len() as f64;
        let variance =
            samples.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / samples.len() as f64;
        let stddev = variance.sqrt();
        let cv = stddev / mean * 100.0;

        assert!((cv - 40.82).abs() < 1.0, "CV should be ~40.8%, got {}", cv);
    }

    // F014: Statistical sample size ≥ 100 for valid CV
    #[test]
    fn f014_statistical_sample_size() {
        // BenchmarkConfig default is 100 samples
        let config = BenchmarkConfig::default();

        // Verify default config
        assert!(
            config.samples >= 100,
            "Default samples should be >= 100, got {}",
            config.samples
        );
    }

    // F015: Warmup samples discarded (not counted in stats)
    #[test]
    fn f015_warmup_samples_discarded() {
        // BenchmarkConfig default is 10 warmup
        let config = BenchmarkConfig::default();

        assert!(
            config.warmup > 0,
            "Warmup should be > 0, got {}",
            config.warmup
        );
        assert!(
            config.warmup < config.samples,
            "Warmup {} should be < samples {}",
            config.warmup,
            config.samples
        );
    }

    // F017: Assertions checkable with check_f32
    #[test]
    fn f017_assertions_checkable() {
        let assertions = vec![
            BrickAssertion::equiv_scalar(0.001),
            BrickAssertion::no_nan(),
            BrickAssertion::no_inf(),
            BrickAssertion::bounds(-100.0, 100.0),
        ];

        let test_data = &[1.0_f32, 2.0, 3.0];

        for assertion in &assertions {
            // All should be checkable
            let result = assertion.check_f32(test_data, true);
            assert!(result.is_ok(), "Assertion {} should pass", assertion.name);
        }
    }

    // F018: Brick composition creates valid layer
    #[test]
    fn f018_brick_composition_valid() {
        let layer = TransformerLayerBrick::from_config(0, 896, 14, 2, 4864, 1e-5, 1000000.0, 2);

        // Verify all component bricks exist and have valid state
        assert!(layer.can_run());
        assert!(!layer.assertions().is_empty());

        // Verify no NaN in budgets
        assert!(!layer.attn_norm.budget().us_per_token.is_nan());
        assert!(!layer.qkv.budget().us_per_token.is_nan());
        assert!(!layer.rope.budget().us_per_token.is_nan());
        assert!(!layer.attention.budget().us_per_token.is_nan());
        assert!(!layer.o_proj.budget().us_per_token.is_nan());
        assert!(!layer.ffn_norm.budget().us_per_token.is_nan());
        assert!(!layer.ffn.budget().us_per_token.is_nan());
    }

    // F019: Benchmark report has valid stats
    #[test]
    fn f019_benchmark_report_valid() {
        // Use larger input to ensure measurable timing (not sub-microsecond)
        let brick = RmsNormBrick::new(vec![1.0; 1024], 1e-5)
            .with_budget(TokenBudget::from_latency(100_000.0)); // lenient budget
        let input: Vec<f32> = (0..1024).map(|i| i as f32).collect();
        let config = BenchmarkConfig {
            warmup: 5,
            samples: 50, // Fewer samples for speed in tests
            max_cv: 1.0, // Allow high CV for test stability
        };

        // Run benchmark using nanoseconds for precision
        let report = benchmark_brick(
            &brick,
            || {
                let start = std::time::Instant::now();
                let _ = brick.run(&input);
                // Use nanos and convert to get sub-microsecond precision
                start.elapsed().as_nanos() as f64 / 1000.0
            },
            &config,
        );

        // All stats must be valid (may be 0 for very fast ops, that's ok)
        assert!(!report.mean_us.is_nan(), "mean must not be NaN");
        assert!(!report.std_us.is_nan(), "stddev must not be NaN");
        // CV can be NaN if mean is 0, so only check if mean > 0
        if report.mean_us > 0.0 {
            assert!(
                !report.cv.is_nan() && !report.cv.is_infinite(),
                "CV must be finite if mean > 0"
            );
        }
        assert!(!report.p50_us.is_nan(), "p50 must not be NaN");
        assert!(!report.p99_us.is_nan(), "p99 must not be NaN");

        // Logical constraints
        assert!(report.p50_us <= report.p99_us, "p50 <= p99");
        // tokens_per_sec can be infinite if mean is 0, so skip that check
    }

    // F050: FlashAttentionBrick FLOPs calculation
    #[test]
    fn f050_flash_attention_flops() {
        let brick = FlashAttentionBrick::new(8, 2, 64);
        let seq_len = 512;
        let expected = 4 * 8 * 64 * seq_len; // 4 * H * D * S
        assert_eq!(brick.flops(seq_len) as usize, expected);
    }

    // F051: FlashAttentionBrick memory reduction vs naive
    #[test]
    fn f051_flash_attention_memory() {
        let brick = FlashAttentionBrick::new(8, 2, 64);
        let seq_len = 512;
        let (naive, flash) = brick.memory_bytes(seq_len);

        // Flash should use less memory (no attention matrix)
        assert!(flash < naive, "Flash attention should use less memory");

        // Memory reduction should be > 1x
        let reduction = naive as f64 / flash as f64;
        assert!(reduction > 1.0, "Memory reduction should be > 1x");
    }

    // F052: FlashAttentionBrick tile count
    #[test]
    fn f052_flash_attention_tiles() {
        let brick = FlashAttentionBrick::with_tile_size(8, 2, 64, 128);
        assert_eq!(brick.num_tiles(512), 4); // 512 / 128 = 4
        assert_eq!(brick.num_tiles(500), 4); // ceil(500 / 128) = 4
        assert_eq!(brick.num_tiles(129), 2); // ceil(129 / 128) = 2
    }

    // F053: FlashAttentionBrick budget is 2x better than naive
    #[test]
    fn f053_flash_attention_budget() {
        let naive = AttentionBrick::new(8, 2, 64);
        let flash = FlashAttentionBrick::new(8, 2, 64);

        let speedup = naive.budget().us_per_token / flash.budget().us_per_token;
        assert!(
            speedup >= 2.0,
            "Flash attention should be >= 2x faster, got {:.1}x",
            speedup
        );
    }

    // F054: FlashAttentionBrick has custom assertions
    #[test]
    fn f054_flash_attention_assertions() {
        let brick = FlashAttentionBrick::new(8, 2, 64);
        let assertions = brick.assertions();

        // Should have online_softmax and tiled_kv_access assertions
        let has_online_softmax = assertions.iter().any(|a| a.name == "online_softmax");
        let has_tiled_kv = assertions.iter().any(|a| a.name == "tiled_kv_access");

        assert!(has_online_softmax, "Should have online_softmax assertion");
        assert!(has_tiled_kv, "Should have tiled_kv_access assertion");
    }

    // F055: FusedFfnBrick FLOPs calculation
    #[test]
    #[cfg(feature = "cuda")]
    fn f055_fused_ffn_flops() {
        let brick = FusedFfnBrick::new(64, 256);
        let expected = 6 * 64 * 256; // 6 * hidden * intermediate
        assert_eq!(brick.flops() as usize, expected);
    }
