// Author: cirakg & claude ai // Summary: Tests for the Data Matrix encoder: ppf.datamatrix references (ASCII), Base256 and upper-shift decoding dumps, limits. import Toybox.Lang; import Toybox.Test; (:test) module MatrixFixtures { function packedHex(m as Codes.Matrix) as String { var bits = m.modules; var out = new [(bits.size() + 7) / 8]b; for (var i = 0; i < bits.size(); i++) { if (bits[i] == 1) { out[i / 8] = out[i / 8] | (0x80 >> (i % 8)); } } return Bytes.toHex(out); } // A line tools/check_codes.py decodes with zxing-cpp. function dump(logger as Test.Logger, symbology as Number, text as String, m as Codes.Matrix) as Void { logger.debug("MATRIXDUMP " + symbology + " " + m.width + " " + m.height + " " + m.rowHeight + " " + m.quiet + " " + Bytes.toHex(Bytes.fromUtf8(text)) + " " + packedHex(m)); } } // README 12.4 check 1: identical to ppf.datamatrix in ASCII mode, from // 10 x 10 to 144 x 144 (multi-region and multi-block sizes). (:test) function dataMatrixMatchesReference(logger as Test.Logger) as Boolean { var rows = CodeVectors.DATAMATRIX; for (var i = 0; i < rows.size(); i++) { var row = rows[i] as Array; var payload = row[0] as String; var result = DataMatrix.encode(payload); if (result instanceof Codes.CodeError) { logger.error("row " + i + ": " + result.message); return false; } var expectedSize = row[1] as Number; var expected = row[2] as String; var got = MatrixFixtures.packedHex(result); if (result.width != expectedSize || !got.equals(expected)) { logger.error("row " + i + " '" + Text.clip(payload, 20) + "': size " + result.width + ", reference " + expectedSize); return false; } } logger.debug("CODES datamatrix reference rows=" + rows.size()); return true; } // README 12.4 check 2: Base256 (UTF-8), Upper Shift and plain payloads, // decoded by tools/check_codes.py. (:test) function dataMatrixDumpForDecoding(logger as Test.Logger) as Boolean { var payloads = ["Zürich", "Ünïcödé 😀 fake card 0042", "é", "1234567890", "Member: fake-0042, valid 2027-12-31", "ÄÖÜäöüß" + "ÄÖÜäöüß" + "ÄÖÜäöüß", "abc"]; var long = ""; for (var i = 0; i < 30; i++) { long += "é0123456789"; } payloads.add(long); var xs = ""; for (var i = 0; i < 50; i++) { xs += "xxxxxxxxxx"; } payloads.add(xs); // 88 x 88: 4 x 4 regions, 4 interleaved blocks var digits = ""; for (var i = 0; i < 110; i++) { digits += "0123456789"; } payloads.add(digits); // 88 x 88 in digit pairs, comparable with zint for (var i = 0; i < payloads.size(); i++) { var text = payloads[i] as String; var result = DataMatrix.encode(text); if (result instanceof Codes.CodeError) { logger.error("'" + text + "': " + result.message); return false; } MatrixFixtures.dump(logger, Codes.DATAMATRIX, text, result); } return true; } (:test) function dataMatrixLimits(logger as Test.Logger) as Boolean { var big = ""; for (var i = 0; i < 160; i++) { big += "abcdefghij"; } // (Separate checks: one long && chain of instanceof tests made the // compiler run out of memory.) if (!(DataMatrix.encode(big) instanceof Codes.CodeError)) { logger.error("too long text was accepted"); return false; } if (!(DataMatrix.encode("") instanceof Codes.CodeError)) { logger.error("empty text was accepted"); return false; } // Base256 is chosen when cheaper than Upper Shift for every byte. var b256 = DataMatrix.codewords(Bytes.fromUtf8("ÄÖÜ")); if (b256[0] != DataMatrix.BASE256_LATCH) { logger.error("Base256 not chosen"); return false; } var ascii = DataMatrix.codewords(Bytes.fromUtf8("A1234")); return ascii.size() == 3 && ascii[0] == 66 && ascii[1] == 142; }