use ktstr::test_support::{MetricCheck, OutputFormat, PayloadKind, Polarity};
#[derive(ktstr::Payload)]
#[payload(binary = "fio")]
#[allow(dead_code)]
struct FioMinimalPayload;
#[test]
fn derive_payload_minimal_const_name_and_defaults() {
assert_eq!(FIO_MINIMAL.name, "fio");
assert!(matches!(FIO_MINIMAL.kind, PayloadKind::Binary("fio")));
assert!(matches!(FIO_MINIMAL.output, OutputFormat::ExitCode));
assert!(FIO_MINIMAL.default_args.is_empty());
assert!(FIO_MINIMAL.default_checks.is_empty());
assert!(FIO_MINIMAL.metrics.is_empty());
}
#[derive(ktstr::Payload)]
#[payload(
binary = "fio",
name = "fio_custom",
output = Json,
)]
#[default_args("--output-format=json", "--minimal")]
#[default_args("--runtime=30")]
#[default_check(exit_code_eq(0))]
#[default_check(min("jobs.0.read.iops", 1000.0))]
#[metric(name = "jobs.0.read.iops", polarity = HigherBetter, unit = "iops")]
#[metric(name = "lat_ns", polarity = LowerBetter, unit = "ns")]
#[metric(name = "target_cpu", polarity = TargetValue(50.0), unit = "%")]
#[metric(name = "unlabeled")]
#[allow(dead_code)]
struct FioFullPayload;
#[test]
fn derive_payload_full_grammar() {
assert_eq!(FIO_FULL.name, "fio_custom");
assert!(matches!(FIO_FULL.kind, PayloadKind::Binary("fio")));
assert!(matches!(FIO_FULL.output, OutputFormat::Json));
assert_eq!(
FIO_FULL.default_args,
&["--output-format=json", "--minimal", "--runtime=30"],
);
assert_eq!(FIO_FULL.default_checks.len(), 2);
assert!(matches!(
FIO_FULL.default_checks[0],
MetricCheck::ExitCodeEq(0)
));
assert!(matches!(
FIO_FULL.default_checks[1],
MetricCheck::Min { metric, value } if metric == "jobs.0.read.iops" && value == 1000.0,
));
assert_eq!(FIO_FULL.metrics.len(), 4);
assert_eq!(FIO_FULL.metrics[0].name, "jobs.0.read.iops");
assert_eq!(FIO_FULL.metrics[0].polarity, Polarity::HigherBetter);
assert_eq!(FIO_FULL.metrics[0].unit, "iops");
assert_eq!(FIO_FULL.metrics[1].name, "lat_ns");
assert_eq!(FIO_FULL.metrics[1].polarity, Polarity::LowerBetter);
assert_eq!(FIO_FULL.metrics[1].unit, "ns");
assert_eq!(FIO_FULL.metrics[2].name, "target_cpu");
assert_eq!(FIO_FULL.metrics[2].polarity, Polarity::TargetValue(50.0));
assert_eq!(FIO_FULL.metrics[2].unit, "%");
assert_eq!(FIO_FULL.metrics[3].name, "unlabeled");
assert_eq!(FIO_FULL.metrics[3].polarity, Polarity::Unknown);
assert_eq!(FIO_FULL.metrics[3].unit, "");
}
#[derive(ktstr::Payload)]
#[payload(binary = "spec_cpu", output = LlmExtract)]
#[allow(dead_code)]
struct SpecCpuPayload;
#[test]
fn derive_payload_llm_extract_bare_is_no_hint() {
assert!(matches!(SPEC_CPU.output, OutputFormat::LlmExtract(None)));
}
#[derive(ktstr::Payload)]
#[payload(binary = "bench_with_hint", output = LlmExtract("focus on throughput"))]
#[allow(dead_code)]
struct BenchHintedPayload;
#[test]
fn derive_payload_llm_extract_call_carries_hint() {
match BENCH_HINTED.output {
OutputFormat::LlmExtract(Some(hint)) => {
assert_eq!(hint, "focus on throughput");
}
other => panic!("expected LlmExtract(Some(..)), got {other:?}"),
}
}
#[derive(ktstr::Payload)]
#[payload(binary = "bench_empty_call", output = LlmExtract())]
#[allow(dead_code)]
struct BenchEmptyCallPayload;
#[test]
fn derive_payload_llm_extract_empty_call_has_no_hint() {
assert!(matches!(
BENCH_EMPTY_CALL.output,
OutputFormat::LlmExtract(None),
));
}
#[derive(ktstr::Payload)]
#[payload(binary = "stress-ng")]
#[allow(dead_code)]
struct StressNg;
#[test]
fn derive_payload_no_suffix_keeps_full_name() {
assert_eq!(STRESS_NG.name, "stress-ng");
assert!(matches!(STRESS_NG.kind, PayloadKind::Binary("stress-ng")));
}
#[derive(ktstr::Payload)]
#[payload(binary = "memcheck-bin", name = "memcheck")]
#[allow(dead_code)]
struct MemcheckPayload;
#[test]
fn derive_payload_suffix_strip_happens_before_uppercase() {
assert_eq!(MEMCHECK.name, "memcheck");
assert!(matches!(MEMCHECK.kind, PayloadKind::Binary("memcheck-bin"),));
}
#[derive(ktstr::Payload)]
#[payload(binary = "bare_metric")]
#[metric(name = "throughput")]
#[allow(dead_code)]
struct BareMetricPayload;
#[test]
fn derive_payload_metric_minimal_defaults_unknown_polarity() {
assert_eq!(BARE_METRIC.metrics.len(), 1);
assert_eq!(BARE_METRIC.metrics[0].name, "throughput");
assert_eq!(BARE_METRIC.metrics[0].polarity, Polarity::Unknown);
assert_eq!(BARE_METRIC.metrics[0].unit, "");
}
#[derive(ktstr::Payload)]
#[payload(binary = "range_check_bin")]
#[default_check(range("cpu_pct", 10.0, 90.0))]
#[default_check(max("latency_us", 500.0))]
#[default_check(exists("sampling_key"))]
#[allow(dead_code)]
struct RangeCheckPayload;
#[test]
fn derive_payload_default_checks_resolve_all_constructors() {
assert_eq!(RANGE_CHECK.default_checks.len(), 3);
assert!(matches!(
RANGE_CHECK.default_checks[0],
MetricCheck::Range { metric, lo, hi } if metric == "cpu_pct" && lo == 10.0 && hi == 90.0,
));
assert!(matches!(
RANGE_CHECK.default_checks[1],
MetricCheck::Max { metric, value } if metric == "latency_us" && value == 500.0,
));
assert!(matches!(
RANGE_CHECK.default_checks[2],
MetricCheck::Exists("sampling_key"),
));
}
#[derive(ktstr::Payload)]
#[payload(binary = "qualified_check_bin")]
#[default_check(MetricCheck::min("iops", 1000.0))]
#[default_check(MetricCheck::max("latency_us", 500.0))]
#[default_check(exists("sampling_key"))]
#[allow(dead_code)]
struct QualifiedCheckPayload;
#[test]
fn derive_payload_accepts_qualified_check_prefix() {
assert_eq!(QUALIFIED_CHECK.default_checks.len(), 3);
assert!(matches!(
QUALIFIED_CHECK.default_checks[0],
MetricCheck::Min { metric, value } if metric == "iops" && value == 1000.0,
));
assert!(matches!(
QUALIFIED_CHECK.default_checks[1],
MetricCheck::Max { metric, value } if metric == "latency_us" && value == 500.0,
));
assert!(matches!(
QUALIFIED_CHECK.default_checks[2],
MetricCheck::Exists("sampling_key"),
));
}
#[derive(ktstr::Payload)]
#[payload(binary = "exit_code_bin", output = ExitCode)]
#[allow(dead_code)]
struct ExplicitExitCodePayload;
#[test]
fn derive_payload_explicit_exit_code_output() {
assert!(matches!(EXPLICIT_EXIT_CODE.output, OutputFormat::ExitCode));
assert_eq!(EXPLICIT_EXIT_CODE.name, "exit_code_bin");
assert!(matches!(
EXPLICIT_EXIT_CODE.kind,
PayloadKind::Binary("exit_code_bin"),
));
}
#[derive(ktstr::Payload)]
#[payload(binary = "fully_qualified_check_bin")]
#[default_check(::ktstr::test_support::MetricCheck::min("iops", 500.0))]
#[default_check(::ktstr::test_support::MetricCheck::exit_code_eq(0))]
#[allow(dead_code)]
struct FullyQualifiedCheckPayload;
#[test]
fn derive_payload_accepts_fully_qualified_check_path() {
assert_eq!(FULLY_QUALIFIED_CHECK.default_checks.len(), 2);
assert!(matches!(
FULLY_QUALIFIED_CHECK.default_checks[0],
MetricCheck::Min { metric, value } if metric == "iops" && value == 500.0,
));
assert!(matches!(
FULLY_QUALIFIED_CHECK.default_checks[1],
MetricCheck::ExitCodeEq(0),
));
}