use anyd::codes::microqr::{
MicroEcLevel, MicroMask, MicroQrDecoder, MicroQrEncoder, MicroQrMeta, MicroVersion,
};
use anyd::output::{BitMatrix, Encoding};
use anyd::segment::Segment;
use anyd::symbol::{Symbol, SymbolMeta};
use anyd::symbology::Symbology;
use anyd::traits::{Decode, Encode};
fn meta(v: MicroVersion, ec: MicroEcLevel, mask: u8) -> MicroQrMeta {
MicroQrMeta {
version: v,
ec_level: ec,
mask: MicroMask::new(mask).unwrap(),
}
}
fn symbol(seg: Segment, m: MicroQrMeta) -> Symbol {
Symbol::new(Symbology::MicroQrCode, vec![seg], SymbolMeta::MicroQr(m))
}
fn assert_matrix(enc: &Encoding, size: usize, flat: &str) {
let Encoding::Matrix(m) = enc else {
panic!("expected a matrix encoding");
};
assert_eq!(m.width(), size);
assert_eq!(m.height(), size);
let bytes = flat.as_bytes();
assert_eq!(bytes.len(), size * size, "reference length mismatch");
for y in 0..size {
for x in 0..size {
let want = bytes[y * size + x] == b'1';
assert_eq!(m.get(x, y), want, "module ({x},{y}) differs from reference");
}
}
}
fn check(seg: Segment, m: MicroQrMeta, size: usize, flat: &str) {
let sym = symbol(seg, m.clone());
let enc = MicroQrEncoder::new().encode(&sym).unwrap();
assert_matrix(&enc, size, flat);
let decoded = MicroQrDecoder::new().decode(&enc).unwrap();
assert_eq!(
decoded.segments, sym.segments,
"segments differ after decode"
);
assert_eq!(decoded.meta, sym.meta, "meta differs after decode");
let re = MicroQrEncoder::new().encode(&decoded).unwrap();
assert_eq!(re, enc, "re-encoding is not byte-identical");
}
#[test]
fn m1_numeric() {
check(
Segment::numeric(b"12345".to_vec()),
meta(MicroVersion::M1, MicroEcLevel::Detection, 2),
11,
"1111111010110000010110101110101001011101000010111010111100000100111111111010000000000011110011100110101000110011110000011",
);
}
#[test]
fn m2_numeric_l() {
check(
Segment::numeric(b"0123456789".to_vec()),
meta(MicroVersion::M2, MicroEcLevel::L, 1),
13,
"1111111010101100000101110110111010001011011101000111101110101000010000010111011111111000111000000000110011010000101010100011010001111100001111001010000111001101010010111",
);
}
#[test]
fn m2_alphanumeric_m() {
check(
Segment::alphanumeric(b"ABCDE".to_vec()),
meta(MicroVersion::M2, MicroEcLevel::M, 0),
13,
"1111111010101100000101100110111010111101011101001100101110100101010000010101011111111000010000000000101011100111101100000110011111110110101001101000101111101001000111100",
);
check(
Segment::alphanumeric(b"HELLO".to_vec()),
meta(MicroVersion::M2, MicroEcLevel::M, 3),
13,
"1111111010101100000100100010111010111111011101001101101110101001010000010010011111111001111000000001100011101000100110101110111111101010100001101010101011101110010001110",
);
}
#[test]
fn m3_byte_l() {
check(
Segment::byte(b"Hello!".to_vec()),
meta(MicroVersion::M3, MicroEcLevel::L, 3),
15,
"111111101010101100000101111011101110100010101101110100101100101110100001001100000100100001111111101011100000000000111110111110010010000000010110100111100010001000111010010100100101100011100010100001010110110010111110011011101",
);
}
#[test]
fn m3_numeric_m() {
check(
Segment::numeric(b"987654".to_vec()),
meta(MicroVersion::M3, MicroEcLevel::M, 1),
15,
"111111101010101100000101010111101110100101011101110100111010101110101010111100000100010101111111101101011000000001101010100000111000100001011001000110100100110111011010011100111010101111011000100010100001000111110110110111000",
);
check(
Segment::alphanumeric(b"AB12".to_vec()),
meta(MicroVersion::M3, MicroEcLevel::M, 2),
15,
"111111101010101100000100000100101110100100101101110100110100101110100111101100000101101100111111101001110000000000000110100011001110100000001001010100100010100101110010101101011100110001111111111011001101000101110000011110100",
);
}
#[test]
fn m4_byte_l() {
check(
Segment::byte(b"Test 123".to_vec()),
meta(MicroVersion::M4, MicroEcLevel::L, 0),
17,
"1111111010101010110000010100001011101110100010000111011101010001101110111010001001101100000101110000101111111011011100100000000011000001100101110111111010110100010000000010000001001110001011100011000000101011000000111010101110101111110000101110101101100100110000011111000011010111111111001",
);
}
#[test]
fn m4_byte_m() {
check(
Segment::byte(b"hello world".to_vec()),
meta(MicroVersion::M4, MicroEcLevel::M, 2),
17,
"1111111010101010110000010000010100101110101111111001011101010100100010111010010010100100000100011110111111111011100100100000000100000001101011110111101010111011010100010111101011110110011000001001110000001000011101001110100111011101100111111111000101001100001011001100110111111011111011000",
);
}
#[test]
fn m4_alphanumeric_q() {
check(
Segment::alphanumeric(b"ABC123".to_vec()),
meta(MicroVersion::M4, MicroEcLevel::Q, 0),
17,
"1111111010101010110000010110110000101110101110010011011101000010111110111010001111001100000100010001001111111011100111000000000101110001101101001111111100010101010100001010101110110001110011001111010100111100101100100111101101100101101101101001001011110010111111001010110110010100101111111",
);
check(
Segment::numeric(b"999".to_vec()),
meta(MicroVersion::M4, MicroEcLevel::Q, 1),
17,
"1111111010101010110000010011100000101110100110101111011101010111111110111010000011100100000101110011001111111000001101100000000101110010101100011100101110011001101011000010111110010100100010101100100011001110111101101111101111001011011110111111010110110110011000000011001111011010101100100",
);
}
fn roundtrip(segments: Vec<Segment>, level: MicroEcLevel) {
let enc = MicroQrEncoder::new();
let sym = enc.build(segments, level).unwrap();
let encoding = enc.encode(&sym).unwrap();
let decoded = MicroQrDecoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.segments, sym.segments);
assert_eq!(decoded.meta, sym.meta);
assert_eq!(enc.encode(&decoded).unwrap(), encoding);
}
#[test]
fn builder_roundtrips() {
roundtrip(
vec![Segment::numeric(b"12345".to_vec())],
MicroEcLevel::Detection,
);
roundtrip(vec![Segment::numeric(b"98765".to_vec())], MicroEcLevel::L);
roundtrip(
vec![Segment::alphanumeric(b"HELLO WORLD".to_vec())],
MicroEcLevel::M,
);
roundtrip(vec![Segment::byte(b"anydcode".to_vec())], MicroEcLevel::M);
roundtrip(
vec![Segment::byte("héllo".as_bytes().to_vec())],
MicroEcLevel::L,
);
roundtrip(vec![Segment::numeric(b"5".to_vec())], MicroEcLevel::L);
roundtrip(vec![Segment::numeric(b"42".to_vec())], MicroEcLevel::M);
roundtrip(
vec![
Segment::alphanumeric(b"AB".to_vec()),
Segment::numeric(b"12345".to_vec()),
],
MicroEcLevel::M,
);
}
#[test]
fn survives_correctable_error_m4() {
let enc = MicroQrEncoder::new();
let sym = enc
.build(
vec![Segment::alphanumeric(b"OK123".to_vec())],
MicroEcLevel::Q,
)
.unwrap();
let Encoding::Matrix(mut m) = enc.encode(&sym).unwrap() else {
panic!("expected matrix");
};
let v = m.get(10, 10);
m.set(10, 10, !v);
let decoded = MicroQrDecoder::new().decode(&Encoding::Matrix(m)).unwrap();
assert_eq!(decoded.segments, sym.segments);
}
#[test]
fn rejects_non_microqr_symbol() {
let sym = Symbol::new(
Symbology::QrCode,
vec![Segment::numeric(b"1".to_vec())],
SymbolMeta::Generic,
);
assert!(MicroQrEncoder::new().encode(&sym).is_err());
}
#[test]
fn wrong_size_matrix_is_undecodable() {
let m = BitMatrix::new(12, 12, 2);
assert!(MicroQrDecoder::new().decode(&Encoding::Matrix(m)).is_err());
}