#![allow(unknown_lints)]
#![allow(clippy::if_then_panic)]
use gazebo::prelude::*;
use itertools::Itertools;
use crate::{assert::assert::Assert, errors::Diagnostic};
impl<'a> Assert<'a> {
pub fn conformance(&self, code: &str) {
self.conformance_except(code, &[])
}
pub fn conformance_except(&self, code: &str, except: &[&str]) {
let mut except = except.iter().peekable();
'next: for x in ConformanceTest::parse(code) {
if let Some(e) = except.peek() {
if x.code.contains(**e) {
except.next();
continue 'next;
}
}
x.test(self)
}
if let Some(missed) = except.next() {
panic!("Exception given but not used, `{}`", missed);
}
}
}
struct ConformanceTest {
code: String,
error: Option<(usize, String)>,
}
impl ConformanceTest {
fn parse(code: &str) -> Vec<Self> {
code.lines()
.collect::<Vec<_>>()
.split(|x| *x == "---")
.map(|xs| Self {
code: xs.join("\n"),
error: xs
.iter()
.find_position(|x| x.contains("###"))
.map(|(i, x)| (i + 1, (**x).split1("###").1.trim_start().to_owned())),
})
.collect()
}
fn test(&self, assert: &Assert) {
fn get_line(err: &anyhow::Error) -> Option<usize> {
match err.downcast_ref::<Diagnostic>() {
Some(Diagnostic {
span: Some(span), ..
}) => Some(span.resolve_span().begin_line + 1),
_ => None,
}
}
match &self.error {
None => {
assert.pass(&self.code);
}
Some((line, _msg)) => {
let err = assert.fail(&self.code, "");
let got = get_line(&err);
if got != Some(*line) {
panic!(
"starlark::assert::conformance, failed at wrong line!\nCode:\n{}\nError:\n{}\nExpected: {}\nGot: {:?}\n",
&self.code, &err, line, got
);
}
}
}
}
}