use std::cmp::max;
use std::fmt::{Debug, Formatter};
use std::path::Path;
use std::process::Command;
use itertools::{EitherOrBoth, Itertools};
use python_parser::ast::Statement;
use mamba::{Arguments, transpile_dir};
use mamba::common::delimit::newline_delimited;
use crate::common::{
delete_dir, python_src_to_stmts, resource_content, resource_content_path,
resource_content_randomize, resource_path,
};
use crate::system::common::PYTHON;
mod common;
pub mod valid;
struct OutTestErr(Vec<String>);
type OutTestRet<T = ()> = Result<T, OutTestErr>;
impl From<Vec<String>> for OutTestErr {
fn from(value: Vec<String>) -> Self {
OutTestErr(value)
}
}
impl Debug for OutTestErr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.iter().map(|err| write!(f, "{}\n", err)).collect()
}
}
fn test_directory(valid: bool, input: &[&str], output: &[&str], file_name: &str) -> OutTestRet {
let args = Arguments { annotate: true };
test_directory_args(valid, input, output, file_name, &args)
}
fn test_directory_args(valid: bool, input: &[&str], _: &[&str], file_name: &str, args: &Arguments) -> OutTestRet {
let (output_path, output_file) =
resource_content_randomize(true, input, &format!("{}.py", file_name));
let res = fallable(valid, input, &output_path, &output_file, file_name, args);
delete_dir(&output_path).map_err(|_| OutTestErr(vec![]))?;
let (check_ast, check_src, out_ast, out_src) = res?;
let check_string = newline_delimited(check_ast.iter().map(|stmt| format!("{:?}", stmt)));
let out_string = newline_delimited(out_ast.iter().map(|stmt| format!("{:?}", stmt)));
let longest_line = out_src.lines().max_by(|l1, l2| l1.len().cmp(&l2.len())).unwrap_or("").len();
let min_line = 25;
let out_line_len = max(min_line, longest_line);
let gap = 12;
let sep_count = 10;
let mut msg = format!(
"Was AST:{}Expected AST:\n{}{}{}\n",
String::from_utf8(vec![b' '; out_line_len - 8 + gap]).unwrap(),
String::from_utf8(vec![b'-'; sep_count]).unwrap(),
String::from_utf8(vec![b' '; out_line_len - sep_count + gap]).unwrap(),
String::from_utf8(vec![b'-'; sep_count]).unwrap()
);
for line in out_src.lines().zip_longest(check_src.lines()) {
match line {
EitherOrBoth::Both(out, check) => {
let left_len = out.len();
msg.push_str(&format!(
"{}{}{}\n",
out,
String::from_utf8(vec![b' '; out_line_len + gap - left_len]).unwrap(),
check
))
}
EitherOrBoth::Left(out) => msg.push_str(&format!("{}\n", out)),
EitherOrBoth::Right(check) => msg.push_str(&format!(
"{}{}\n",
String::from_utf8(vec![b' '; out_line_len + gap]).unwrap(),
check
)),
}
}
assert_eq!(out_string, check_string, "{}", msg);
Ok(())
}
fn fallable(
valid: bool,
input: &[&str],
output_path: &str,
output_file: &str,
file_name: &str,
arguments: &Arguments,
) -> OutTestRet<(Vec<Statement>, String, Vec<Statement>, String)> {
let current_dir_string = resource_path(valid, input, "");
let current_dir = Path::new(¤t_dir_string);
transpile_dir(¤t_dir, Some(&format!("{}.mamba", file_name)), Some(output_path), arguments)
.map_err(|errs| OutTestErr(errs))?;
let cmd1 = Command::new(PYTHON)
.arg("-m")
.arg("py_compile")
.arg(&resource_path(valid, input, &format!("{}_check.py", file_name)))
.output()
.expect("Could not run Python command.");
let cmd2 = Command::new(PYTHON)
.arg("-m")
.arg("py_compile")
.arg(&output_file)
.output()
.expect("Could not run Python command.");
let check_src = resource_content(true, input, &format!("{}_check.py", file_name));
let check_ast = python_src_to_stmts(&check_src.replace("\r\n", "\n"));
let out_src = resource_content_path(output_file)?;
let out_ast = python_src_to_stmts(&out_src);
let width = 3;
if cmd1.status.code().unwrap() != 0 {
let msg = format!(
"{}\nRunning Python command on reference resource: {}\n\
Source:\n\
----------\n\
{}\n\
----------",
String::from_utf8(cmd1.stderr).unwrap().trim(),
resource_path(valid, input, &format!("{}_check.py", file_name)),
check_src
.lines()
.enumerate()
.map(|(line, src)| { format!("{:width$} | {}\n", line + 1, src) })
.collect::<String>()
);
Err(OutTestErr(vec![msg]))
} else if cmd2.status.code().unwrap() != 0 {
let msg = format!(
"{}\nRunning Python command on Mamba output.\n\
Source:\n\
----------\n\
{}\n\
----------",
String::from_utf8(cmd2.stderr).unwrap().trim(),
out_src
.lines()
.enumerate()
.map(|(line, src)| { format!("{:width$} | {}\n", line + 1, src) })
.collect::<String>()
);
Err(OutTestErr(vec![msg]))
} else {
Ok((check_ast, check_src, out_ast, out_src))
}
}