diff --git a/.gitignore b/.gitignore index 2ad53959..d17bcb23 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,158 @@ report.html /screenshot/* sample/index.html from-json.html +### Generated by gibo (https://github.com/simonwhitaker/gibo) +### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Node.gitignore + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + + +### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +wasi-sdk-21.0 +m.md + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..9bcceb87 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +resolver = "2" +members = [ + "core", +] \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..3fa94b11 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "core" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image-diff-rs = { git = "https://github.com/bokuweb/image-diff-rs.git" } +globmatch = "0.3" +rayon = "1.8" + +[dev-dependencies] +rstest = "0.18.2" \ No newline at end of file diff --git a/core/examples/simple.rs b/core/examples/simple.rs new file mode 100644 index 00000000..365085c0 --- /dev/null +++ b/core/examples/simple.rs @@ -0,0 +1,5 @@ +use core::run; + +pub fn main() { + run("./sample/actual/", "./sample/expected") +} diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..9bcb36ef --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,120 @@ +// type RegParams = { +// actualDir: string, +// expectedDir: string, +// diffDir: string, +// report?: string, +// junitReport?: string, +// json?: string, +// update?: boolean, +// extendedErrors?: boolean, +// urlPrefix?: string, +// matchingThreshold?: number, +// threshold?: number, // alias to thresholdRate. +// thresholdRate?: number, +// thresholdPixel?: number, +// concurrency?: number, +// enableAntialias?: boolean, +// enableClientAdditionalDetection?: boolean, +// }; +// fn main() { +// // let a = include_bytes!("../../sample/actual/sample.png"); +// // let b = include_bytes!("../../sample/expected/sample.png"); +// // image_diff_rs::diff( +// // a, +// // b, +// // &image_diff_rs::DiffOption { +// // threshold: Some(0.1), +// // include_anti_alias: Some(true), +// // }, +// // ) +// // .unwrap(); +// // crate::run(); +// } +// + +use image_diff_rs::DiffOption; +use rayon::prelude::*; +use std::{ + collections::BTreeSet, + path::{Path, PathBuf}, +}; + +static IMAGE_FILES: &str = "/**/*.{tiff,jpeg,jpg,gif,png,bmp,webp}"; + +#[derive(Debug)] +pub(crate) struct DetectedImages { + pub(crate) expected: BTreeSet, + pub(crate) actual: BTreeSet, + pub(crate) deleted: BTreeSet, + pub(crate) new: BTreeSet, +} + +pub fn run(expected_dir: impl AsRef, actual_dir: impl AsRef) { + let actual_dir = actual_dir.as_ref().to_owned(); + let expected_dir = expected_dir.as_ref().to_owned(); + let detected = find_images(&expected_dir, &actual_dir); + + let targets: Vec = detected + .actual + .intersection(&detected.expected) + .cloned() + .collect(); + + let results: Result, std::io::Error> = targets + .par_iter() + .map(|path| { + let img1 = std::fs::read(actual_dir.clone().join(path))?; + let img2 = std::fs::read(expected_dir.clone().join(path))?; + let res = image_diff_rs::diff( + img1, + img2, + &DiffOption { + threshold: Some(0.05), + include_anti_alias: Some(true), + }, + ); + std::fs::write("./test.png", res.unwrap().diff_image)?; + Ok(()) + }) + .inspect(|r| if let Err(e) = r { /*TODO: logging */ }) + .collect(); +} + +pub(crate) fn find_images( + expected_dir: impl AsRef, + actual_dir: impl AsRef, +) -> DetectedImages { + let expected_dir = expected_dir.as_ref(); + let actual_dir = actual_dir.as_ref(); + + let expected: BTreeSet = + globmatch::Builder::new(&(expected_dir.display().to_string() + IMAGE_FILES)) + .build(".") + .expect("the pattern should be correct.") + .into_iter() + .flatten() + .map(|p| p.strip_prefix(expected_dir).unwrap().to_path_buf()) + .collect(); + + let actual: BTreeSet = + globmatch::Builder::new(&(actual_dir.display().to_string() + IMAGE_FILES)) + .build(".") + .expect("the pattern should be correct.") + .into_iter() + .flatten() + .map(|p| p.strip_prefix(actual_dir).unwrap().to_path_buf()) + .collect(); + + dbg!(&expected); + + let deleted = expected.difference(&actual).cloned().collect(); + + let new = actual.difference(&expected).cloned().collect(); + + DetectedImages { + expected, + actual, + deleted, + new, + } +} diff --git a/core/src/main.rs b/core/src/main.rs new file mode 100644 index 00000000..365085c0 --- /dev/null +++ b/core/src/main.rs @@ -0,0 +1,5 @@ +use core::run; + +pub fn main() { + run("./sample/actual/", "./sample/expected") +}