File: QRFormatInformationTest.cpp

package info (click to toggle)
zxing-cpp 2.3.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 26,832 kB
  • sloc: cpp: 32,803; ansic: 18,360; php: 1,156; python: 215; makefile: 25; sh: 3
file content (146 lines) | stat: -rw-r--r-- 7,132 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2007 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0

#include "qrcode/QRFormatInformation.h"

#include "gtest/gtest.h"

using namespace ZXing;
using namespace ZXing::QRCode;

static const int MASKED_TEST_FORMAT_INFO = 0x2BED;
static const int MASKED_TEST_FORMAT_INFO2 = ((0x2BED << 1) & 0b1111111000000000) | 0b100000000 | (0x2BED & 0b11111111); // insert the 'Dark Module'
static const int UNMASKED_TEST_FORMAT_INFO = MASKED_TEST_FORMAT_INFO ^ 0x5412;
static const int MICRO_MASKED_TEST_FORMAT_INFO = 0x3BBA;
static const int RMQR_MASKED_TEST_FORMAT_INFO = 0x20137;
static const int RMQR_MASKED_TEST_FORMAT_INFO_SUB = 0x1F1FE;

static void DoFormatInformationTest(const int formatInfo, const uint8_t expectedMask, const ErrorCorrectionLevel& expectedECL)
{
	FormatInformation parsedFormat = FormatInformation::DecodeMQR(formatInfo);
	EXPECT_TRUE(parsedFormat.isValid());
	EXPECT_EQ(expectedMask, parsedFormat.dataMask);
	EXPECT_EQ(expectedECL, parsedFormat.ecLevel);
}

// Helper for rMQR to unset `numBits` number of bits
static uint32_t RMQRUnsetBits(uint32_t formatInfoBits, int numBits)
{
	for (int i = 0; i < 18 && numBits; i++) {
		if (formatInfoBits & (1 << i)) {
			formatInfoBits ^= 1 << i;
			numBits--;
		}
	}
	return formatInfoBits;
}

TEST(QRFormatInformationTest, Decode)
{
	// Normal case
	FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
	EXPECT_TRUE(expected.isValid());
	EXPECT_EQ(0x07, expected.dataMask);
	EXPECT_EQ(ErrorCorrectionLevel::Quality, expected.ecLevel);
	// where the code forgot the mask!
	EXPECT_EQ(expected, FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2));
}

TEST(QRFormatInformationTest, DecodeWithBitDifference)
{
	FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
	// 1,2,3,4 bits difference
	EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO2 ^ 0x01));
	EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO2 ^ 0x03));
	EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x07, MASKED_TEST_FORMAT_INFO2 ^ 0x07));
	auto unexpected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO2 ^ 0x0F);
	EXPECT_FALSE(expected == unexpected);
	EXPECT_FALSE(unexpected.isValid() && unexpected.type() == Type::Model2);
}

TEST(QRFormatInformationTest, DecodeWithMisread)
{
	FormatInformation expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2);
	EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO2 ^ 0x0F));
}

TEST(QRFormatInformationTest, DecodeMicro)
{
	// Normal cases.
	DoFormatInformationTest(0x4445, 0x0, ErrorCorrectionLevel::Low);
	DoFormatInformationTest(0x4172, 0x1, ErrorCorrectionLevel::Low);
	DoFormatInformationTest(0x5fc0, 0x2, ErrorCorrectionLevel::Low);
	DoFormatInformationTest(0x5af7, 0x3, ErrorCorrectionLevel::Low);
	DoFormatInformationTest(0x6793, 0x0, ErrorCorrectionLevel::Medium);
	DoFormatInformationTest(0x62a4, 0x1, ErrorCorrectionLevel::Medium);
	DoFormatInformationTest(0x3e8d, 0x2, ErrorCorrectionLevel::Quality);
	DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality);

	// where the code forgot the mask!
//	static const int MICRO_UNMASKED_TEST_FORMAT_INFO = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445;
//	DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality);
}

// This doesn't work as expected because the implementation of the decode tries with
// and without the mask (0x4445).  This effectively adds a tolerance of 5 bits to the Hamming
// distance calculation.
TEST(QRFormatInformationTest, DecodeMicroWithBitDifference)
{
	FormatInformation expected = FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO);

	// 1,2,3 bits difference
	EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01));
	EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03));
	EXPECT_EQ(expected, FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07));

	// Bigger bit differences can return valid FormatInformation objects but the data mask and error
	// correction levels do not match.
//	EXPECT_TRUE(FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).isValid());
//	EXPECT_NE(expected.dataMask(), FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).dataMask());
//	EXPECT_NE(expected.errorCorrectionLevel(),
//			  FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel());
}

TEST(QRFormatInformationTest, DecodeRMQR)
{
	// Normal case
	FormatInformation expected = FormatInformation::DecodeRMQR(RMQR_MASKED_TEST_FORMAT_INFO, RMQR_MASKED_TEST_FORMAT_INFO_SUB);
	EXPECT_TRUE(expected.isValid());
	EXPECT_EQ(4, expected.dataMask);
	EXPECT_EQ(ErrorCorrectionLevel::High, expected.ecLevel);
	EXPECT_EQ(FORMAT_INFO_MASK_RMQR, expected.mask);
	// Not catered for: where the code forgot the mask!
}

TEST(QRFormatInformationTest, DecodeRMQRWithBitDifference)
{
	FormatInformation expected = FormatInformation::DecodeRMQR(RMQR_MASKED_TEST_FORMAT_INFO, RMQR_MASKED_TEST_FORMAT_INFO_SUB);
	EXPECT_EQ(expected.ecLevel, ErrorCorrectionLevel::High);
	// 1,2,3,4,5 bits difference
	EXPECT_EQ(expected, FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 1), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 1)));
	EXPECT_EQ(expected, FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 2)));
	EXPECT_EQ(expected, FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 3), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 3)));
	EXPECT_EQ(expected, FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 4), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4)));
	auto unexpected = FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 5));
	EXPECT_FALSE(expected == unexpected);
	EXPECT_FALSE(unexpected.isValid());
	EXPECT_TRUE(unexpected.type() == Type::rMQR); // Note `mask` (used to determine type) set regardless
}

TEST(QRFormatInformationTest, DecodeRMQRWithMisread)
{
	FormatInformation expected = FormatInformation::DecodeRMQR(RMQR_MASKED_TEST_FORMAT_INFO, RMQR_MASKED_TEST_FORMAT_INFO_SUB);
	{
		auto actual = FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4));
		EXPECT_EQ(expected, actual);
		EXPECT_EQ(actual.mask, FORMAT_INFO_MASK_RMQR);
	}
	{
		auto actual = FormatInformation::DecodeRMQR(RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5), RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4));
		EXPECT_EQ(expected, actual);
		EXPECT_EQ(actual.mask, FORMAT_INFO_MASK_RMQR_SUB);
	}
}