use std::{fs, path::PathBuf};
const RAW_BUILDERS: [&str; 4] = [
"DataFrame::new_with_column_order",
"DataFrame::new(",
"Column::from_values",
"Scalar::Null(NullKind::",
];
const FORBIDDEN_POLICY_HELPERS: [&str; 1] = ["collect_dict_constructor_payloads"];
const ALLOWED: [(&str, &str); 4] = [
(
"execute_dataframe_merge_fixture_operation",
"delegates to merge_dataframes_on_with_options; reassembles its parts into a DataFrame",
),
(
"execute_dataframe_merge_asof_fixture_operation",
"delegates to fp_join::merge_asof_with_options; reassembles its parts",
),
(
"execute_dataframe_merge_ordered_fixture_operation",
"delegates to the ordered-merge entry point; reassembles its parts",
),
(
"execute_dataframe_fixture_operation",
"multi-op arm: builds INPUT columns for specific ops (e.g. a mask or a \
replacement column) before calling the op under test",
),
];
fn harness_source() -> String {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/lib.rs");
fs::read_to_string(&path).unwrap_or_else(|err| panic!("read {}: {err}", path.display()))
}
fn strip_line_comments(source: &str) -> Vec<String> {
source
.lines()
.map(|line| {
if line.trim_start().starts_with("//") {
String::new()
} else {
line.to_string()
}
})
.collect()
}
fn self_building_arms(source: &str) -> Vec<String> {
let lines = strip_line_comments(source);
let is_top_level_fn =
|line: &str| (line.starts_with("fn ") || line.starts_with("pub fn ")) && line.contains('(');
let tops: Vec<usize> = lines
.iter()
.enumerate()
.filter(|(_, line)| is_top_level_fn(line))
.map(|(idx, _)| idx)
.collect();
let mut found = Vec::new();
for (position, &start) in tops.iter().enumerate() {
let header = &lines[start];
let name = header
.trim_start_matches("pub ")
.trim_start_matches("fn ")
.split('(')
.next()
.unwrap_or_default()
.trim()
.to_string();
if !(name.starts_with("execute_") && name.ends_with("_fixture_operation")) {
continue;
}
let end = tops.get(position + 1).copied().unwrap_or(lines.len());
let body = lines[start..end].join("\n");
if RAW_BUILDERS.iter().any(|needle| body.contains(needle)) {
found.push(name);
}
}
found.sort();
found
}
fn forbidden_policy_helpers(source: &str) -> Vec<String> {
let source = strip_line_comments(source).join("\n");
let mut found: Vec<String> = FORBIDDEN_POLICY_HELPERS
.iter()
.filter(|name| source.contains(&format!("fn {name}(")))
.map(|name| (*name).to_owned())
.collect();
found.sort();
found
}
#[test]
fn fixture_operation_arms_delegate_to_frankenpandas() {
let source = harness_source();
let actual = self_building_arms(&source);
let mut expected: Vec<String> = ALLOWED.iter().map(|(name, _)| (*name).to_owned()).collect();
expected.sort();
let unexpected: Vec<&String> = actual.iter().filter(|a| !expected.contains(a)).collect();
assert!(
unexpected.is_empty(),
"these fixture-operation arms build a frame/column themselves instead of \
calling the FrankenPandas operation under test:\n {unexpected:#?}\n\n\
A packet over a self-built arm certifies the HARNESS, not the library — \
and if the arm is more capable than fp-frame, it is green over a real \
gap. Delegate to the real API (adding it to fp-frame if it does not \
exist yet, as DataFrame::from_scalar had to be), or add the arm to \
ALLOWED with a reason. See br-frankenpandas-oxodo."
);
let stale: Vec<&String> = expected.iter().filter(|e| !actual.contains(e)).collect();
assert!(
stale.is_empty(),
"these arms are on the ALLOWED list but no longer build anything \
themselves:\n {stale:#?}\n\nPrune them, so the list keeps meaning \
something instead of pre-approving whatever appears later."
);
}
#[test]
fn fixture_policy_helpers_do_not_preempt_frankenpandas() {
let forbidden = forbidden_policy_helpers(&harness_source());
assert!(
forbidden.is_empty(),
"these helpers own pandas-visible policy before FrankenPandas runs: \
{forbidden:#?}\n\n\
Delegate the policy to the public fp-* API instead. A helper that \
rejects, projects, or fabricates constructor payloads can certify the \
harness rather than the implementation. See br-frankenpandas-oxodo."
);
}
#[test]
fn documenting_a_raw_builder_in_a_comment_is_not_a_violation() {
let source = "\
fn execute_documented_fixture_operation() {
// This arm used to call Column::from_values and DataFrame::new_with_column_order.
delegate()
}
";
assert!(
self_building_arms(source).is_empty(),
"a comment mentioning a raw builder must not be read as a call"
);
let real = "\
fn execute_offender_fixture_operation() {
let c = Column::from_values(v)?;
}
";
assert_eq!(
self_building_arms(real),
vec!["execute_offender_fixture_operation".to_string()]
);
}
#[test]
fn policy_helper_is_detected_even_without_a_raw_builder() {
let source = "\
fn collect_dict_constructor_payloads() {
return Err(\"column is absent\".to_owned());
}
";
assert_eq!(
forbidden_policy_helpers(source),
vec!["collect_dict_constructor_payloads".to_string()]
);
}
fn struct_field_names(source: &str, header: &str) -> Vec<String> {
let start = source
.find(header)
.unwrap_or_else(|| panic!("{header} not found in src/lib.rs"));
let body = &source[start + header.len()..];
let end = body.find("\n}").expect("struct body must close");
body[..end]
.lines()
.filter_map(|line| {
let trimmed = line.trim();
let trimmed = trimmed.strip_prefix("pub ").unwrap_or(trimmed);
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
return None;
}
let (name, _) = trimmed.split_once(':')?;
let name = name.trim();
name.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
.then(|| name.to_owned())
})
.collect()
}
#[test]
fn every_fixture_option_reaches_the_oracle_request_00de2() {
let source = harness_source();
let fixture_fields = struct_field_names(&source, "pub struct PacketFixture {");
let request_fields = struct_field_names(&source, "struct OracleRequest {");
assert!(
fixture_fields.len() > 150,
"PacketFixture scan found only {} fields; the scanner is broken",
fixture_fields.len()
);
assert!(
request_fields.len() > 100,
"OracleRequest scan found only {} fields; the scanner is broken",
request_fields.len()
);
const NOT_FORWARDED: &[&str] = &[
"packet_id",
"case_id",
"mode",
"fixture_provenance",
"oracle_source",
"requirement_level",
"retired",
"expected_series",
"expected_frame",
"expected_join",
"expected_alignment",
"expected_bool",
"expected_positions",
"expected_scalar",
"expected_dtype",
"expected_error_contains",
"excel_input_base64",
"feather_input_base64",
"ipc_stream_input_base64",
"parquet_input_base64",
];
let missing: Vec<&str> = fixture_fields
.iter()
.map(String::as_str)
.filter(|field| !request_fields.iter().any(|forwarded| forwarded == field))
.filter(|field| !NOT_FORWARDED.contains(field))
.collect();
assert!(
missing.is_empty(),
"PacketFixture options that never reach OracleRequest — add each to the struct AND to \
its population in capture_live_oracle_expected, or to NOT_FORWARDED with a reason: \
{missing:?}"
);
let stale: Vec<&&str> = NOT_FORWARDED
.iter()
.filter(|field| !fixture_fields.iter().any(|present| present == *field))
.collect();
assert!(
stale.is_empty(),
"NOT_FORWARDED names fields PacketFixture no longer has: {stale:?}"
);
}