# Author: cirakg & claude ai # Summary: Tests for the barcode reference tools: zxing-cpp rows, rendering, decoding, check digits, generator, checker. from __future__ import annotations import random from pathlib import Path import barcode_ref as ref import check_codes import gen_code_vectors import gen_qr_tables ROOT = Path(__file__).resolve().parents[2] def test_reference_rows_have_the_standard_lengths() -> None: assert ref.reference_modules(ref.EAN13, "590123412345") == ( "10100010110100111011001100100110111101001110101010110011011011001000010101110010011101000100101" ) assert len(ref.reference_modules(ref.EAN8, "1234567")) == 67 assert len(ref.reference_modules(ref.UPCE, "0425261")) == 51 def test_rendered_codes_decode() -> None: modules = ref.reference_modules(ref.CODE39, "ABC") for scale in (1, 2, 3): assert ref.decode(ref.render_linear(modules, scale), ref.CODE39) == ["ABC"] def test_check_digits() -> None: assert ref.gs1_check("590123412345") == 7 assert ref.gs1_check("1234567") == 0 assert ref.code39_check("CODE 39") == "R" # (12+24+13+14+38+3+9) % 43 = 27 def test_expected_reading_normalises_upc() -> None: assert ref.expected_reading(ref.UPCA, "012345678905") == "0012345678905" assert ref.upce_expand("01234565") == "0012345000065" def test_generator_only_makes_canonical_upce() -> None: rng = random.Random(5) for _ in range(200): d = gen_code_vectors.valid_upce(rng) ref.reference_modules(ref.UPCE, "0" + d) # zxing-cpp rejects non-canonical ones def test_committed_vectors_match_the_generator() -> None: rows = gen_code_vectors.cases(random.Random(1212), 25) committed = (ROOT / "test" / "data" / "CodeVectors.mc").read_text(encoding="utf-8") assert committed == gen_code_vectors.render(rows, 1212) def test_check_codes_reads_dump_lines(tmp_path: Path) -> None: good = ref.reference_modules(ref.EAN8, "1234567") log = tmp_path / "dev.log" log.write_text( f"CODEDUMP {ref.EAN8} {b'12345670'.hex()} {good}\nCODEDUMP {ref.EAN8} {b'12345670'.hex()} {good[:-5]}00000\n", encoding="utf-8", ) checked, failures = check_codes.check_log(log) assert checked == 2 and len(failures) == 3 # the damaged code fails at all three sizes assert check_codes.main([str(log)]) == 1 empty = tmp_path / "empty.log" empty.write_text("nothing\n", encoding="utf-8") assert check_codes.main([str(empty)]) == 2 def test_committed_qr_tables_match_segno() -> None: committed = (ROOT / "source" / "codes" / "QrTables.mc").read_text(encoding="utf-8") assert committed == gen_qr_tables.render(gen_qr_tables.table()) rows = gen_qr_tables.table() assert rows[0] == [7, 1, 19, 0, 0] # version 1-L: 7 EC codewords, one block of 19 assert len(gen_qr_tables.alignment()) == 40 and gen_qr_tables.alignment()[0] == [] def test_segno_padding_quirk_detection() -> None: # Byte mode always ends on a codeword boundary after the terminator, so # segno would add a zero byte unless the symbol is exactly full. assert gen_code_vectors.segno_pads_extra_byte("hello", 0, 1) assert not gen_code_vectors.segno_pads_extra_byte("01234567", 1, 1) def test_rendered_qr_matrix_decodes() -> None: _, _, hexbits = gen_code_vectors.qr_reference("HELLO WORLD", 1, 1, 2) image = ref.render_matrix(bytes.fromhex(hexbits), 21, 2, quiet=4) assert ref.decode(image, ref.QR) == ["HELLO WORLD"]