// Author: cirakg & claude ai // Summary: Tests for the linear barcode encoders: zxing-cpp reference rows, validation messages, and a dump for decoding. import Toybox.Lang; import Toybox.Test; (:test) module LinearFixtures { function moduleText(m as ByteArray) as String { var s = ""; for (var i = 0; i < m.size(); i++) { s += m[i] == 1 ? "1" : "0"; } return s; } // The error message for payload, or "" when it encodes. function errorOf(symbology as Number, payload as String, options as Number) as String { var r = Codes.encode(symbology, payload, options); return r instanceof Codes.CodeError ? r.message : ""; } function opts(check as Number) as Number { return Codes.OPT_SHOW_TEXT | (check << Codes.OPT_CHECK_SHIFT) | 1; } } // README 12.4 check 1: identical modules to zxing-cpp for every row. (:test) function linearCodesMatchTheReference(logger as Test.Logger) as Boolean { var rows = CodeVectors.LINEAR; for (var i = 0; i < rows.size(); i++) { var row = rows[i] as Array; var symbology = row[0] as Number; var payload = row[1] as String; var result = Codes.encode(symbology, payload, row[2] as Number); if (result instanceof Codes.CodeError) { logger.error("row " + i + " (" + Codes.name(symbology) + " '" + payload + "'): " + result.message); return false; } var got = LinearFixtures.moduleText(result.modules); var expected = row[3] as String; if (symbology == Codes.CODE128) { // zxing-cpp 3 writes Code 128 through zint, whose optimal encoder // breaks ties between equally short encodings differently (for // example set C for a final digit pair). Both are optimal: the // length must match, and the code is decoded by check_codes.py. logger.debug("CODEDUMP " + symbology + " " + Bytes.toHex(Bytes.fromUtf8(result.text)) + " " + got); if (got.length() != expected.length()) { logger.error("row " + i + " (Code 128 '" + payload + "') has " + got.length() + " modules, zxing-cpp " + expected.length()); return false; } continue; } if (!got.equals(expected)) { logger.error("row " + i + " (" + Codes.name(symbology) + " '" + payload + "') differs:\n got " + got + "\n exp " + expected); return false; } } logger.debug("CODES reference rows=" + rows.size()); return true; } (:test) function linearCheckDigitModes(logger as Test.Logger) as Boolean { var add = LinearFixtures.opts(Codes.CHECK_ADD_VERIFY); var fix = LinearFixtures.opts(Codes.CHECK_FIX); var off = LinearFixtures.opts(Codes.CHECK_OFF); var added = Codes.encode(Codes.EAN13, "590123412345", add) as Codes.LinearCode; var fixed = Codes.encode(Codes.EAN13, "5901234123450", fix) as Codes.LinearCode; var kept = Codes.encode(Codes.EAN13, "5901234123450", off) as Codes.LinearCode; var wrong = LinearFixtures.errorOf(Codes.EAN13, "5901234123450", add); var offShort = LinearFixtures.errorOf(Codes.EAN13, "590123412345", off); return added.text.equals("5901234123457") && added.note != null && fixed.text.equals("5901234123457") && (fixed.note as String).find("was 0") != null && kept.text.equals("5901234123450") && wrong.find("check digit should be 7, got 0") != null && wrong.find("Fix if wrong") != null && offShort.find("needs all 13 digits") != null; } (:test) function linearValidationMessages(logger as Test.Logger) as Boolean { var o = LinearFixtures.opts(Codes.CHECK_ADD_VERIFY); var off = LinearFixtures.opts(Codes.CHECK_OFF); var cases = [ [Codes.EAN13, "59012341234a", o, "digits only"], [Codes.EAN13, "123", o, "needs 12 or 13 digits"], [Codes.UPCA, "1234", o, "UPC-A needs 11 or 12"], [Codes.EAN8, "12345678", o, "check digit should be"], [Codes.UPCE, "2123456", o, "number system must be 0 or 1"], [Codes.UPCE, "0502553", o, "not a valid zero suppression"], [Codes.UPCE, "12345", o, "6, 7 or 8 digits"], [Codes.ITF, "123", off, "even number of digits"], [Codes.ITF, "1234", o, "odd number of digits"], [Codes.CODE39, "abc", o, "isn't allowed"], [Codes.CODE128, "Ünï", o, "printable ASCII"], [Codes.CODE128, "", o, "needs some text"], [Codes.CODABAR, "A123", o, "both ends or at neither"], [Codes.CODABAR, "A1B2A", o, "isn't allowed there"], [Codes.QR, "hello", o, "not supported yet"] ]; for (var i = 0; i < cases.size(); i++) { var c = cases[i] as Array; var message = LinearFixtures.errorOf(c[0] as Number, c[1] as String, c[2] as Number); if (message.find(c[3] as String) == null) { logger.error("case " + i + ": got '" + message + "'"); return false; } } var codabar = Codes.encode(Codes.CODABAR, "40156", o) as Codes.LinearCode; return codabar.text.equals("A40156A") && codabar.note != null; } // README 12.4 check 2: dumps encoder outputs for seeded random payloads; // tools/check_codes.py decodes them with zxing-cpp after the test run. (:test) function linearDumpForDecoding(logger as Test.Logger) as Boolean { var seed = 12345; var count = 0; var symbologies = [Codes.EAN13, Codes.EAN8, Codes.UPCA, Codes.UPCE, Codes.CODE128, Codes.CODE39, Codes.ITF, Codes.CODABAR]; var o = LinearFixtures.opts(Codes.CHECK_ADD_VERIFY); for (var i = 0; i < 160; i++) { var symbology = symbologies[i % symbologies.size()] as Number; var length = [12, 7, 11, 7, 10, 8, 10, 8][i % symbologies.size()] as Number; var alphabet = ["0123456789", "0123456789", "0123456789", "0123456789", "0123456789 AZaz!#$%&()*+,-./:;<=>?@", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%", "0123456789", "0123456789-$:/.+"][i % symbologies.size()] as String; var text = ""; for (var k = 0; k < length; k++) { seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF; var at = (seed >> 8) % alphabet.length(); text += alphabet.substring(at, at + 1); } if (symbology == Codes.UPCE) { text = "0" + text.substring(1, 7); } var result = Codes.encode(symbology, text, o); if (result instanceof Codes.CodeError) { continue; // invalid random UPC-E suppressions are expected } logger.debug("CODEDUMP " + symbology + " " + Bytes.toHex(Bytes.fromUtf8(result.text)) + " " + LinearFixtures.moduleText(result.modules)); count++; } return count > 120; }