#![cfg(feature = "experimental-ion-1-1")]
use std::collections::HashMap;
use ion_rs::serde::to_string;
use serde::Serialize;
use crate::conformance_dsl::prelude::*;
#[cfg(feature = "experimental-reader-writer")]
mod conformance_dsl;
#[derive(PartialEq, Serialize)]
enum TestResult {
Ok,
Failed,
Panic,
}
impl ToString for TestResult {
fn to_string(&self) -> String {
match self {
Self::Ok => "Ok".to_string(),
Self::Failed => "Failed".to_string(),
Self::Panic => "Panic".to_string(),
}
}
}
#[derive(Serialize)]
struct TestReport {
name: String,
result: TestResult,
error: Option<String>,
}
trait TestReporter {
fn current_collection(&mut self, source: &str, collection: &TestCollection);
fn add_test_result(&mut self, result: TestReport);
fn finalize(&mut self);
}
#[derive(Default)]
struct PlainTextReporter {
name_width: usize,
current_source: String,
failed_tests: Vec<(String, TestReport)>,
}
impl TestReporter for PlainTextReporter {
fn current_collection(&mut self, source: &str, collection: &TestCollection) {
println!("\nRunning tests: {}", source);
self.current_source = source.to_owned();
self.name_width = collection.iter().fold(0, |acc, d| {
std::cmp::max(acc, d.name.as_ref().map_or(0, |n| n.len()))
});
}
fn add_test_result(&mut self, report: TestReport) {
println!(
" {:<width$} ... [{}]",
report.name,
report.result.to_string(),
width = self.name_width
);
if report.result != TestResult::Ok {
self.failed_tests
.push((self.current_source.clone(), report));
}
}
fn finalize(&mut self) {
if self.failed_tests.len() > 0 {
println!(
"============================================================================"
);
for (source, result) in self.failed_tests.iter() {
println!("Source: {}", source);
println!("Test : {}", result.name);
let error = if result.result == TestResult::Panic {
format!("PANIC: {}", result.error.clone().unwrap())
} else {
result.error.clone().unwrap()
};
println!("Error : {}", error);
println!("----------------------------------------------");
}
}
}
}
#[derive(Default)]
struct IonReporter {
current_source: String,
results: HashMap<String, Vec<TestReport>>,
}
impl TestReporter for IonReporter {
fn current_collection(&mut self, source: &str, _collection: &TestCollection) {
self.current_source = source.to_string();
self.results.insert(self.current_source.clone(), vec![]);
}
fn add_test_result(&mut self, report: TestReport) {
self.results
.get_mut(&self.current_source)
.unwrap()
.push(report);
}
fn finalize(&mut self) {
println!("{}", to_string(&self.results).unwrap());
}
}
#[cfg(feature = "experimental-reader-writer")]
pub fn main() {
let options = std::env::args()
.skip(1)
.take_while(|a| a.starts_with("-"))
.collect::<Vec<String>>();
let test_paths = std::env::args()
.skip(1 + options.len())
.collect::<Vec<String>>();
let emit_ion = Some("-i") == options.get(0).map(|v| v.as_str());
let reporter: &mut dyn TestReporter = if emit_ion {
&mut IonReporter::default()
} else {
&mut PlainTextReporter::default()
};
for test_path in test_paths {
let collection = TestCollection::load(&test_path).expect("unable to load test file");
reporter.current_collection(&test_path, &collection);
for doc in collection.iter() {
let panic_buffer = std::sync::Arc::new(std::sync::Mutex::new(String::new()));
let old_hook = std::panic::take_hook();
std::panic::set_hook({
let panic_buffer = panic_buffer.clone();
Box::new(move |info| {
let mut panic_buffer = panic_buffer.lock().unwrap();
if let Some(s) = info.payload().downcast_ref::<&str>() {
panic_buffer.push_str(s);
} else {
panic_buffer.push_str(&format!("{:?}", info));
}
})
});
let panic_result = std::panic::catch_unwind(|| doc.run());
std::panic::set_hook(old_hook);
let test_result = if panic_result.is_ok() {
let name = doc.name.as_deref().unwrap_or("<unnamed>").to_owned();
match panic_result.unwrap() {
Err(e) => TestReport {
name,
result: TestResult::Failed,
error: Some(format!("{:?}", e)),
},
Ok(_) => TestReport {
name,
result: TestResult::Ok,
error: None,
},
}
} else {
TestReport {
name: doc.name.as_deref().unwrap_or("<unnamed>").to_owned(),
result: TestResult::Panic,
error: Some((*panic_buffer.lock().unwrap()).to_string()),
}
};
reporter.add_test_result(test_result);
}
}
reporter.finalize();
}
#[cfg(not(feature = "experimental-reader-writer"))]
pub fn main() {
println!("Needs feature experimental-reader-writer");
}