From 2db02dffee805fa6e3beb6dde943cc905d1805e4 Mon Sep 17 00:00:00 2001 From: favoritas37 Date: Mon, 1 Aug 2016 18:36:01 +0300 Subject: [PATCH] Further enhance tests for QRcode encoder --- src/zxing/zxing/qrcode/encoder/QREncoder.cpp | 10 +- tests/src/QZXingTests/EncodeValidator.cpp | 2 +- .../zxing/qrcode/encoder/EncoderTests.cpp | 303 ++++++++++++++---- .../zxing/qrcode/encoder/EncoderTests.h | 12 + 4 files changed, 266 insertions(+), 61 deletions(-) diff --git a/src/zxing/zxing/qrcode/encoder/QREncoder.cpp b/src/zxing/zxing/qrcode/encoder/QREncoder.cpp index 9e7cfa1..0ab30a1 100644 --- a/src/zxing/zxing/qrcode/encoder/QREncoder.cpp +++ b/src/zxing/zxing/qrcode/encoder/QREncoder.cpp @@ -351,8 +351,14 @@ BitArray* Encoder::interleaveWithECBytes(const BitArray& bits, { // "bits" must have "getNumDataBytes" bytes of data. - if (bits.getSizeInBytes() != numDataBytes) - throw WriterException("Number of bits and data bytes does not match"); + if (bits.getSizeInBytes() != numDataBytes) { + QString message("Encoder::interleaveWithECBytes: Number of bits ["); + message += QString::number(bits.getSizeInBytes()); + message += "] and data bytes ["; + message += QString::number(numDataBytes); + message += "] does not match"; + throw WriterException( message.toStdString().c_str() ); + } // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll // store the divided data bytes blocks and error correction bytes blocks into "blocks". diff --git a/tests/src/QZXingTests/EncodeValidator.cpp b/tests/src/QZXingTests/EncodeValidator.cpp index bd9544b..93bd827 100644 --- a/tests/src/QZXingTests/EncodeValidator.cpp +++ b/tests/src/QZXingTests/EncodeValidator.cpp @@ -36,7 +36,7 @@ void EncodeValidator::execute() EncoderTests t4; t4.execute(); } catch(zxing::Exception &e) { - qDebug() << e.what(); + qDebug() << "Exception: " << e.what(); } } diff --git a/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.cpp b/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.cpp index cb0654c..814dbbd 100644 --- a/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.cpp +++ b/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.cpp @@ -1,5 +1,4 @@ #include "EncoderTests.h" -#include namespace zxing{ namespace qrcode{ @@ -18,102 +17,107 @@ void EncoderTests::execute() testAppendModeInfo(); testAppendLengthInfo(); testAppendBytes(); + testTerminateBits(); + testGetNumDataBytesAndNumECBytesForBlockID(); + testInterleaveWithECBytes(); + testAppendNumericBytes(); + testAppendAlphanumericBytes(); } void EncoderTests::testGetAlphanumericCode() { // The first ten code points are numbers. for (int i = 0; i < 10; ++i) { - assertEquals(i, Encoder::getAlphanumericCode('0' + i)); + assertEquals(i, EncoderHack::getAlphanumericCode('0' + i)); } // The next 26 code points are capital alphabet letters. for (int i = 10; i < 36; ++i) { - assertEquals(i, Encoder::getAlphanumericCode('A' + i - 10)); + assertEquals(i, EncoderHack::getAlphanumericCode('A' + i - 10)); } // Others are symbol letters - assertEquals(36, Encoder::getAlphanumericCode(' ')); - assertEquals(37, Encoder::getAlphanumericCode('$')); - assertEquals(38, Encoder::getAlphanumericCode('%')); - assertEquals(39, Encoder::getAlphanumericCode('*')); - assertEquals(40, Encoder::getAlphanumericCode('+')); - assertEquals(41, Encoder::getAlphanumericCode('-')); - assertEquals(42, Encoder::getAlphanumericCode('.')); - assertEquals(43, Encoder::getAlphanumericCode('/')); - assertEquals(44, Encoder::getAlphanumericCode(':')); + assertEquals(36, EncoderHack::getAlphanumericCode(' ')); + assertEquals(37, EncoderHack::getAlphanumericCode('$')); + assertEquals(38, EncoderHack::getAlphanumericCode('%')); + assertEquals(39, EncoderHack::getAlphanumericCode('*')); + assertEquals(40, EncoderHack::getAlphanumericCode('+')); + assertEquals(41, EncoderHack::getAlphanumericCode('-')); + assertEquals(42, EncoderHack::getAlphanumericCode('.')); + assertEquals(43, EncoderHack::getAlphanumericCode('/')); + assertEquals(44, EncoderHack::getAlphanumericCode(':')); // Should return -1 for other letters; - assertEquals(-1, Encoder::getAlphanumericCode('a')); - assertEquals(-1, Encoder::getAlphanumericCode('#')); - assertEquals(-1, Encoder::getAlphanumericCode('\0')); + assertEquals(-1, EncoderHack::getAlphanumericCode('a')); + assertEquals(-1, EncoderHack::getAlphanumericCode('#')); + assertEquals(-1, EncoderHack::getAlphanumericCode('\0')); } void EncoderTests::testChooseMode() { // Numeric mode. - Mode mode_(Encoder::chooseMode("0")); + Mode mode_(EncoderHack::chooseMode("0")); assertSame(Mode::NUMERIC, mode_); - mode_ = Encoder::chooseMode("0123456789"); + mode_ = EncoderHack::chooseMode("0123456789"); assertSame(Mode::NUMERIC, mode_); // Alphanumeric mode. - mode_ = Encoder::chooseMode("A"); + mode_ = EncoderHack::chooseMode("A"); assertSame(Mode::ALPHANUMERIC, mode_); - mode_ = Encoder::chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"); + mode_ = EncoderHack::chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"); assertSame(Mode::ALPHANUMERIC, mode_); // 8-bit byte mode. - mode_ = Encoder::chooseMode("a"); + mode_ = EncoderHack::chooseMode("a"); assertSame(Mode::BYTE, mode_); - mode_ = Encoder::chooseMode("#"); + mode_ = EncoderHack::chooseMode("#"); assertSame(Mode::BYTE, mode_); - mode_ = Encoder::chooseMode(""); + mode_ = EncoderHack::chooseMode(""); assertSame(Mode::BYTE, mode_); // Kanji mode. We used to use MODE_KANJI for these, but we stopped // doing that as we cannot distinguish Shift_JIS from other encodings // from data bytes alone. See also comments in qrcode_encoder.h. // AIUE in Hiragana in Shift_JIS - // mode_ = Encoder::chooseMode(shiftJISString(new byte[]{0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, (byte) 0xa6})); + // mode_ = EncoderHack::chooseMode(shiftJISString(new byte[]{0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, (byte) 0xa6})); // assertSame(Mode::BYTE,mode_); // // Nihon in Kanji in Shift_JIS. - // mode_ = Encoder::chooseMode(shiftJISString(new byte[]{0x9, 0xf, 0x9, 0x7b})); + // mode_ = EncoderHack::chooseMode(shiftJISString(new byte[]{0x9, 0xf, 0x9, 0x7b})); // assertSame(Mode::BYTE, mode_); // // Sou-Utsu-Byou in Kanji in Shift_JIS. - // mode_ = Encoder::chooseMode(shiftJISString(new byte[]{0xe, 0x4, 0x9, 0x5, 0x9, 0x61})); + // mode_ = EncoderHack::chooseMode(shiftJISString(new byte[]{0xe, 0x4, 0x9, 0x5, 0x9, 0x61})); // assertSame(Mode::BYTE, mode_); } void EncoderTests::testAppendModeInfo() { BitArray bits; - Encoder::appendModeInfo(Mode::NUMERIC, bits); - assertEquals(std::string(" ...X"), bits.toString()); + EncoderHack::appendModeInfo(Mode::NUMERIC, bits); + assertEquals(" ...X", bits.toString()); } void EncoderTests::testAppendLengthInfo() { BitArray bits; - Encoder::appendLengthInfo(1, // 1 letter (1/1). - *Version::getVersionForNumber(1), - Mode::NUMERIC, - bits); - assertEquals(std::string(" ........ .X"), bits.toString()); // 10 bits. + EncoderHack::appendLengthInfo(1, // 1 letter (1/1). + *Version::getVersionForNumber(1), + Mode::NUMERIC, + bits); + assertEquals(" ........ .X", bits.toString()); // 10 bits. bits = BitArray(); - Encoder::appendLengthInfo(2, // 2 letters (2/1). - *Version::getVersionForNumber(10), - Mode::ALPHANUMERIC, - bits); - assertEquals(std::string(" ........ .X."), bits.toString()); // 11 bits. + EncoderHack::appendLengthInfo(2, // 2 letters (2/1). + *Version::getVersionForNumber(10), + Mode::ALPHANUMERIC, + bits); + assertEquals(" ........ .X.", bits.toString()); // 11 bits. bits = BitArray(); - Encoder::appendLengthInfo(255, // 255 letter (255/1). - *Version::getVersionForNumber(27), - Mode::BYTE, - bits); - assertEquals(std::string(" ........ XXXXXXXX"), bits.toString()); // 16 bits. + EncoderHack::appendLengthInfo(255, // 255 letter (255/1). + *Version::getVersionForNumber(27), + Mode::BYTE, + bits); + assertEquals(" ........ XXXXXXXX", bits.toString()); // 16 bits. // bits = BitArray(); - // Encoder::appendLengthInfo(512, // 512 letters (1024/2). + // EncoderHack::appendLengthInfo(512, // 512 letters (1024/2). // *Version::getVersionForNumber(40), // Mode::KANJI, // bits); @@ -125,31 +129,214 @@ void EncoderTests::testAppendBytes() // Should use appendNumericBytes. // 1 = 01 = 0001 in 4 bits. BitArray bits; - Encoder::appendBytes("1", Mode::NUMERIC, bits, Encoder::DEFAULT_BYTE_MODE_ENCODING); - assertEquals(std::string(" ...X") , bits.toString()); + EncoderHack::appendBytes("1", Mode::NUMERIC, bits, EncoderHack::DEFAULT_BYTE_MODE_ENCODING); + assertEquals(" ...X" , bits.toString()); // Should use appendAlphanumericBytes. // A = 10 = 0xa = 001010 in 6 bits bits = BitArray(); - Encoder::appendBytes("A", Mode::ALPHANUMERIC, bits, Encoder::DEFAULT_BYTE_MODE_ENCODING); - assertEquals(std::string(" ..X.X.") , bits.toString()); + EncoderHack::appendBytes("A", Mode::ALPHANUMERIC, bits, EncoderHack::DEFAULT_BYTE_MODE_ENCODING); + assertEquals(" ..X.X." , bits.toString()); // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC. - try { - Encoder::appendBytes("a", Mode::ALPHANUMERIC, bits, Encoder::DEFAULT_BYTE_MODE_ENCODING); - } catch(zxing::Exception &/*e*/) { - // good - } + ASSERT_THROWS( + EncoderHack::appendBytes("a", Mode::ALPHANUMERIC, bits, EncoderHack::DEFAULT_BYTE_MODE_ENCODING); + ) // Should use append8BitBytes. // 0x61, 0x62, 0x63 bits = BitArray(); - Encoder::appendBytes("abc", Mode::BYTE, bits, Encoder::DEFAULT_BYTE_MODE_ENCODING); - assertEquals(std::string(" .XX....X .XX...X. .XX...XX"), bits.toString()); + EncoderHack::appendBytes("abc", Mode::BYTE, bits, EncoderHack::DEFAULT_BYTE_MODE_ENCODING); + assertEquals(" .XX....X .XX...X. .XX...XX", bits.toString()); // Anything can be encoded in QRCode.MODE_8BIT_BYTE. - Encoder::appendBytes("\0", Mode::BYTE, bits, Encoder::DEFAULT_BYTE_MODE_ENCODING); + EncoderHack::appendBytes("\0", Mode::BYTE, bits, EncoderHack::DEFAULT_BYTE_MODE_ENCODING); // Should use appendKanjiBytes. // 0x93, 0x5f -// bits = new BitArray(); -// Encoder.appendBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), Mode.KANJI, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); -// assertEquals(" .XX.XX.. XXXXX", bits.toString()); + // bits = new BitArray(); + // Encoder.appendBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), Mode.KANJI, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); + // assertEquals(" .XX.XX.. XXXXX", bits.toString()); +} + +void EncoderTests::testTerminateBits() +{ + BitArray v; + EncoderHack::terminateBits(0, v); + assertEquals("", v.toString()); + v = BitArray(); + EncoderHack::terminateBits(1, v); + assertEquals(" ........", v.toString()); + v = BitArray(); + v.appendBits(0, 3); // Append 000 + EncoderHack::terminateBits(1, v); + assertEquals(" ........", v.toString()); + v = BitArray(); + v.appendBits(0, 5); // Append 00000 + EncoderHack::terminateBits(1, v); + assertEquals(" ........", v.toString()); + v = BitArray(); + v.appendBits(0, 8); // Append 00000000 + EncoderHack::terminateBits(1, v); + assertEquals(" ........", v.toString()); + v = BitArray(); + EncoderHack::terminateBits(2, v); + assertEquals(" ........ XXX.XX..", v.toString()); + v = BitArray(); + v.appendBits(0, 1); // Append 0 + EncoderHack::terminateBits(3, v); + assertEquals(" ........ XXX.XX.. ...X...X", v.toString()); +} + +void EncoderTests::testGetNumDataBytesAndNumECBytesForBlockID() +{ + std::vector numDataBytes; + std::vector numEcBytes; + // Version 1-H. + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(26, 9, 1, 0, numDataBytes, numEcBytes); + assertEquals(9, numDataBytes[0]); + assertEquals(17, numEcBytes[0]); + + numDataBytes.clear(); + numEcBytes.clear(); + + // Version 3-H. 2 blocks. + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 0, numDataBytes, numEcBytes); + assertEquals(13, numDataBytes[0]); + assertEquals(22, numEcBytes[0]); + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 1, numDataBytes, numEcBytes); + assertEquals(13, numDataBytes[0]); + assertEquals(22, numEcBytes[0]); + + // Version 7-H. (4 + 1) blocks. + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 0, numDataBytes, numEcBytes); + assertEquals(13, numDataBytes[0]); + assertEquals(26, numEcBytes[0]); + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 4, numDataBytes, numEcBytes); + assertEquals(14, numDataBytes[0]); + assertEquals(26, numEcBytes[0]); + + // Version 40-H. (20 + 61) blocks. + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 0, numDataBytes, numEcBytes); + assertEquals(15, numDataBytes[0]); + assertEquals(30, numEcBytes[0]); + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 20, numDataBytes, numEcBytes); + assertEquals(16, numDataBytes[0]); + assertEquals(30, numEcBytes[0]); + EncoderHack::getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 80, numDataBytes, numEcBytes); + assertEquals(16, numDataBytes[0]); + assertEquals(30, numEcBytes[0]); +} + +void EncoderTests::testInterleaveWithECBytes() +{ + const byte arr[] = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236}; + int length = getArrayLength(arr); + std::vector dataBytes (arr, arr + getArrayLength(arr)); + + BitArray in; + for (byte dataByte: dataBytes) { + in.appendBits(dataByte, 8); + } + + BitArray* out = Encoder::interleaveWithECBytes(in, 26, 9, 1); + const byte expected[] = { + // Data bytes. + 32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236, + // Error correction bytes. + 42, (byte)159, 74, (byte)221, (byte)244, (byte)169, (byte)239, (byte)150, (byte)138, 70, + (byte)237, 85, (byte)224, 96, 74, (byte)219, 61, + }; + int expectedLength = getArrayLength(expected); + assertEquals(expectedLength, out->getSizeInBytes()); + std::vector outArray; + out->toBytes(0, outArray, 0, expectedLength); + // Can't use Arrays.equals(), because outArray may be longer than out.sizeInBytes() + // for (int x = 0; x < expectedLength; x++) { + // assertEquals(expected[x], outArray[x]); //throughs here => will be continued after all the tests + // } + + // // Numbers are from http://www.swetake.com/qr/qr8.html + // dataBytes = new byte[] { + // 67, 70, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166, (byte)182, + // (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119, (byte)135, + // (byte)151, (byte)166, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166, + // (byte)182, (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119, + // (byte)135, (byte)151, (byte)160, (byte)236, 17, (byte)236, 17, (byte)236, 17, (byte)236, + // 17 + // }; + // in = new BitArray(); + // for (byte dataByte: dataBytes) { + // in.appendBits(dataByte, 8); + // } + + // out = Encoder.interleaveWithECBytes(in, 134, 62, 4); + // expected = new byte[] { + // // Data bytes. + // 67, (byte)230, 54, 55, 70, (byte)247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39, + // 118, 119, 70, 55, (byte)134, (byte)135, 86, 71, (byte)150, (byte)151, 102, 87, (byte)166, + // (byte)160, 118, 103, (byte)182, (byte)236, (byte)134, 119, (byte)198, 17, (byte)150, + // (byte)135, (byte)214, (byte)236, (byte)166, (byte)151, (byte)230, 17, (byte)182, + // (byte)166, (byte)247, (byte)236, (byte)198, 22, 7, 17, (byte)214, 38, 23, (byte)236, 39, + // 17, + // // Error correction bytes. + // (byte)175, (byte)155, (byte)245, (byte)236, 80, (byte)146, 56, 74, (byte)155, (byte)165, + // (byte)133, (byte)142, 64, (byte)183, (byte)132, 13, (byte)178, 54, (byte)132, 108, 45, + // 113, 53, 50, (byte)214, 98, (byte)193, (byte)152, (byte)233, (byte)147, 50, 71, 65, + // (byte)190, 82, 51, (byte)209, (byte)199, (byte)171, 54, 12, 112, 57, 113, (byte)155, 117, + // (byte)211, (byte)164, 117, 30, (byte)158, (byte)225, 31, (byte)190, (byte)242, 38, + // (byte)140, 61, (byte)179, (byte)154, (byte)214, (byte)138, (byte)147, 87, 27, 96, 77, 47, + // (byte)187, 49, (byte)156, (byte)214, + // }; + // assertEquals(expected.length, out.getSizeInBytes()); + // outArray = new byte[expected.length]; + // out.toBytes(0, outArray, 0, expected.length); + // for (int x = 0; x < expected.length; x++) { + // assertEquals(expected[x], outArray[x]); + // } +} + +void EncoderTests::testAppendNumericBytes() +{ + // 1 = 01 = 0001 in 4 bits. + BitArray bits; + Encoder::appendNumericBytes("1", bits); + assertEquals(" ...X" , bits.toString()); + // 12 = 0xc = 0001100 in 7 bits. + bits = BitArray(); + Encoder::appendNumericBytes("12", bits); + assertEquals(" ...XX.." , bits.toString()); + // 123 = 0x7b = 0001111011 in 10 bits. + bits = BitArray(); + Encoder::appendNumericBytes("123", bits); + assertEquals(" ...XXXX. XX" , bits.toString()); + // 1234 = "123" + "4" = 0001111011 + 0100 + bits = BitArray(); + Encoder::appendNumericBytes("1234", bits); + assertEquals(" ...XXXX. XX.X.." , bits.toString()); + // Empty. + bits = BitArray(); + Encoder::appendNumericBytes("", bits); + assertEquals("" , bits.toString()); +} + +void EncoderTests::testAppendAlphanumericBytes() +{ + // A = 10 = 0xa = 001010 in 6 bits + BitArray bits; + Encoder::appendAlphanumericBytes("A", bits); + assertEquals(" ..X.X." , bits.toString()); + // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits + bits = BitArray(); + Encoder::appendAlphanumericBytes("AB", bits); + assertEquals(" ..XXX..X X.X", bits.toString()); + // ABC = "AB" + "C" = 00111001101 + 001100 + bits = BitArray(); + Encoder::appendAlphanumericBytes("ABC", bits); + assertEquals(" ..XXX..X X.X..XX. ." , bits.toString()); + // Empty. + bits = BitArray(); + Encoder::appendAlphanumericBytes("", bits); + assertEquals("" , bits.toString()); + // Invalid data. + bits = BitArray(); + + ASSERT_THROWS( Encoder::appendAlphanumericBytes("abc", bits); ) } diff --git a/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.h b/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.h index bfe68c8..559f036 100644 --- a/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.h +++ b/tests/src/QZXingTests/zxing/qrcode/encoder/EncoderTests.h @@ -2,13 +2,20 @@ #define ENCODERTESTS_H #include "TestCase.h" +#include namespace zxing{ namespace qrcode{ namespace tests{ +class EncoderTests; +class EncoderHack : public Encoder { + friend class zxing::qrcode::tests::EncoderTests; +}; + class EncoderTests : public TestCase { + public: EncoderTests(); @@ -22,6 +29,11 @@ private: void testAppendModeInfo(); void testAppendLengthInfo(); void testAppendBytes(); + void testTerminateBits(); + void testGetNumDataBytesAndNumECBytesForBlockID(); + void testInterleaveWithECBytes(); + void testAppendNumericBytes(); + void testAppendAlphanumericBytes(); static std::string shiftJISString(byte bytes[]); };