#![cfg(all(feature = "qr", feature = "png"))]
use smart_package_tracker::symbology::{Ecc, Qr, QrVersion};
use smart_package_tracker::{Barcode, Length, RenderOptions};
fn render_greyscale(barcode: &Barcode, options: &RenderOptions) -> (usize, usize, Vec<u8>) {
let png = barcode.to_png(options).expect("png renders");
let mut reader = png::Decoder::new(std::io::Cursor::new(&png[..]))
.read_info()
.expect("our own png is valid");
let mut buf = vec![0; reader.output_buffer_size().unwrap()];
let info = reader.next_frame(&mut buf).expect("valid frame");
let luma = buf[..info.buffer_size()]
.chunks_exact(4)
.map(|px| px[0])
.collect();
(info.width as usize, info.height as usize, luma)
}
fn decode(barcode: &Barcode, options: &RenderOptions) -> String {
let (w, h, luma) = render_greyscale(barcode, options);
let mut img = rqrr::PreparedImage::prepare_from_greyscale(w, h, |x, y| luma[y * w + x]);
let grids = img.detect_grids();
assert_eq!(
grids.len(),
1,
"expected exactly one QR grid, found {}",
grids.len()
);
let (_meta, content) = grids[0].decode().expect("grid decodes");
content
}
fn options() -> RenderOptions {
RenderOptions::builder()
.module_width(Length::Px(8.0))
.human_readable(false)
.build()
.unwrap()
}
#[test]
fn rendered_qr_is_readable_by_an_independent_decoder() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
assert_eq!(decode(&barcode, &options()), "PKG-9ED9285C");
}
#[test]
fn survives_every_error_correction_level() {
for ecc in [Ecc::Low, Ecc::Medium, Ecc::Quartile, Ecc::High] {
let barcode = Barcode::qr_with(Qr::new().ecc(ecc), "PKG-9ED9285C").unwrap();
assert_eq!(decode(&barcode, &options()), "PKG-9ED9285C", "ecc {ecc:?}");
}
}
#[test]
fn survives_a_pinned_version() {
let barcode = Barcode::qr_with(Qr::new().version(QrVersion::Fixed(6)), "PKG-9ED9285C").unwrap();
assert_eq!(barcode.symbol().modules().width(), 41);
assert_eq!(decode(&barcode, &options()), "PKG-9ED9285C");
}
#[test]
fn round_trips_realistic_payloads() {
for payload in [
"PKG-9ED9285C",
"PKG-8520D28455EB13A9D",
"https://track.example.com/PKG-9ED9285C",
"1Z999AA10123456784",
] {
let barcode = Barcode::qr(payload).unwrap();
assert_eq!(
decode(&barcode, &options()),
payload,
"failed for {payload}"
);
}
}
#[test]
fn round_trips_non_latin1_payloads() {
let payload = "PKG-\u{4e2d}\u{6587}-\u{1f4e6}";
let barcode = Barcode::qr(payload).unwrap();
assert_eq!(decode(&barcode, &options()), payload);
}
#[test]
fn human_readable_text_does_not_break_detection() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
let with_text = RenderOptions::builder()
.module_width(Length::Px(8.0))
.human_readable(true)
.build()
.unwrap();
assert_eq!(decode(&barcode, &with_text), "PKG-9ED9285C");
}
#[test]
fn the_quiet_zone_is_actually_blank_on_all_four_sides() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
let options = options();
let layout = options.layout(barcode.symbol()).unwrap();
let (w, h, luma) = render_greyscale(&barcode, &options);
let quiet = layout.quiet_x_px as usize;
assert!(quiet > 0);
for y in 0..h {
for x in 0..w {
let in_quiet_zone = x < quiet || y < quiet || x >= w - quiet || y >= h - quiet;
if in_quiet_zone {
assert_eq!(
luma[y * w + x],
255,
"quiet zone pixel at ({x}, {y}) is not blank"
);
}
}
}
}
#[test]
fn qr_layout_is_square_and_quiet_zoned() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
let options = options();
let layout = options.layout(barcode.symbol()).unwrap();
assert_eq!(layout.symbol_w_px, layout.symbol_h_px, "QR must be square");
assert_eq!(layout.quiet_y_px, layout.quiet_x_px);
assert_eq!(layout.quiet_x_px, 4 * layout.module_px);
assert_eq!(layout.width_px, layout.symbol_w_px + 2 * layout.quiet_x_px);
assert_eq!(layout.height_px, layout.symbol_h_px + 2 * layout.quiet_y_px);
}
#[test]
#[cfg(feature = "svg")]
fn svg_and_png_agree_for_matrix_symbols() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
let options = options();
let layout = options.layout(barcode.symbol()).unwrap();
let svg = barcode.to_svg(&options).unwrap();
assert!(svg.contains(&format!(
"viewBox=\"0 0 {} {}\"",
layout.width_px, layout.height_px
)));
assert!(svg.contains(&format!("height=\"{}\"/>", layout.module_px)));
}
#[test]
fn long_payload_text_widens_the_image_instead_of_overflowing() {
let payload = "PKG-8520D28455EB13A9D-0123456789";
let barcode = Barcode::qr(payload).unwrap();
let options = RenderOptions::builder()
.module_width(Length::Px(1.0))
.human_readable(true)
.build()
.unwrap();
let layout = options.layout(barcode.symbol()).unwrap();
let text_w = (payload.chars().count() as u32 * 6 - 1) * layout.hri_scale;
assert!(
text_w > layout.symbol_w_px,
"test needs text wider than the symbol"
);
assert!(
layout.hri_x_px + text_w <= layout.width_px,
"HRI text runs past the right edge: x={} + w={} > {}",
layout.hri_x_px,
text_w,
layout.width_px
);
assert_eq!(
layout.symbol_x_px + layout.symbol_w_px + layout.symbol_x_px,
layout.width_px
);
}
#[test]
fn human_readable_text_stays_out_of_the_quiet_zone() {
let barcode = Barcode::qr("PKG-9ED9285C").unwrap();
let options = RenderOptions::builder()
.module_width(Length::Px(8.0))
.human_readable(true)
.build()
.unwrap();
let layout = options.layout(barcode.symbol()).unwrap();
let (w, h, luma) = render_greyscale(&barcode, &options);
let quiet = layout.quiet_x_px;
assert_eq!(quiet, 4 * layout.module_px);
let left = layout.symbol_x_px - quiet;
let top = layout.symbol_y_px - quiet;
let right = layout.symbol_x_px + layout.symbol_w_px + quiet;
let bottom = layout.symbol_y_px + layout.symbol_h_px + quiet;
assert!(
right as usize <= w && bottom as usize <= h,
"quiet zone is clipped by the image"
);
for y in top..bottom {
for x in left..right {
let inside_symbol = x >= layout.symbol_x_px
&& x < layout.symbol_x_px + layout.symbol_w_px
&& y >= layout.symbol_y_px
&& y < layout.symbol_y_px + layout.symbol_h_px;
if !inside_symbol {
assert_eq!(
luma[(y as usize) * w + (x as usize)],
255,
"quiet zone pixel at ({x}, {y}) is not blank"
);
}
}
}
let text_rows = (bottom as usize..h).any(|y| (0..w).any(|x| luma[y * w + x] < 128));
assert!(text_rows, "no human-readable text was drawn");
}