Add basic integration tests
Also add force option--necessary for testing but may be useful to some people who like to live dangerously
This commit is contained in:
parent
442b5ed947
commit
ee31774078
4 changed files with 164 additions and 10 deletions
30
src/lib.rs
30
src/lib.rs
|
|
@ -28,6 +28,7 @@ pub struct Options {
|
||||||
pub recursive: bool,
|
pub recursive: bool,
|
||||||
pub clobber: bool,
|
pub clobber: bool,
|
||||||
pub create: bool,
|
pub create: bool,
|
||||||
|
pub force: bool,
|
||||||
pub preserve_attrs: bool,
|
pub preserve_attrs: bool,
|
||||||
pub verbosity: Option<u8>,
|
pub verbosity: Option<u8>,
|
||||||
pub filter: HashSet<u8>,
|
pub filter: HashSet<u8>,
|
||||||
|
|
@ -129,7 +130,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
// TODO: Perform stripping
|
// TODO: Perform stripping
|
||||||
|
|
||||||
let output_data = png.output();
|
let output_data = png.output();
|
||||||
if file_original_size <= output_data.len() {
|
if file_original_size <= output_data.len() && !opts.force {
|
||||||
println!("File already optimized");
|
println!("File already optimized");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -174,13 +175,26 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!(" IDAT size = {} bytes ({} bytes decrease)",
|
if idat_original_size >= png.idat_data.len() {
|
||||||
png.idat_data.len(),
|
println!(" IDAT size = {} bytes ({} bytes decrease)",
|
||||||
idat_original_size - png.idat_data.len());
|
png.idat_data.len(),
|
||||||
println!(" file size = {} bytes ({} bytes = {:.2}% decrease)",
|
idat_original_size - png.idat_data.len());
|
||||||
output_data.len(),
|
} else {
|
||||||
file_original_size - output_data.len(),
|
println!(" IDAT size = {} bytes ({} bytes increate)",
|
||||||
(file_original_size - output_data.len()) as f64 / file_original_size as f64);
|
png.idat_data.len(),
|
||||||
|
png.idat_data.len() - idat_original_size);
|
||||||
|
}
|
||||||
|
if file_original_size >= output_data.len() {
|
||||||
|
println!(" file size = {} bytes ({} bytes = {:.2}% decrease)",
|
||||||
|
output_data.len(),
|
||||||
|
file_original_size - output_data.len(),
|
||||||
|
(file_original_size - output_data.len()) as f64 / file_original_size as f64);
|
||||||
|
} else {
|
||||||
|
println!(" file size = {} bytes ({} bytes = {:.2}% increase)",
|
||||||
|
output_data.len(),
|
||||||
|
output_data.len() - file_original_size,
|
||||||
|
(output_data.len() - file_original_size) as f64 / file_original_size as f64);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ fn main() {
|
||||||
fix_errors: false,
|
fix_errors: false,
|
||||||
clobber: true,
|
clobber: true,
|
||||||
create: true,
|
create: true,
|
||||||
|
force: false,
|
||||||
preserve_attrs: false,
|
preserve_attrs: false,
|
||||||
verbosity: Some(0),
|
verbosity: Some(0),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
|
|
@ -70,6 +71,10 @@ fn main() {
|
||||||
.help("Back up modified files")
|
.help("Back up modified files")
|
||||||
.short("b")
|
.short("b")
|
||||||
.long("backup"))
|
.long("backup"))
|
||||||
|
.arg(Arg::with_name("force")
|
||||||
|
.help("Write output even if larger than the original")
|
||||||
|
.short("F")
|
||||||
|
.long("force"))
|
||||||
.arg(Arg::with_name("recursive")
|
.arg(Arg::with_name("recursive")
|
||||||
.help("Recurse into subdirectories")
|
.help("Recurse into subdirectories")
|
||||||
.short("r")
|
.short("r")
|
||||||
|
|
@ -409,6 +414,10 @@ fn parse_opts_into_struct(matches: &ArgMatches, opts: &mut oxipng::Options) -> R
|
||||||
opts.backup = true;
|
opts.backup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches.is_present("force") {
|
||||||
|
opts.force = true;
|
||||||
|
}
|
||||||
|
|
||||||
if matches.is_present("recursive") {
|
if matches.is_present("recursive") {
|
||||||
opts.recursive = true;
|
opts.recursive = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use std::io::prelude::*;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,PartialEq)]
|
||||||
pub enum ColorType {
|
pub enum ColorType {
|
||||||
Grayscale,
|
Grayscale,
|
||||||
RGB,
|
RGB,
|
||||||
|
|
@ -43,7 +43,7 @@ impl ColorType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,PartialEq)]
|
||||||
pub enum BitDepth {
|
pub enum BitDepth {
|
||||||
One,
|
One,
|
||||||
Two,
|
Two,
|
||||||
|
|
|
||||||
131
tests/oxipng.rs
Normal file
131
tests/oxipng.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
extern crate oxipng;
|
||||||
|
|
||||||
|
use oxipng::png;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fs::remove_file;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn get_opts(input: &Path) -> oxipng::Options {
|
||||||
|
let mut filter = HashSet::new();
|
||||||
|
filter.insert(0);
|
||||||
|
filter.insert(5);
|
||||||
|
let mut compression = HashSet::new();
|
||||||
|
compression.insert(9);
|
||||||
|
let mut memory = HashSet::new();
|
||||||
|
memory.insert(9);
|
||||||
|
let mut strategies = HashSet::new();
|
||||||
|
for i in 0..4 {
|
||||||
|
strategies.insert(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
oxipng::Options {
|
||||||
|
backup: false,
|
||||||
|
out_file: input.with_extension("out.png").to_owned(),
|
||||||
|
out_dir: None,
|
||||||
|
stdout: false,
|
||||||
|
pretend: false,
|
||||||
|
recursive: false,
|
||||||
|
fix_errors: false,
|
||||||
|
force: true,
|
||||||
|
clobber: true,
|
||||||
|
create: true,
|
||||||
|
preserve_attrs: false,
|
||||||
|
verbosity: Some(0),
|
||||||
|
filter: filter,
|
||||||
|
interlace: None,
|
||||||
|
compression: compression,
|
||||||
|
memory: memory,
|
||||||
|
strategies: strategies,
|
||||||
|
window: 15,
|
||||||
|
bit_depth_reduction: true,
|
||||||
|
color_type_reduction: true,
|
||||||
|
palette_reduction: true,
|
||||||
|
idat_recoding: true,
|
||||||
|
strip: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reduce_rgba_png() {
|
||||||
|
let input = PathBuf::from("tests/files/test_rgba.png");
|
||||||
|
let opts = get_opts(&input);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
match oxipng::optimize(&input, &opts) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(x) => panic!(x.to_owned())
|
||||||
|
};
|
||||||
|
assert!(output.exists());
|
||||||
|
|
||||||
|
let png = match png::PngData::new(&output) {
|
||||||
|
Ok(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
x
|
||||||
|
},
|
||||||
|
Err(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
panic!(x.to_owned())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||||
|
assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight);
|
||||||
|
assert!(png.palette.unwrap().len() == 43 * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reduce_rgb_png() {
|
||||||
|
let input = PathBuf::from("tests/files/test_rgb.png");
|
||||||
|
let opts = get_opts(&input);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
match oxipng::optimize(&input, &opts) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(x) => panic!(x.to_owned())
|
||||||
|
};
|
||||||
|
assert!(output.exists());
|
||||||
|
|
||||||
|
let png = match png::PngData::new(&output) {
|
||||||
|
Ok(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
x
|
||||||
|
},
|
||||||
|
Err(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
panic!(x.to_owned())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||||
|
assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight);
|
||||||
|
assert!(png.palette.unwrap().len() == 43 * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reduce_palette_png() {
|
||||||
|
let input = PathBuf::from("tests/files/test_palette.png");
|
||||||
|
let opts = get_opts(&input);
|
||||||
|
let output = opts.out_file.clone();
|
||||||
|
|
||||||
|
match oxipng::optimize(&input, &opts) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(x) => panic!(x.to_owned())
|
||||||
|
};
|
||||||
|
assert!(output.exists());
|
||||||
|
|
||||||
|
let png = match png::PngData::new(&output) {
|
||||||
|
Ok(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
x
|
||||||
|
},
|
||||||
|
Err(x) => {
|
||||||
|
remove_file(output).ok();
|
||||||
|
panic!(x.to_owned())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(png.ihdr_data.color_type == png::ColorType::Indexed);
|
||||||
|
assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight);
|
||||||
|
assert!(png.palette.unwrap().len() == 43 * 3);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue