Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
on-keyday committed Apr 26, 2024
1 parent 5117c21 commit 38b35c0
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 235 deletions.
247 changes: 13 additions & 234 deletions src/tool/cmptest/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
use std::{
collections::HashMap,
env,
ffi::OsStr,
fs, os,
path::{Path, PathBuf},
process,
};
use std::{fs, path::Path};

use clap::{arg, Parser};
use serde::Deserialize;
use testutil::TestSchedule;
use testutil::{Error, TestSchedule};
mod testutil;
use rand::{
self,
distributions::{Alphanumeric, DistString},
};

#[derive(Parser)]
struct Args {
Expand All @@ -23,226 +12,9 @@ struct Args {
#[arg(long, short('c'))]
test_config_file: String,
}
type Error = Box<dyn std::error::Error>;

struct TestScheduler {
template_files: HashMap<String, String>,
tmpdir: Option<PathBuf>,
input_binaries: HashMap<String, Vec<u8>>,
}

impl TestScheduler {
fn new() -> Self {
Self {
template_files: HashMap::new(),
tmpdir: None,
input_binaries: HashMap::new(),
}
}

fn read_template(&mut self, path: &str) -> Result<String, Error> {
if let Some(x) = self.template_files.get(path) {
return Ok(x.clone());
} else {
let t = fs::read_to_string(path)?;
self.template_files.insert(path.to_string(), t);
Ok(self.template_files.get(path).unwrap().clone())
}
}

fn read_input_binary(&mut self, path: &str) -> Result<Vec<u8>, Error> {
if let Some(x) = self.input_binaries.get(path) {
return Ok(x.clone());
} else {
let t = fs::read(path)?;
self.input_binaries.insert(path.to_string(), t);
Ok(self.input_binaries.get(path).unwrap().clone())
}
}

fn get_tmp_dir<'a>(&'a mut self) -> PathBuf {
if let Some(x) = self.tmpdir.as_ref() {
x.clone()
} else {
let dir = env::temp_dir();
let mut rng = rand::thread_rng();
let random_str = Alphanumeric.sample_string(&mut rng, 32);
let dir = dir.join(random_str);
self.tmpdir = Some(dir);
self.tmpdir.as_ref().unwrap().clone()
}
}

fn prepare_content<'a>(&mut self, sched: &TestSchedule<'a>) -> Result<String, Error> {
// get template and replace with target
let template = self.read_template(&sched.runner.test_template)?;
let replace_with = &sched.runner.replace_struct_name;
let template = template.replace(replace_with, &sched.input.format_name);
let replace_with = &sched.runner.replace_file_name;
let path = format!("{}/{}", sched.file.dir, sched.file.base);
let instance = template.replace(replace_with, &path);
Ok(instance)
}

fn create_input_file<'a>(
&mut self,
sched: &TestSchedule<'a>,
instance: String,
) -> Result<(PathBuf, PathBuf, PathBuf), Error> {
let tmp_dir = self.get_tmp_dir();
let tmp_dir = tmp_dir.join(&sched.file.base);
let tmp_dir = tmp_dir.join(&sched.input.format_name);
let tmp_dir = tmp_dir.join(&sched.file.suffix);
fs::create_dir_all(&tmp_dir)?;
let input_file = tmp_dir.join(&sched.runner.build_input_name);
let output_file = tmp_dir.join(&sched.runner.build_output_name);
fs::write(&input_file, instance)?;
Ok((tmp_dir, input_file, output_file))
}

fn replace_cmd(
cmd: &mut Vec<String>,
tmp_dir: &PathBuf,
input: &PathBuf,
output: &PathBuf,
exec: Option<&PathBuf>,
) {
for c in cmd {
if c == "$INPUT" {
*c = input.to_str().unwrap().to_string();
}
if c == "$OUTPUT" {
*c = output.to_str().unwrap().to_string();
}
if c == "$EXEC" {
if let Some(e) = exec {
*c = e.to_str().unwrap().to_string();
}
}
if c == "$TMPDIR" {
*c = tmp_dir.to_str().unwrap().to_string();
}
}
}

fn exec_cmd<'a>(
&mut self,
base: &Vec<String>,
tmp_dir: &PathBuf,
input: &PathBuf,
output: &PathBuf,
exec: Option<&PathBuf>,
expect_ok: bool,
) -> Result<bool, Error> {
let mut cmd = base.clone();
Self::replace_cmd(&mut cmd, tmp_dir, input, output, exec);
let mut r = process::Command::new(&cmd[0]);
r.args(&cmd[1..]);
let done = r.output()?;
let code = done.status.code();
match code {
Some(0) => return Ok(true),
status => {
if let Some(x) = status {
if x == 1 && !expect_ok {
return Ok(false);
}
}
let stderr_str = String::from_utf8_lossy(&done.stderr);
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("process exit with {:?}\n{}", status, stderr_str),
)
.into());
}
};
}

pub fn run_test_schedule<'a>(&mut self, sched: &TestSchedule<'a>) -> Result<(), Error> {
let instance = self.prepare_content(sched)?;

let (tmp_dir, input, output) = self.create_input_file(sched, instance)?;

// build test
self.exec_cmd(
&sched.runner.build_command,
&tmp_dir,
&input,
&output,
None,
true,
)?;

let exec = output;

let output = tmp_dir.join("output.bin");

let input_binary = sched.input.binary.clone().into();

// run test
let status = self.exec_cmd(
&sched.runner.run_command,
&tmp_dir,
&input_binary,
&output,
Some(&exec),
false,
)?;

let expect = !sched.input.failure_case;

if status != expect {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("test failed: expect {} but got {}", expect, status),
)
.into());
}

let input_binary = self.read_input_binary(&input_binary.to_string_lossy())?; // check input is valid
let output = fs::read(&output)?; // check output is valid

let min_size = if input_binary.len() < output.len() {
input_binary.len()
} else {
output.len()
};

let mut diff = Vec::new();

for i in 0..min_size {
if input_binary[i] != output[i] {
diff.push((i, Some(input_binary[i]), Some(output[i])));
}
}

if input_binary.len() != output.len() {
if input_binary.len() > output.len() {
diff.push((output.len(), None, Some(output[output.len()])));
} else {
diff.push((
input_binary.len(),
Some(input_binary[input_binary.len()]),
None,
));
}
}

if !diff.is_empty() {
let mut debug = format!("test failed: input and output is different\n");
for (i, a, b) in diff {
debug += &format!("{}: {:02x?} != {:02x?}\n", i, a, b);
}
return Err(std::io::Error::new(std::io::ErrorKind::Other, debug).into());
}

Ok(())
}
}

fn main() -> Result<(), Error> {
let parsed = Args::parse();
let test_config = fs::read_to_string(Path::new(&parsed.test_config_file))?;
let test_config =fs::read_to_string(Path::new (&parsed.test_config_file))?;
let mut d1 = serde_json::Deserializer::from_str(&test_config);
let test_config = testutil::TestConfig::deserialize(&mut d1)?;
let mut ext_set = std::collections::HashMap::new();
Expand Down Expand Up @@ -332,11 +104,18 @@ fn main() -> Result<(), Error> {
}
}
}
let mut scheduler = TestScheduler::new();
let mut scheduler = testutil::TestScheduler::new();
for s in sched {
match scheduler.run_test_schedule(&s) {
Ok(()) => println!("test passed: {:?}", s.file.into_path() + &s.input.format_name),
Err(x) => println!("test failed: {:?} {:?}", s.file.into_path() + &s.input.format_name, x),
Ok(()) => println!(
"test passed: {:?}",
s.file.into_path() + &s.input.format_name
),
Err(x) => println!(
"test failed: {:?} {:?}",
s.file.into_path() + &s.input.format_name,
x
),
}
}
Ok(())
Expand Down
Loading

0 comments on commit 38b35c0

Please sign in to comment.