use std::collections::HashSet;
use std::sync::Arc;
use xpile_backend::{Backend, BackendConfig, HwProfile, Profile, Target};
use xpile_core::default_session;
use xpile_meta_hir::{Module, SourceLang};
fn empty_module(lang: SourceLang) -> Module {
Module {
name: "trait_runtime_test".into(),
source_lang: lang,
items: Vec::new(),
ffi_boundaries: Vec::new(),
}
}
fn config_for(target: Target) -> BackendConfig {
let hardware = match target {
Target::Ptx => Some(HwProfile::Ptx {
compute_capability: "sm_80".to_string(),
}),
Target::Wgsl => Some(HwProfile::Wgsl {
features: Vec::new(),
}),
_ => None,
};
BackendConfig {
target,
profile: Profile::RustOut,
hardware,
}
}
#[test]
fn backend_target_ownership_is_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<Target> = HashSet::new();
for backend in &session.backends {
for target in backend.targets() {
assert!(
seen.insert(*target),
"target {:?} is owned by more than one backend (collision on `{}`)",
target,
backend.name(),
);
}
}
}
#[test]
fn backend_names_are_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<&'static str> = HashSet::new();
for backend in &session.backends {
assert!(
seen.insert(backend.name()),
"backend name `{}` is registered more than once",
backend.name()
);
}
}
#[test]
fn frontend_extensions_are_disjoint_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<&str> = HashSet::new();
for frontend in &session.frontends {
for ext in frontend.extensions() {
assert!(
seen.insert(ext),
"extension `.{}` is claimed by more than one frontend (collision on `{}`)",
ext,
frontend.name(),
);
}
}
}
#[test]
fn every_backend_lower_is_deterministic_on_minimal_module() {
let session = default_session();
for backend in &session.backends {
for target in backend.targets() {
let module = empty_module(SourceLang::Rust);
let config = config_for(*target);
let first = backend.lower(&module, &config);
let second = backend.lower(&module, &config);
match (&first, &second) {
(Ok(a), Ok(b)) => assert_eq!(
a.primary, b.primary,
"backend `{}` target {:?} non-deterministic: outputs differ between calls",
backend.name(),
target,
),
(Err(e1), Err(e2)) => assert_eq!(
format!("{e1}"),
format!("{e2}"),
"backend `{}` target {:?} non-deterministic: errors differ between calls",
backend.name(),
target,
),
_ => panic!(
"backend `{}` target {:?} non-deterministic: one call succeeded, the other failed; first={:?} second={:?}",
backend.name(),
target,
first,
second,
),
}
}
}
}
#[test]
fn every_backend_targets_slice_is_stable_across_calls() {
let session = default_session();
for backend in &session.backends {
let first: Vec<Target> = backend.targets().to_vec();
let second: Vec<Target> = backend.targets().to_vec();
assert_eq!(
first,
second,
"backend `{}` returned different `targets()` slices on subsequent calls",
backend.name()
);
}
}
#[test]
fn default_session_registers_at_least_one_backend() {
let session = default_session();
assert!(
!session.backends.is_empty(),
"default_session() must register at least one backend — \
otherwise every other trait_runtime_properties test passes vacuously"
);
assert!(
!session.frontends.is_empty(),
"default_session() must register at least one frontend"
);
}
use xpile_contract_backend::ContractRenderConfig;
use xpile_contracts::{
Contract, ContractFormat, ContractId, XpileContractLane, XpileContractLayer,
};
fn dummy_contract(id: &str) -> Contract {
Contract {
id: ContractId::new(id),
layer: XpileContractLayer::Architectural,
lane: XpileContractLane::Proof,
depends_on: Vec::new(),
references: Vec::new(),
}
}
fn render_config_for(format: ContractFormat) -> ContractRenderConfig {
ContractRenderConfig {
format,
embed_citation: true,
include_falsification: false,
lean_version: Some((4, 0)),
}
}
#[test]
fn contract_backend_format_ownership_is_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<ContractFormat> = HashSet::new();
for backend in &session.contract_backends {
for format in backend.formats() {
assert!(
seen.insert(*format),
"ContractFormat {:?} is owned by more than one contract backend (collision on `{}`)",
format,
backend.name(),
);
}
}
}
#[test]
fn contract_backend_names_are_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<&'static str> = HashSet::new();
for backend in &session.contract_backends {
assert!(
seen.insert(backend.name()),
"contract backend name `{}` is registered more than once",
backend.name()
);
}
}
#[test]
fn contract_frontend_format_ownership_is_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<ContractFormat> = HashSet::new();
for frontend in &session.contract_frontends {
for format in frontend.formats() {
assert!(
seen.insert(*format),
"ContractFormat {:?} is claimed by more than one contract frontend (collision on `{}`)",
format,
frontend.name(),
);
}
}
}
#[test]
fn contract_frontend_names_are_unique_across_registered_impls() {
let session = default_session();
let mut seen: HashSet<&'static str> = HashSet::new();
for frontend in &session.contract_frontends {
assert!(
seen.insert(frontend.name()),
"contract frontend name `{}` is registered more than once",
frontend.name()
);
}
}
#[test]
fn every_contract_backend_render_is_deterministic_on_minimal_contract() {
let session = default_session();
for backend in &session.contract_backends {
for format in backend.formats() {
let contract = dummy_contract("C-CONTRACT-BACKEND-DETERMINISM");
let config = render_config_for(*format);
let first = backend.render(&contract, &config);
let second = backend.render(&contract, &config);
match (&first, &second) {
(Ok(a), Ok(b)) => {
assert_eq!(
a.primary,
b.primary,
"contract backend `{}` format {:?} non-deterministic primary",
backend.name(),
format,
);
assert_eq!(
a.citations,
b.citations,
"contract backend `{}` format {:?} non-deterministic citations",
backend.name(),
format,
);
}
(Err(e1), Err(e2)) => assert_eq!(
format!("{e1}"),
format!("{e2}"),
"contract backend `{}` format {:?} non-deterministic errors",
backend.name(),
format,
),
_ => panic!(
"contract backend `{}` format {:?} non-deterministic: one call succeeded, the other failed; first={:?} second={:?}",
backend.name(),
format,
first,
second,
),
}
}
}
}
#[test]
fn every_contract_frontend_parse_is_deterministic_on_minimal_source() {
let source = r#"\documentclass{article}
\begin{document}
\section{Basic equations}
A simple math span: $E = mc^2$.
A display math span:
\[ \int_0^{\infty} e^{-x} \, dx = 1 \]
\end{document}
"#;
let session = default_session();
for frontend in &session.contract_frontends {
let first = frontend.parse_to_equations(source);
let second = frontend.parse_to_equations(source);
match (&first, &second) {
(Ok(a), Ok(b)) => assert_eq!(
a,
b,
"contract frontend `{}` non-deterministic EquationsBlock",
frontend.name(),
),
(Err(e1), Err(e2)) => assert_eq!(
format!("{e1}"),
format!("{e2}"),
"contract frontend `{}` non-deterministic error",
frontend.name(),
),
_ => panic!(
"contract frontend `{}` non-deterministic: one call succeeded, the other failed",
frontend.name(),
),
}
}
}
#[test]
fn default_session_registers_at_least_one_contract_backend_and_frontend() {
let session = default_session();
assert!(
!session.contract_backends.is_empty(),
"default_session() must register at least one contract backend — \
otherwise the contract-backend tests pass vacuously"
);
assert!(
!session.contract_frontends.is_empty(),
"default_session() must register at least one contract frontend — \
otherwise the contract-frontend tests pass vacuously"
);
}
#[allow(dead_code)]
fn _force_use(_b: Arc<dyn Backend>) {}