AZHighLevelEncoderTest.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * Copyright 2017 Huy Cuong Nguyen
  3. * Copyright 2013 ZXing authors
  4. */
  5. // SPDX-License-Identifier: Apache-2.0
  6. #include "aztec/AZHighLevelEncoder.h"
  7. #include "BitArray.h"
  8. #include "BitArrayUtility.h"
  9. #include "DecoderResult.h"
  10. #include "StructuredAppend.h"
  11. #include "gtest/gtest.h"
  12. #include <algorithm>
  13. namespace ZXing::Aztec {
  14. DecoderResult Decode(const BitArray& bits);
  15. }
  16. using namespace ZXing;
  17. namespace {
  18. std::string StripSpaces(std::string str) {
  19. #ifdef __cpp_lib_erase_if
  20. std::erase_if(str, isspace);
  21. #else
  22. str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
  23. #endif
  24. return str;
  25. }
  26. void TestHighLevelEncodeString(const std::string& s, const std::string& expectedBits) {
  27. BitArray bits = Aztec::HighLevelEncoder::Encode(s);
  28. EXPECT_EQ(Utility::ToString(bits), StripSpaces(expectedBits)) << "highLevelEncode() failed for input string: " + s;
  29. EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().bytes);
  30. }
  31. void TestHighLevelEncodeString(const std::string& s, int expectedReceivedBits) {
  32. BitArray bits = Aztec::HighLevelEncoder::Encode(s);
  33. int receivedBitCount = Size(Utility::ToString(bits));
  34. EXPECT_EQ(receivedBitCount, expectedReceivedBits) << "highLevelEncode() failed for input string: " + s;
  35. EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().bytes);
  36. }
  37. }
  38. TEST(AZHighLevelEncoderTest, HighLevelEncode)
  39. {
  40. TestHighLevelEncodeString("A. b.",
  41. // 'A' P/S '. ' L/L b D/L '.'
  42. "...X. ..... ...XX XXX.. ...XX XXXX. XX.X");
  43. TestHighLevelEncodeString("Lorem ipsum.",
  44. // 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.'
  45. ".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X");
  46. TestHighLevelEncodeString("Lo. Test 123.",
  47. // 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.'
  48. ".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X");
  49. TestHighLevelEncodeString("Lo...x",
  50. // 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x'
  51. ".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X");
  52. TestHighLevelEncodeString(". x://abc/.",
  53. //P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.'
  54. "..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X");
  55. // Uses Binary/Shift rather than Lower/Shift to save two bits.
  56. TestHighLevelEncodeString("ABCdEFG",
  57. //'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
  58. "...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...");
  59. TestHighLevelEncodeString(
  60. // Found on an airline boarding pass. Several stretches of Binary shift are
  61. // necessary to keep the bitcount so low.
  62. "09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"
  63. "Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
  64. 823);
  65. }
  66. TEST(AZHighLevelEncoderTest, HighLevelEncodeBinary)
  67. {
  68. // binary short form single byte
  69. TestHighLevelEncodeString(std::string("N\0N", 3),
  70. // 'N' B/S =1 '\0' N
  71. ".XXXX XXXXX ....X ........ .XXXX"); // Encode "N" in UPPER
  72. TestHighLevelEncodeString(std::string("N\0n", 3),
  73. // 'N' B/S =2 '\0' 'n'
  74. ".XXXX XXXXX ...X. ........ .XX.XXX."); // Encode "n" in BINARY
  75. // binary short form consecutive bytes
  76. TestHighLevelEncodeString(std::string("N\0\x80 A", 5),
  77. // 'N' B/S =2 '\0' \u0080 ' ' 'A'
  78. ".XXXX XXXXX ...X. ........ X....... ....X ...X.");
  79. // binary skipping over single character
  80. TestHighLevelEncodeString(std::string("\0a\xff\x80 A", 6),
  81. // B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A'
  82. "XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X.");
  83. // getting into binary mode from digit mode
  84. TestHighLevelEncodeString(std::string("1234\0", 5),
  85. //D/L '1' '2' '3' '4' U/L B/S =1 \0
  86. "XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........"
  87. );
  88. // Create a string in which every character requires binary
  89. std::string sb;
  90. sb.reserve(3000);
  91. for (int i = 0; i <= 3000; i++) {
  92. sb.push_back((char)(128 + (i % 30)));
  93. }
  94. // Test the output generated by Binary/Switch, particularly near the
  95. // places where the encoding changes: 31, 62, and 2047+31=2078
  96. for (int i : { 1, 2, 3, 10, 29, 30, 31, 32, 33, 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) {
  97. // This is the expected length of a binary string of length "i"
  98. int expectedLength = (8 * i) +
  99. ((i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31);
  100. // Verify that we are correct about the length.
  101. TestHighLevelEncodeString(sb.substr(0, i), expectedLength);
  102. if (i != 1 && i != 32 && i != 2079) {
  103. // The addition of an 'a' at the beginning or end gets merged into the binary code
  104. // in those cases where adding another binary character only adds 8 or 9 bits to the result.
  105. // So we exclude the border cases i=1,32,2079
  106. // A lower case letter at the beginning will be merged into binary mode
  107. TestHighLevelEncodeString('a' + sb.substr(0, i - 1), expectedLength);
  108. // A lower case letter at the end will also be merged into binary mode
  109. TestHighLevelEncodeString(sb.substr(0, i - 1) + 'a', expectedLength);
  110. }
  111. // A lower case letter at both ends will enough to latch us into LOWER.
  112. TestHighLevelEncodeString('a' + sb.substr(0, i) + 'b', expectedLength + 15);
  113. }
  114. sb.clear();
  115. for (int i = 0; i < 32; i++) {
  116. sb.push_back('\xA7'); // § forces binary encoding
  117. }
  118. sb[1] = 'A';
  119. // expect B/S(1) A B/S(30)
  120. TestHighLevelEncodeString(sb, 5 + 20 + 31 * 8);
  121. sb.clear();
  122. for (int i = 0; i < 31; i++) {
  123. sb.push_back('\xA7');
  124. }
  125. sb[1] = 'A';
  126. // expect B/S(31)
  127. TestHighLevelEncodeString(sb, 10 + 31 * 8);
  128. sb.clear();
  129. for (int i = 0; i < 34; i++) {
  130. sb.push_back('\xA7');
  131. }
  132. sb[1] = 'A';
  133. // expect B/S(31) B/S(3)
  134. TestHighLevelEncodeString(sb, 20 + 34 * 8);
  135. sb.clear();
  136. for (int i = 0; i < 64; i++) {
  137. sb.push_back('\xA7');
  138. }
  139. sb[30] = 'A';
  140. // expect B/S(64)
  141. TestHighLevelEncodeString(sb, 21 + 64 * 8);
  142. }
  143. TEST(AZHighLevelEncoderTest, HighLevelEncodePairs)
  144. {
  145. // Typical usage
  146. TestHighLevelEncodeString("ABC. DEF\r\n",
  147. // A B C P/S .<sp> D E F P/S \r\n
  148. "...X. ...XX ..X.. ..... ...XX ..X.X ..XX. ..XXX ..... ...X.");
  149. // We should latch to PUNCT mode, rather than shift. Also check all pairs
  150. TestHighLevelEncodeString("A. : , \r\n",
  151. // 'A' M/L P/L ". " ": " ", " "\r\n"
  152. "...X. XXX.X XXXX. ...XX ..X.X ..X.. ...X.");
  153. // Latch to DIGIT rather than shift to PUNCT
  154. TestHighLevelEncodeString("A. 1234",
  155. // 'A' D/L '.' ' ' '1' '2' '3' '4'
  156. "...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X."
  157. );
  158. // Don't bother leaving Binary Shift.
  159. TestHighLevelEncodeString("A\200. \200",
  160. // 'A' B/S =2 \200 "." " " \200
  161. "...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X.......");
  162. }