| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /*
- * Copyright 2017 Huy Cuong Nguyen
- * Copyright 2014 ZXing authors
- */
- // SPDX-License-Identifier: Apache-2.0
- #include "aztec/AZDecoder.h"
- #include "BitArray.h"
- #include "BitMatrixIO.h"
- #include "DecoderResult.h"
- #include "aztec/AZDetectorResult.h"
- #include "gtest/gtest.h"
- #include <string_view>
- #include <utility>
- namespace ZXing::Aztec {
- DecoderResult Decode(const BitArray& bits);
- }
- using namespace ZXing;
- // Shorthand to call Decode()
- static DecoderResult parse(BitMatrix&& bits, bool compact, int nbDatablocks, int nbLayers)
- {
- return Aztec::Decode({{std::move(bits), {}}, compact, nbDatablocks, nbLayers, false /*readerInit*/, false /*isMirrored*/, 0 /*runeValue*/});
- }
- TEST(AZDecoderTest, AztecResult)
- {
- auto bits = ParseBitMatrix(
- "X X X X X X X X X X X X X X \n"
- "X X X X X X X X X X X X X X X \n"
- " X X X X X X X X X X X X \n"
- " X X X X X X X X X X \n"
- " X X X X X X X X \n"
- " X X X X X X X X X X X X X X X X X X \n"
- " X X X X X X X X X \n"
- " X X X X X X X X X X X X X X X X X \n"
- " X X X X X X X X X \n"
- " X X X X X X X X X X X X X X X X \n"
- " X X X X X X X X X X X X \n"
- " X X X X X X X X X X X \n"
- " X X X X X X X X X X X X \n"
- " X X X X X X X X X X X X X X X X X \n"
- "X X X X X X X X X X X \n"
- " X X X X X X X X X X X X X X \n"
- " X X X X X X X X \n"
- " X X X X X X X X X X X X X X X X X X X \n"
- "X X X X X X X X X \n"
- "X X X X X X X X X X X X X X X \n"
- "X X X X X X X X X X X X \n"
- "X X X X X X X X X X X X X X \n"
- " X X X X X X X X X X X X X \n"
- , 'X', true);
- DecoderResult result = parse(std::move(bits), false, 30, 2);
- EXPECT_EQ(result.isValid(), true);
- EXPECT_EQ(result.text(), L"88888TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT");
- EXPECT_EQ(result.symbologyIdentifier(), "]z0");
- }
- TEST(AZDecoderTest, DecodeTooManyErrors)
- {
- auto bits = ParseBitMatrix(
- "X X . X . . . X X . . . X . . X X X . X . X X X X X . \n"
- "X X . . X X . . . . . X X . . . X X . . . X . X . . X \n"
- "X . . . X X . . X X X . X X . X X X X . X X . . X . . \n"
- ". . . . X . X X . . X X . X X . X . X X X X . X . . X \n"
- "X X X . . X X X X X . . . . . X X . . . X . X . X . X \n"
- "X X . . . . . . . . X . . . X . X X X . X . . X . . . \n"
- "X X . . X . . . . . X X . . . . . X . . . . X . . X X \n"
- ". . . X . X . X . . . . . X X X X X X . . . . . . X X \n"
- "X . . . X . X X X X X X . . X X X . X . X X X X X X . \n"
- "X . . X X X . X X X X X X X X X X X X X . . . X . X X \n"
- ". . . . X X . . . X . . . . . . . X X . . . X X . X . \n"
- ". . . X X X . . X X . X X X X X . X . . X . . . . . . \n"
- "X . . . . X . X . X . X . . . X . X . X X . X X . X X \n"
- "X . X . . X . X . X . X . X . X . X . . . . . X . X X \n"
- "X . X X X . . X . X . X . . . X . X . X X X . . . X X \n"
- "X X X X X X X X . X . X X X X X . X . X . X . X X X . \n"
- ". . . . . . . X . X . . . . . . . X X X X . . . X X X \n"
- "X X . . X . . X . X X X X X X X X X X X X X . . X . X \n"
- "X X X . X X X X . . X X X X . . X . . . . X . . X X X \n"
- ". . . . X . X X X . . . . X X X X . . X X X X . . . . \n"
- ". . X . . X . X . . . X . X X . X X . X . . . X . X . \n"
- "X X . . X . . X X X X X X X . . X . X X X X X X X . . \n"
- "X . X X . . X X . . . . . X . . . . . . X X . X X X . \n"
- "X . . X X . . X X . X . X . . . . X . X . . X . . X . \n"
- "X . X . X . . X . X X X X X X X X . X X X X . . X X . \n"
- "X X X X . . . X . . X X X . X X . . X . . . . X X X . \n"
- "X X . X . X . . . X . X . . . . X X . X . . X X . . . \n"
- , 'X', true);
- DecoderResult result = parse(std::move(bits), true, 16, 4);
- EXPECT_EQ(result.error(), Error::Checksum);
- }
- TEST(AZDecoderTest, DecodeTooManyErrors2)
- {
- auto bits = ParseBitMatrix(
- ". X X . . X . X X . . . X . . X X X . . . X X . X X . \n"
- "X X . X X . . X . . . X X . . . X X . X X X . X . X X \n"
- ". . . . X . . . X X X . X X . X X X X . X X . . X . . \n"
- "X . X X . . X . . . X X . X X . X . X X . . . . . X . \n"
- "X X . X . . X . X X . . . . . X X . . . . . X . . . X \n"
- "X . . X . . . . . . X . . . X . X X X X X X X . . . X \n"
- "X . . X X . . X . . X X . . . . . X . . . . . X X X . \n"
- ". . X X X X . X . . . . . X X X X X X . . . . . . X X \n"
- "X . . . X . X X X X X X . . X X X . X . X X X X X X . \n"
- "X . . X X X . X X X X X X X X X X X X X . . . X . X X \n"
- ". . . . X X . . . X . . . . . . . X X . . . X X . X . \n"
- ". . . X X X . . X X . X X X X X . X . . X . . . . . . \n"
- "X . . . . X . X . X . X . . . X . X . X X . X X . X X \n"
- "X . X . . X . X . X . X . X . X . X . . . . . X . X X \n"
- "X . X X X . . X . X . X . . . X . X . X X X . . . X X \n"
- "X X X X X X X X . X . X X X X X . X . X . X . X X X . \n"
- ". . . . . . . X . X . . . . . . . X X X X . . . X X X \n"
- "X X . . X . . X . X X X X X X X X X X X X X . . X . X \n"
- "X X X . X X X X . . X X X X . . X . . . . X . . X X X \n"
- ". . X X X X X . X . . . . X X X X . . X X X . X . X . \n"
- ". . X X . X . X . . . X . X X . X X . . . . X X . . . \n"
- "X . . . X . X . X X X X X X . . X . X X X X X . X . . \n"
- ". X . . . X X X . . . . . X . . . . . X X X X X . X . \n"
- "X . . X . X X X X . X . X . . . . X . X X . X . . X . \n"
- "X . . . X X . X . X X X X X X X X . X X X X . . X X . \n"
- ". X X X X . . X . . X X X . X X . . X . . . . X X X . \n"
- "X X . . . X X . . X . X . . . . X X . X . . X . X . X \n"
- , 'X', true);
- DecoderResult result = parse(std::move(bits), true, 16, 4);
- EXPECT_EQ(result.error(), Error::Checksum);
- }
- // Helper taking bit string to call GetEncodedData()
- static DecoderResult getData(std::string_view bitStr)
- {
- BitArray bits;
- for (auto b : bitStr)
- bits.appendBit(b == '1');
- return Aztec::Decode(bits);
- }
- TEST(AZDecoderTest, SymbologyIdentifier)
- {
- {
- // Plain
- auto data = getData("00010");
- EXPECT_EQ(data.symbologyIdentifier(), "]z0");
- EXPECT_EQ(data.text(), L"A");
- }
- {
- // GS1 ("PS FLGN(0) DL (20)01")
- auto data = getData("0000000000000111100100001000100011");
- EXPECT_EQ(data.symbologyIdentifier(), "]z1");
- EXPECT_EQ(data.text(), L"2001");
- }
- {
- // AIM ("A PS FLGN(0) B")
- auto data = getData("00010000000000000000011");
- EXPECT_EQ(data.symbologyIdentifier(), "]z2");
- EXPECT_EQ(data.text(), L"AB");
- }
- {
- // AIM ("DL 99 UL PS FLGN(0) B")
- auto data = getData("11110101110111110000000000000000011");
- EXPECT_EQ(data.symbologyIdentifier(), "]z2");
- EXPECT_EQ(data.text(), L"99B");
- }
- {
- // Structured Append ("UL ML A D A")
- auto data = getData("1110111101000100010100010");
- EXPECT_EQ(data.symbologyIdentifier(), "]z6");
- EXPECT_EQ(data.text(), L"A");
- EXPECT_EQ(data.structuredAppend().index, 0);
- EXPECT_EQ(data.structuredAppend().count, 4);
- }
- {
- // Structured Append with GS1 ("UL ML A D PS FLGN(0) DL (20)01")
- auto data = getData("111011110100010001010000000000000111100100001000100011");
- EXPECT_EQ(data.symbologyIdentifier(), "]z7");
- EXPECT_EQ(data.text(), L"2001");
- EXPECT_EQ(data.structuredAppend().index, 0);
- EXPECT_EQ(data.structuredAppend().count, 4);
- }
- {
- // Structured Append with AIM ("UL ML A D A PS FLGN(0) B")
- auto data = getData("1110111101000100010100010000000000000000011");
- EXPECT_EQ(data.symbologyIdentifier(), "]z8");
- EXPECT_EQ(data.text(), L"AB");
- EXPECT_EQ(data.structuredAppend().index, 0);
- EXPECT_EQ(data.structuredAppend().count, 4);
- }
- {
- // Plain with FNC1 not in first/second position ("A B PS FLGN(0) C")
- auto data = getData("0001000011000000000000000100");
- EXPECT_EQ(data.symbologyIdentifier(), "]z0");
- EXPECT_EQ(data.text(), L"AB\u001DC"); // "AB<GS>C"
- }
- {
- // Plain with FNC1 not in first/second position ("A B C PS FLGN(0) D")
- auto data = getData("000100001100100000000000000000101");
- EXPECT_EQ(data.symbologyIdentifier(), "]z0");
- EXPECT_EQ(data.text(), L"ABC\u001DD"); // "ABC<GS>D"
- }
- {
- // Plain with FNC1 not in first/second position ("DL 1 UL PS FLGN(0) A")
- auto data = getData("1111000111110000000000000000010");
- EXPECT_EQ(data.symbologyIdentifier(), "]z0");
- EXPECT_EQ(data.text(), L"1\u001DA"); // "1<GS>D"
- }
- }
- // Helper taking 5-bit word array to call GetEncodedData()
- static DecoderResult getData(const ByteArray& bytes)
- {
- BitArray bits; // 5-bit words (assuming no digits/binary)
- for (auto b : bytes)
- bits.appendBits(b, 5);
- return Aztec::Decode(bits);
- }
- // Shorthand to return Structured Append given 5-bit word array
- static StructuredAppendInfo sai(const ByteArray& bytes)
- {
- return getData(bytes).structuredAppend();
- }
- // Shorthand to return string result given 5-bit word array
- static std::wstring text(const ByteArray& bytes)
- {
- return getData(bytes).text();
- }
- TEST(AZDecoderTest, StructuredAppend)
- {
- // Null
- EXPECT_EQ(sai({2}).index, -1);
- EXPECT_EQ(sai({2}).count, -1);
- EXPECT_TRUE(sai({2}).id.empty());
- EXPECT_EQ(text({2}), L"A");
- // Example from ISO/IEC 24778:2008 Section 8
- EXPECT_EQ(sai({29, 29, 2, 5, 2}).index, 0); // AD
- EXPECT_EQ(sai({29, 29, 2, 5, 2}).count, 4);
- EXPECT_TRUE(sai({29, 29, 2, 5, 2}).id.empty());
- EXPECT_EQ(text({29, 29, 2, 5, 2}), L"A");
- EXPECT_EQ(sai({29, 29, 3, 5, 2}).index, 1); // BD
- EXPECT_EQ(sai({29, 29, 3, 5, 2}).count, 4);
- EXPECT_TRUE(sai({29, 29, 3, 5, 2}).id.empty());
- EXPECT_EQ(text({29, 29, 3, 5, 2}), L"A");
- EXPECT_EQ(sai({29, 29, 4, 5, 2}).index, 2); // CD
- EXPECT_EQ(sai({29, 29, 4, 5, 2}).count, 4);
- EXPECT_TRUE(sai({29, 29, 4, 5, 2}).id.empty());
- EXPECT_EQ(text({29, 29, 4, 5, 2}), L"A");
- EXPECT_EQ(sai({29, 29, 5, 5, 2}).index, 3); // DD
- EXPECT_EQ(sai({29, 29, 5, 5, 2}).count, 4);
- EXPECT_TRUE(sai({29, 29, 5, 5, 2}).id.empty());
- EXPECT_EQ(text({29, 29, 5, 5, 2}), L"A");
- // Sequencing field
- EXPECT_EQ(sai({29, 29, 2, 27, 2}).index, 0); // AZ
- EXPECT_EQ(sai({29, 29, 2, 27, 2}).count, 26);
- EXPECT_EQ(sai({29, 29, 14, 27, 2}).index, 12); // MZ
- EXPECT_EQ(sai({29, 29, 14, 27, 2}).count, 26);
- EXPECT_EQ(sai({29, 29, 27, 27, 2}).index, 25); // ZZ
- EXPECT_EQ(sai({29, 29, 27, 27, 2}).count, 26);
- // Id
- EXPECT_EQ(sai({29, 29, 1, 10, 5, 1, 2, 5, 2}).id, "ID");
- EXPECT_EQ(text({29, 29, 1, 10, 5, 1, 2, 5, 2}), L"A");
- // Invalid sequencing
- EXPECT_EQ(sai({29, 29, 2, 2, 2}).index, 0); // AA
- EXPECT_EQ(sai({29, 29, 2, 2, 2}).count, 0); // Count 1 so set to 0
- EXPECT_EQ(text({29, 29, 2, 2, 2}), L"A");
- EXPECT_EQ(sai({29, 29, 6, 5, 2}).index, 4); // ED
- EXPECT_EQ(sai({29, 29, 6, 5, 2}).count, 0); // Count 4 <= index 4 so set to 0
- EXPECT_EQ(text({29, 29, 6, 5, 2}), L"A");
- EXPECT_EQ(sai({29, 29, 1, 5, 2}).index, -1); // Index < 'A'
- EXPECT_EQ(sai({29, 29, 1, 5, 2}).count, -1);
- EXPECT_EQ(text({29, 29, 1, 5, 2}), L" DA"); // Bad sequencing left in result
- EXPECT_EQ(sai({29, 29, 28, 5, 2}).index, -1); // Index > 'Z' (LL)
- EXPECT_EQ(sai({29, 29, 28, 5, 2}).count, -1);
- EXPECT_EQ(text({29, 29, 28, 5, 2}), L"da");
- EXPECT_EQ(sai({29, 29, 2, 1, 2}).index, -1); // Count < 'A'
- EXPECT_EQ(sai({29, 29, 2, 1, 2}).count, -1);
- EXPECT_EQ(text({29, 29, 2, 1, 2}), L"A A");
- EXPECT_EQ(sai({29, 29, 2, 28, 2}).index, -1); // Count > 'Z'
- EXPECT_EQ(sai({29, 29, 2, 28, 2}).count, -1);
- EXPECT_EQ(text({29, 29, 2, 28, 2}), L"Aa");
- EXPECT_EQ(sai({29, 29, 2, 5}).index, -1); // Sequencing but no data
- EXPECT_EQ(sai({29, 29, 2, 5}).count, -1);
- EXPECT_EQ(text({29, 29, 2, 5}), L"AD");
- // Invalid Ids
- {
- auto data = getData({29, 29, 1, 10, 5, 2, 5, 2}); // No terminating space
- EXPECT_TRUE(data.structuredAppend().id.empty());
- EXPECT_EQ(data.structuredAppend().index, -1); // Not recognized as sequence
- EXPECT_EQ(data.structuredAppend().count, -1);
- EXPECT_EQ(data.text(), L" IDADA"); // Bad ID and sequencing left in result
- }
- {
- auto data = getData({29, 29, 1, 1, 2, 5, 2}); // Blank
- EXPECT_TRUE(data.structuredAppend().id.empty());
- EXPECT_EQ(data.structuredAppend().index, 0); // Recognized as sequence
- EXPECT_EQ(data.structuredAppend().count, 4);
- EXPECT_EQ(data.text(), L"A");
- }
- {
- auto data = getData({29, 29, 1, 10, 1, 5, 1, 2, 5, 2}); // Space in "I D"
- EXPECT_TRUE(data.structuredAppend().id.empty());
- EXPECT_EQ(data.structuredAppend().index, -1); // Not recognized as sequence as sequence count invalid (space)
- EXPECT_EQ(data.structuredAppend().count, -1);
- EXPECT_EQ(data.text(), L" I D ADA"); // Bad ID and sequencing left in result
- }
- {
- auto data = getData({29, 29, 1, 10, 1, 2, 5, 1, 2, 5, 2}); // "I AD" (happens to have valid sequencing at end)
- EXPECT_EQ(data.structuredAppend().id, "I");
- EXPECT_EQ(data.structuredAppend().index, 0);
- EXPECT_EQ(data.structuredAppend().count, 4);
- EXPECT_EQ(data.text(), L" ADA"); // Trailing space and "real" sequencing left in result
- }
- }
|