mirror of
https://github.com/Rust-GPU/rust-gpu.git
synced 2026-06-04 16:49:51 +09:00
difftest structure refactor
* one `#[test]` per difftest * in any crate * just evaluate all sides within the test * allows sharing structs between tests * allows diffing structs instead of bytes
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -926,6 +926,14 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "difftests"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dissimilar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difftests-old-bin"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"difftest-runner",
|
||||
|
||||
@@ -33,6 +33,7 @@ members = [
|
||||
"tests/difftests/bin",
|
||||
"tests/difftests/runner",
|
||||
"tests/difftests/types",
|
||||
"tests/difftests/difftests",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "difftests"
|
||||
name = "difftests-old-bin"
|
||||
version = "0.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
15
tests/difftests/difftests/Cargo.toml
Normal file
15
tests/difftests/difftests/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "difftests"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
dissimilar = "1.0.11"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
68
tests/difftests/difftests/src/fmt.rs
Normal file
68
tests/difftests/difftests/src/fmt.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::fmt::{Formatter, Write};
|
||||
|
||||
type Result = std::fmt::Result;
|
||||
|
||||
pub trait Diff {
|
||||
fn fmt(f: &mut Formatter<'_>, pad: Padding, a: Self, b: Self) -> Result;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Padding {
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
impl Padding {
|
||||
pub fn new(pad: u32) -> Self {
|
||||
Self { pad }
|
||||
}
|
||||
|
||||
pub fn pad(&self, fmt: &mut Formatter<'_>) -> Result {
|
||||
for _ in 0..self.pad {
|
||||
fmt.write_char(' ')?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add(&self, pad: u32) -> Padding {
|
||||
Padding {
|
||||
pad: self.pad + pad,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const PADDING_TAB: u32 = 4;
|
||||
|
||||
pub struct StructDiff<'a, 'b: 'a> {
|
||||
fmt: &'b mut Formatter<'a>,
|
||||
pad: Padding,
|
||||
result: Result,
|
||||
has_fields: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> StructDiff<'a, 'b> {
|
||||
pub fn new(fmt: &'b mut Formatter<'a>, pad: Padding, name: &str) -> Self {
|
||||
Self {
|
||||
result: write!(fmt, "{name} {{"),
|
||||
fmt,
|
||||
pad,
|
||||
has_fields: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field<T: Diff>(&mut self, name: &str, value_a: &T, value_b: &T) -> &mut self {
|
||||
self.result = self.result.and_then(|_| {
|
||||
self.pad.add(PADDING_TAB).pad(&mut self.fmt)?;
|
||||
write!(self.fmt, "{name}: ")?;
|
||||
T::fmt(&mut self.fmt, self.pad, value_a, value_b)?;
|
||||
write!(self.fmt, ",\n")
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finish(&mut self) -> Result {
|
||||
self.result.and_then(|_| {
|
||||
self.pad.pad(&mut self.fmt)?;
|
||||
write!(self.fmt, "}}\n")
|
||||
})
|
||||
}
|
||||
}
|
||||
68
tests/difftests/difftests/src/lib.rs
Normal file
68
tests/difftests/difftests/src/lib.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
mod side;
|
||||
mod fmt;
|
||||
|
||||
pub use side::*;
|
||||
pub use fmt::*;
|
||||
|
||||
/// A [`Side`] of a difftest
|
||||
pub struct Side<'a, T> {
|
||||
pub name: &'a str,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl<'a, T> Side<'a, T> {
|
||||
pub fn new(name: &'a str, t: T) -> Self {
|
||||
Self { name, value: t }
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a difftest between these different [`Side`]s, panics on mismatch
|
||||
pub fn difftest<T: Debug>(sides: &[Side<'_, T>]) {
|
||||
difftest_anyhow(sides).unwrap()
|
||||
}
|
||||
|
||||
/// Run a difftest between these different [`Side`]s, returns an [`anyhow::Result`] instead of panic-ing
|
||||
pub fn difftest_anyhow<T: Debug>(sides: &[Side<'_, T>]) -> anyhow::Result<()> {
|
||||
if sides.len() < 2 {
|
||||
anyhow::bail!("No trails compared, expected at least two trails");
|
||||
}
|
||||
let results = sides
|
||||
.into_iter()
|
||||
.map(|t| Side::new(t.name, format!("{:#?}", t.value)))
|
||||
.collect::<Vec<_>>();
|
||||
let reference = &results.first().unwrap().value;
|
||||
if results.iter().skip(1).all(|t| &t.value == reference) {
|
||||
// all trails are equal to each other
|
||||
Ok(())
|
||||
} else {
|
||||
// there was a mismatch between trails
|
||||
difftest_report(&results)
|
||||
}
|
||||
}
|
||||
|
||||
/// Report a difference in any amount of trails
|
||||
#[cold]
|
||||
fn difftest_report(sides: &[Side<'_, String>]) -> anyhow::Result<()> {
|
||||
let mut hashed = HashMap::with_capacity(sides.len());
|
||||
for (i, side) in sides.iter().enumerate() {
|
||||
hashed
|
||||
.entry(side.value.as_str())
|
||||
.or_insert(Vec::new())
|
||||
.push(i);
|
||||
}
|
||||
|
||||
let mut groups = hashed.into_iter().collect::<Vec<_>>();
|
||||
assert!(groups.len() >= 2);
|
||||
// sort with the most common group first
|
||||
// if there are multiple groups that are as common as the rest, sort them by *something* stable
|
||||
groups.sort_by(|a, b| a.1.len().cmp(&b.1.len()).then(a.0.cmp(b.0)));
|
||||
|
||||
// the most common group serves as the reference
|
||||
let reference_ids = groups[0].1.as_slice();
|
||||
let reference = &sides[reference_ids[0]];
|
||||
|
||||
anyhow::bail!(r#"Difftest failed!"#)
|
||||
}
|
||||
50
tests/difftests/difftests/src/side.rs
Normal file
50
tests/difftests/difftests/src/side.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::fmt::Formatter;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SideConfig {
|
||||
pub float_epsilon: f32,
|
||||
}
|
||||
|
||||
pub trait SideValue {
|
||||
fn check_equality(&self, other: &Self, config: &SideConfig) -> Result<(), >;
|
||||
|
||||
fn report_error(&self, other: &Self, config: &SideConfig) -> anyhow::Result<String>;
|
||||
}
|
||||
|
||||
impl SideValue for &str {
|
||||
fn check_equality(&self, other: &Self, config: &SideConfig) -> anyhow::Result<bool> {
|
||||
self == other
|
||||
}
|
||||
|
||||
fn report_error(&self, other: &Self, config: &SideConfig) -> anyhow::Result<String> {
|
||||
let diff = dissimilar::diff(self, other);
|
||||
diff.iter()
|
||||
.map(|chunk| match chunk {
|
||||
dissimilar::Chunk::Equal(text) => Cow::Borrowed(text),
|
||||
dissimilar::Chunk::Delete(text) => {
|
||||
Cow::Owned(format!("\x1b[4m\x1b[31m{}\x1b[0m", text))
|
||||
}
|
||||
dissimilar::Chunk::Insert(text) => {
|
||||
Cow::Owned(format!("\x1b[4m\x1b[32m{}\x1b[0m", text))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl SideValue for u32 {
|
||||
fn check_equality(&self, other: &Self, _: &SideConfig) -> Result<(), String> {
|
||||
if self == other {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiffFormatter<'a> {
|
||||
pub f: Formatter<'a>,
|
||||
has_diff: bool,
|
||||
}
|
||||
|
||||
impl DiffFormatter {
|
||||
pub fn
|
||||
}
|
||||
Reference in New Issue
Block a user