use crate::e2e::codegen::TestBackendEmission;
use crate::e2e::fixture::Fixture;
pub fn emit_test_backend(
trait_bridge: &crate::core::config::TraitBridgeConfig,
methods: &[&crate::core::ir::MethodDef],
fixture: &Fixture,
) -> TestBackendEmission {
use crate::codegen::defaults::language_defaults;
use crate::e2e::escape::{escape_python, sanitize_ident};
use std::fmt::Write as FmtWrite;
let stub_name = format!("_TestStub_{}", sanitize_ident(&fixture.id));
let backend_name = extract_backend_name_from_input(&fixture.input, &fixture.id);
let defaults = language_defaults("python");
let mut setup = String::new();
let _ = writeln!(setup, "class {stub_name}:");
let mut method_count = 0usize;
if trait_bridge.super_trait.is_some() {
let escaped = escape_python(&backend_name);
let _ = writeln!(setup, " def name(self):");
let _ = writeln!(setup, " return \"{escaped}\"");
method_count += 1;
let _ = writeln!(setup, " def initialize(self):");
let _ = writeln!(setup, " pass");
method_count += 1;
let _ = writeln!(setup, " def shutdown(self):");
let _ = writeln!(setup, " pass");
method_count += 1;
}
for method in methods {
if method.has_default_impl {
continue;
}
if trait_bridge.super_trait.is_some() && method.name == "name" {
continue;
}
emit_python_stub_method(&mut setup, method, &*defaults);
method_count += 1;
}
if method_count == 0 {
let _ = writeln!(setup, " pass");
}
let arg_expr = format!("{stub_name}()");
let indented_setup = indent_block(&setup, 4);
let teardown_block = trait_bridge
.unregister_fn
.as_deref()
.map(|unregister_fn| {
let escaped = escape_python(&backend_name);
format!(" {unregister_fn}(\"{escaped}\")\n")
})
.unwrap_or_default();
TestBackendEmission {
setup_block: indented_setup,
arg_expr,
type_imports: Vec::new(),
teardown_block,
}
}
fn indent_block(block: &str, spaces: usize) -> String {
let prefix = " ".repeat(spaces);
block
.lines()
.map(|line| {
if line.trim().is_empty() {
line.to_string()
} else {
format!("{prefix}{line}")
}
})
.collect::<Vec<_>>()
.join("\n")
+ if block.ends_with('\n') { "\n" } else { "" }
}
fn emit_python_stub_method(
out: &mut String,
method: &crate::core::ir::MethodDef,
defaults: &dyn crate::codegen::defaults::LanguageDefaults,
) {
use std::fmt::Write as FmtWrite;
let mut param_parts = vec!["self".to_string()];
for (i, _) in method.params.iter().enumerate() {
param_parts.push(format!("_p{i}"));
}
let params_str = param_parts.join(", ");
let default_val = match &method.return_type {
crate::core::ir::TypeRef::Named(_) => "{}".to_string(),
crate::core::ir::TypeRef::Primitive(crate::core::ir::PrimitiveType::Bool) => "False".to_string(),
crate::core::ir::TypeRef::Primitive(crate::core::ir::PrimitiveType::F32) => "0.0".to_string(),
crate::core::ir::TypeRef::Primitive(crate::core::ir::PrimitiveType::F64) => "0.0".to_string(),
crate::core::ir::TypeRef::Primitive(_) => "1".to_string(),
other => defaults.emit_default(other),
};
let async_kw = if method.is_async { "async " } else { "" };
let _ = writeln!(out, " {async_kw}def {name}({params_str}):", name = method.name);
let _ = writeln!(out, " return {default_val}");
}
fn extract_backend_name_from_input(input: &serde_json::Value, fallback: &str) -> String {
if let Some(obj) = input.as_object() {
if let Some(s) = obj.get("name").and_then(|v| v.as_str()) {
return s.to_string();
}
for v in obj.values() {
if let Some(inner) = v.as_object()
&& let Some(s) = inner.get("name").and_then(|v| v.as_str())
{
return s.to_string();
}
}
for v in obj.values() {
if let Some(s) = v.as_str() {
return s.to_string();
}
}
}
fallback.to_string()
}