crapify 0.3.0

Deep-fry your images, and other crimes against pixels.
use std::path::Path;
use std::process::Command;

use image::{ImageBuffer, ImageReader, Rgb, RgbImage};
use tempfile::TempDir;

const FIXTURE_W: u32 = 16;
const FIXTURE_H: u32 = 16;

fn make_fixture(path: &Path) {
    let img: RgbImage = ImageBuffer::from_fn(FIXTURE_W, FIXTURE_H, |x, y| {
        Rgb([(x * 16) as u8, (y * 16) as u8, 128])
    });
    img.save(path).expect("write fixture");
}

fn run_crapify(args: &[&str]) -> std::process::ExitStatus {
    Command::new(env!("CARGO_BIN_EXE_crapify"))
        .args(args)
        .status()
        .expect("spawn crapify")
}

fn decode_dimensions(path: &Path) -> (u32, u32) {
    let img = ImageReader::open(path)
        .expect("open output")
        .with_guessed_format()
        .expect("guess output format")
        .decode()
        .expect("decode output");
    (img.width(), img.height())
}

#[test]
fn deep_fry_raw_preset_round_trips() {
    let dir = TempDir::new().expect("create tempdir");
    let input = dir.path().join("in.png");
    let output = dir.path().join("out.png");
    make_fixture(&input);

    let status = run_crapify(&[
        "deep-fry",
        "--preset",
        "raw",
        input.to_str().unwrap(),
        output.to_str().unwrap(),
    ]);

    assert!(status.success(), "crapify exited non-zero: {status:?}");
    assert!(output.exists(), "output file was not created");
    assert_eq!(decode_dimensions(&output), (FIXTURE_W, FIXTURE_H));
}

#[test]
fn palettecrush_geocities_preset_round_trips() {
    let dir = TempDir::new().expect("create tempdir");
    let input = dir.path().join("in.png");
    let output = dir.path().join("out.png");
    make_fixture(&input);

    let status = run_crapify(&[
        "palettecrush",
        "--preset",
        "geocities",
        input.to_str().unwrap(),
        output.to_str().unwrap(),
    ]);

    assert!(status.success(), "crapify exited non-zero: {status:?}");
    assert!(output.exists(), "output file was not created");
    assert_eq!(decode_dimensions(&output), (FIXTURE_W, FIXTURE_H));
}

#[test]
fn palettecrush_adaptive_colors_round_trips() {
    let dir = TempDir::new().expect("create tempdir");
    let input = dir.path().join("in.png");
    let output = dir.path().join("out.png");
    make_fixture(&input);

    let status = run_crapify(&[
        "palettecrush",
        "--colors",
        "4",
        "--dither",
        "none",
        input.to_str().unwrap(),
        output.to_str().unwrap(),
    ]);

    assert!(status.success(), "crapify exited non-zero: {status:?}");
    assert!(output.exists(), "output file was not created");
    assert_eq!(decode_dimensions(&output), (FIXTURE_W, FIXTURE_H));
}

fn run_xerox_smoke(extra_args: &[&str]) {
    let dir = TempDir::new().expect("create tempdir");
    let input = dir.path().join("in.png");
    let output = dir.path().join("out.png");
    make_fixture(&input);

    let input_s = input.to_str().unwrap();
    let output_s = output.to_str().unwrap();
    let mut args = vec!["xerox"];
    args.extend_from_slice(extra_args);
    args.push(input_s);
    args.push(output_s);

    let status = run_crapify(&args);

    assert!(status.success(), "crapify exited non-zero: {status:?}");
    assert!(output.exists(), "output file was not created");
    assert_eq!(decode_dimensions(&output), (FIXTURE_W, FIXTURE_H));
}

#[test]
fn xerox_decent_copy_preset_round_trips() {
    run_xerox_smoke(&["--preset", "decent-copy"]);
}

#[test]
fn xerox_staff_meeting_handout_preset_round_trips() {
    run_xerox_smoke(&["--preset", "staff-meeting-handout"]);
}

#[test]
fn xerox_fax_with_end_of_chain_threshold_round_trips() {
    run_xerox_smoke(&[
        "--preset",
        "from-a-fax-from-1994",
        "--threshold-timing",
        "end-of-chain",
    ]);
}

#[test]
fn xerox_from_a_fax_from_1994_preset_round_trips() {
    run_xerox_smoke(&["--preset", "from-a-fax-from-1994"]);
}

#[test]
fn format_override_writes_png_to_bin_extension() {
    let dir = TempDir::new().expect("create tempdir");
    let input = dir.path().join("in.png");
    let output = dir.path().join("out.bin");
    make_fixture(&input);

    let status = run_crapify(&[
        "--format",
        "png",
        "deep-fry",
        "--preset",
        "raw",
        input.to_str().unwrap(),
        output.to_str().unwrap(),
    ]);

    assert!(status.success(), "crapify exited non-zero: {status:?}");
    assert!(output.exists(), "output file was not created");
    assert_eq!(decode_dimensions(&output), (FIXTURE_W, FIXTURE_H));
}