1 /*
2 * QR Code generator test suite (C)
3 *
4 * When compiling this program, the library qrcodegen.c needs QRCODEGEN_TEST
5 * to be defined. Run this command line program with no arguments.
6 *
7 * Copyright (c) Project Nayuki. (MIT License)
8 * https://www.nayuki.io/page/qr-code-generator-library
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11 * this software and associated documentation files (the "Software"), to deal in
12 * the Software without restriction, including without limitation the rights to
13 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14 * the Software, and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 * - The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 * - The Software is provided "as is", without warranty of any kind, express or
19 * implied, including but not limited to the warranties of merchantability,
20 * fitness for a particular purpose and noninfringement. In no event shall the
21 * authors or copyright holders be liable for any claim, damages or other
22 * liability, whether in an action of contract, tort or otherwise, arising from,
23 * out of or in connection with the Software or the use or other dealings in the
24 * Software.
25 */
26
27 #include <assert.h>
28 #include <limits.h>
29 #include <stdbool.h>
30 #include <stddef.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include "qrcodegen.h"
37
38 #define ARRAY_LENGTH(name) (sizeof(name) / sizeof(name[0]))
39
40
41 // Global variables
42 static int numTestCases = 0;
43
44
45 // Prototypes of private functions under test
46 extern const int8_t ECC_CODEWORDS_PER_BLOCK[4][41];
47 extern const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41];
48 void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen);
49 void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]);
50 int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl);
51 int getNumRawDataModules(int version);
52 void reedSolomonComputeDivisor(int degree, uint8_t result[]);
53 void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]);
54 uint8_t reedSolomonMultiply(uint8_t x, uint8_t y);
55 void initializeFunctionModules(int version, uint8_t qrcode[]);
56 int getAlignmentPatternPositions(int version, uint8_t result[7]);
57 bool getModuleBounded(const uint8_t qrcode[], int x, int y);
58 void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark);
59 void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark);
60 int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars);
61 int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version);
62
63
64 /*---- Test cases ----*/
65
testAppendBitsToBuffer(void)66 static void testAppendBitsToBuffer(void) {
67 {
68 uint8_t buf[1] = {0};
69 int bitLen = 0;
70 appendBitsToBuffer(0, 0, buf, &bitLen);
71 assert(bitLen == 0);
72 assert(buf[0] == 0);
73 appendBitsToBuffer(1, 1, buf, &bitLen);
74 assert(bitLen == 1);
75 assert(buf[0] == 0x80);
76 appendBitsToBuffer(0, 1, buf, &bitLen);
77 assert(bitLen == 2);
78 assert(buf[0] == 0x80);
79 appendBitsToBuffer(5, 3, buf, &bitLen);
80 assert(bitLen == 5);
81 assert(buf[0] == 0xA8);
82 appendBitsToBuffer(6, 3, buf, &bitLen);
83 assert(bitLen == 8);
84 assert(buf[0] == 0xAE);
85 numTestCases++;
86 }
87 {
88 uint8_t buf[6] = {0};
89 int bitLen = 0;
90 appendBitsToBuffer(16942, 16, buf, &bitLen);
91 assert(bitLen == 16);
92 assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x00 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00);
93 appendBitsToBuffer(10, 7, buf, &bitLen);
94 assert(bitLen == 23);
95 assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x14 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00);
96 appendBitsToBuffer(15, 4, buf, &bitLen);
97 assert(bitLen == 27);
98 assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xE0 && buf[4] == 0x00 && buf[5] == 0x00);
99 appendBitsToBuffer(26664, 15, buf, &bitLen);
100 assert(bitLen == 42);
101 assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xFA && buf[4] == 0x0A && buf[5] == 0x00);
102 numTestCases++;
103 }
104 }
105
106
107 // Ported from the Java version of the code.
addEccAndInterleaveReference(const uint8_t * data,int version,enum qrcodegen_Ecc ecl)108 static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) {
109 // Calculate parameter numbers
110 size_t numBlocks = (size_t)NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version];
111 size_t blockEccLen = (size_t)ECC_CODEWORDS_PER_BLOCK[(int)ecl][version];
112 size_t rawCodewords = (size_t)getNumRawDataModules(version) / 8;
113 size_t numShortBlocks = numBlocks - rawCodewords % numBlocks;
114 size_t shortBlockLen = rawCodewords / numBlocks;
115
116 // Split data into blocks and append ECC to each block
117 uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*));
118 uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t));
119 if (blocks == NULL || generator == NULL) {
120 perror("malloc");
121 exit(EXIT_FAILURE);
122 }
123 reedSolomonComputeDivisor((int)blockEccLen, generator);
124 for (size_t i = 0, k = 0; i < numBlocks; i++) {
125 uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t));
126 if (block == NULL) {
127 perror("malloc");
128 exit(EXIT_FAILURE);
129 }
130 size_t datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1);
131 memcpy(block, &data[k], datLen * sizeof(uint8_t));
132 reedSolomonComputeRemainder(&data[k], (int)datLen, generator, (int)blockEccLen, &block[shortBlockLen + 1 - blockEccLen]);
133 k += datLen;
134 blocks[i] = block;
135 }
136 free(generator);
137
138 // Interleave (not concatenate) the bytes from every block into a single sequence
139 uint8_t *result = malloc(rawCodewords * sizeof(uint8_t));
140 if (result == NULL) {
141 perror("malloc");
142 exit(EXIT_FAILURE);
143 }
144 for (size_t i = 0, k = 0; i < shortBlockLen + 1; i++) {
145 for (size_t j = 0; j < numBlocks; j++) {
146 // Skip the padding byte in short blocks
147 if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) {
148 result[k] = blocks[j][i];
149 k++;
150 }
151 }
152 }
153 for (size_t i = 0; i < numBlocks; i++)
154 free(blocks[i]);
155 free(blocks);
156 return result;
157 }
158
159
testAddEccAndInterleave(void)160 static void testAddEccAndInterleave(void) {
161 for (int version = 1; version <= 40; version++) {
162 for (int ecl = 0; ecl < 4; ecl++) {
163 size_t dataLen = (size_t)getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl);
164 uint8_t *pureData = malloc(dataLen * sizeof(uint8_t));
165 if (pureData == NULL) {
166 perror("malloc");
167 exit(EXIT_FAILURE);
168 }
169 for (size_t i = 0; i < dataLen; i++)
170 pureData[i] = (uint8_t)(rand() % 256);
171 uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl);
172
173 size_t dataAndEccLen = (size_t)getNumRawDataModules(version) / 8;
174 uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t));
175 if (paddedData == NULL) {
176 perror("malloc");
177 exit(EXIT_FAILURE);
178 }
179 memcpy(paddedData, pureData, dataLen * sizeof(uint8_t));
180 uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t));
181 if (actualOutput == NULL) {
182 perror("malloc");
183 exit(EXIT_FAILURE);
184 }
185 addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput);
186
187 assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0);
188 free(pureData);
189 free(expectOutput);
190 free(paddedData);
191 free(actualOutput);
192 numTestCases++;
193 }
194 }
195 }
196
197
testGetNumDataCodewords(void)198 static void testGetNumDataCodewords(void) {
199 const int cases[][3] = {
200 { 3, 1, 44},
201 { 3, 2, 34},
202 { 3, 3, 26},
203 { 6, 0, 136},
204 { 7, 0, 156},
205 { 9, 0, 232},
206 { 9, 1, 182},
207 {12, 3, 158},
208 {15, 0, 523},
209 {16, 2, 325},
210 {19, 3, 341},
211 {21, 0, 932},
212 {22, 0, 1006},
213 {22, 1, 782},
214 {22, 3, 442},
215 {24, 0, 1174},
216 {24, 3, 514},
217 {28, 0, 1531},
218 {30, 3, 745},
219 {32, 3, 845},
220 {33, 0, 2071},
221 {33, 3, 901},
222 {35, 0, 2306},
223 {35, 1, 1812},
224 {35, 2, 1286},
225 {36, 3, 1054},
226 {37, 3, 1096},
227 {39, 1, 2216},
228 {40, 1, 2334},
229 };
230 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
231 const int *tc = cases[i];
232 assert(getNumDataCodewords(tc[0], (enum qrcodegen_Ecc)tc[1]) == tc[2]);
233 numTestCases++;
234 }
235 }
236
237
testGetNumRawDataModules(void)238 static void testGetNumRawDataModules(void) {
239 const int cases[][2] = {
240 { 1, 208},
241 { 2, 359},
242 { 3, 567},
243 { 6, 1383},
244 { 7, 1568},
245 {12, 3728},
246 {15, 5243},
247 {18, 7211},
248 {22, 10068},
249 {26, 13652},
250 {32, 19723},
251 {37, 25568},
252 {40, 29648},
253 };
254 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
255 const int *tc = cases[i];
256 assert(getNumRawDataModules(tc[0]) == tc[1]);
257 numTestCases++;
258 }
259 }
260
261
testReedSolomonComputeDivisor(void)262 static void testReedSolomonComputeDivisor(void) {
263 uint8_t generator[30];
264
265 reedSolomonComputeDivisor(1, generator);
266 assert(generator[0] == 0x01);
267 numTestCases++;
268
269 reedSolomonComputeDivisor(2, generator);
270 assert(generator[0] == 0x03);
271 assert(generator[1] == 0x02);
272 numTestCases++;
273
274 reedSolomonComputeDivisor(5, generator);
275 assert(generator[0] == 0x1F);
276 assert(generator[1] == 0xC6);
277 assert(generator[2] == 0x3F);
278 assert(generator[3] == 0x93);
279 assert(generator[4] == 0x74);
280 numTestCases++;
281
282 reedSolomonComputeDivisor(30, generator);
283 assert(generator[ 0] == 0xD4);
284 assert(generator[ 1] == 0xF6);
285 assert(generator[ 5] == 0xC0);
286 assert(generator[12] == 0x16);
287 assert(generator[13] == 0xD9);
288 assert(generator[20] == 0x12);
289 assert(generator[27] == 0x6A);
290 assert(generator[29] == 0x96);
291 numTestCases++;
292 }
293
294
testReedSolomonComputeRemainder(void)295 static void testReedSolomonComputeRemainder(void) {
296 {
297 uint8_t data[1];
298 uint8_t generator[3];
299 uint8_t remainder[ARRAY_LENGTH(generator)];
300 reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator);
301 reedSolomonComputeRemainder(data, 0, generator, ARRAY_LENGTH(generator), remainder);
302 assert(remainder[0] == 0);
303 assert(remainder[1] == 0);
304 assert(remainder[2] == 0);
305 numTestCases++;
306 }
307 {
308 uint8_t data[2] = {0, 1};
309 uint8_t generator[4];
310 uint8_t remainder[ARRAY_LENGTH(generator)];
311 reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator);
312 reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder);
313 assert(remainder[0] == generator[0]);
314 assert(remainder[1] == generator[1]);
315 assert(remainder[2] == generator[2]);
316 assert(remainder[3] == generator[3]);
317 numTestCases++;
318 }
319 {
320 uint8_t data[5] = {0x03, 0x3A, 0x60, 0x12, 0xC7};
321 uint8_t generator[5];
322 uint8_t remainder[ARRAY_LENGTH(generator)];
323 reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator);
324 reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder);
325 assert(remainder[0] == 0xCB);
326 assert(remainder[1] == 0x36);
327 assert(remainder[2] == 0x16);
328 assert(remainder[3] == 0xFA);
329 assert(remainder[4] == 0x9D);
330 numTestCases++;
331 }
332 {
333 uint8_t data[43] = {
334 0x38, 0x71, 0xDB, 0xF9, 0xD7, 0x28, 0xF6, 0x8E, 0xFE, 0x5E,
335 0xE6, 0x7D, 0x7D, 0xB2, 0xA5, 0x58, 0xBC, 0x28, 0x23, 0x53,
336 0x14, 0xD5, 0x61, 0xC0, 0x20, 0x6C, 0xDE, 0xDE, 0xFC, 0x79,
337 0xB0, 0x8B, 0x78, 0x6B, 0x49, 0xD0, 0x1A, 0xAD, 0xF3, 0xEF,
338 0x52, 0x7D, 0x9A,
339 };
340 uint8_t generator[30];
341 uint8_t remainder[ARRAY_LENGTH(generator)];
342 reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator);
343 reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder);
344 assert(remainder[ 0] == 0xCE);
345 assert(remainder[ 1] == 0xF0);
346 assert(remainder[ 2] == 0x31);
347 assert(remainder[ 3] == 0xDE);
348 assert(remainder[ 8] == 0xE1);
349 assert(remainder[12] == 0xCA);
350 assert(remainder[17] == 0xE3);
351 assert(remainder[19] == 0x85);
352 assert(remainder[20] == 0x50);
353 assert(remainder[24] == 0xBE);
354 assert(remainder[29] == 0xB3);
355 numTestCases++;
356 }
357 }
358
359
testReedSolomonMultiply(void)360 static void testReedSolomonMultiply(void) {
361 const uint8_t cases[][3] = {
362 {0x00, 0x00, 0x00},
363 {0x01, 0x01, 0x01},
364 {0x02, 0x02, 0x04},
365 {0x00, 0x6E, 0x00},
366 {0xB2, 0xDD, 0xE6},
367 {0x41, 0x11, 0x25},
368 {0xB0, 0x1F, 0x11},
369 {0x05, 0x75, 0xBC},
370 {0x52, 0xB5, 0xAE},
371 {0xA8, 0x20, 0xA4},
372 {0x0E, 0x44, 0x9F},
373 {0xD4, 0x13, 0xA0},
374 {0x31, 0x10, 0x37},
375 {0x6C, 0x58, 0xCB},
376 {0xB6, 0x75, 0x3E},
377 {0xFF, 0xFF, 0xE2},
378 };
379 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
380 const uint8_t *tc = cases[i];
381 assert(reedSolomonMultiply(tc[0], tc[1]) == tc[2]);
382 numTestCases++;
383 }
384 }
385
386
testInitializeFunctionModulesEtc(void)387 static void testInitializeFunctionModulesEtc(void) {
388 for (int ver = 1; ver <= 40; ver++) {
389 uint8_t *qrcode = malloc((size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(ver) * sizeof(uint8_t));
390 if (qrcode == NULL) {
391 perror("malloc");
392 exit(EXIT_FAILURE);
393 }
394 initializeFunctionModules(ver, qrcode);
395
396 int size = qrcodegen_getSize(qrcode);
397 if (ver == 1)
398 assert(size == 21);
399 else if (ver == 40)
400 assert(size == 177);
401 else
402 assert(size == ver * 4 + 17);
403
404 bool hasLight = false;
405 bool hasDark = false;
406 for (int y = 0; y < size; y++) {
407 for (int x = 0; x < size; x++) {
408 bool color = qrcodegen_getModule(qrcode, x, y);
409 if (color)
410 hasDark = true;
411 else
412 hasLight = true;
413 }
414 }
415 assert(hasLight && hasDark);
416 free(qrcode);
417 numTestCases++;
418 }
419 }
420
421
testGetAlignmentPatternPositions(void)422 static void testGetAlignmentPatternPositions(void) {
423 const int cases[][9] = {
424 { 1, 0, -1, -1, -1, -1, -1, -1, -1},
425 { 2, 2, 6, 18, -1, -1, -1, -1, -1},
426 { 3, 2, 6, 22, -1, -1, -1, -1, -1},
427 { 6, 2, 6, 34, -1, -1, -1, -1, -1},
428 { 7, 3, 6, 22, 38, -1, -1, -1, -1},
429 { 8, 3, 6, 24, 42, -1, -1, -1, -1},
430 {16, 4, 6, 26, 50, 74, -1, -1, -1},
431 {25, 5, 6, 32, 58, 84, 110, -1, -1},
432 {32, 6, 6, 34, 60, 86, 112, 138, -1},
433 {33, 6, 6, 30, 58, 86, 114, 142, -1},
434 {39, 7, 6, 26, 54, 82, 110, 138, 166},
435 {40, 7, 6, 30, 58, 86, 114, 142, 170},
436 };
437 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
438 const int *tc = cases[i];
439 uint8_t pos[7];
440 int num = getAlignmentPatternPositions(tc[0], pos);
441 assert(num == tc[1]);
442 for (int j = 0; j < num; j++)
443 assert(pos[j] == tc[2 + j]);
444 numTestCases++;
445 }
446 }
447
448
testGetSetModule(void)449 static void testGetSetModule(void) {
450 uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(23)];
451 initializeFunctionModules(23, qrcode);
452 int size = qrcodegen_getSize(qrcode);
453
454 for (int y = 0; y < size; y++) { // Clear all to light
455 for (int x = 0; x < size; x++)
456 setModuleBounded(qrcode, x, y, false);
457 }
458 for (int y = 0; y < size; y++) { // Check all light
459 for (int x = 0; x < size; x++)
460 assert(qrcodegen_getModule(qrcode, x, y) == false);
461 }
462 for (int y = 0; y < size; y++) { // Set all to dark
463 for (int x = 0; x < size; x++)
464 setModuleBounded(qrcode, x, y, true);
465 }
466 for (int y = 0; y < size; y++) { // Check all dark
467 for (int x = 0; x < size; x++)
468 assert(qrcodegen_getModule(qrcode, x, y) == true);
469 }
470
471 // Set some out of bounds modules to light
472 setModuleUnbounded(qrcode, -1, -1, false);
473 setModuleUnbounded(qrcode, -1, 0, false);
474 setModuleUnbounded(qrcode, 0, -1, false);
475 setModuleUnbounded(qrcode, size, 5, false);
476 setModuleUnbounded(qrcode, 72, size, false);
477 setModuleUnbounded(qrcode, size, size, false);
478 for (int y = 0; y < size; y++) { // Check all dark
479 for (int x = 0; x < size; x++)
480 assert(qrcodegen_getModule(qrcode, x, y) == true);
481 }
482
483 // Set some modules to light
484 setModuleBounded(qrcode, 3, 8, false);
485 setModuleBounded(qrcode, 61, 49, false);
486 for (int y = 0; y < size; y++) { // Check most dark
487 for (int x = 0; x < size; x++) {
488 bool light = (x == 3 && y == 8) || (x == 61 && y == 49);
489 assert(qrcodegen_getModule(qrcode, x, y) != light);
490 }
491 }
492 numTestCases++;
493 }
494
495
testGetSetModuleRandomly(void)496 static void testGetSetModuleRandomly(void) {
497 uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(1)];
498 initializeFunctionModules(1, qrcode);
499 int size = qrcodegen_getSize(qrcode);
500
501 bool modules[21][21];
502 for (int y = 0; y < size; y++) {
503 for (int x = 0; x < size; x++)
504 modules[y][x] = qrcodegen_getModule(qrcode, x, y);
505 }
506
507 long trials = 100000;
508 for (long i = 0; i < trials; i++) {
509 int x = rand() % (size * 2) - size / 2;
510 int y = rand() % (size * 2) - size / 2;
511 bool isInBounds = 0 <= x && x < size && 0 <= y && y < size;
512 bool oldColor = isInBounds && modules[y][x];
513 if (isInBounds)
514 assert(getModuleBounded(qrcode, x, y) == oldColor);
515 assert(qrcodegen_getModule(qrcode, x, y) == oldColor);
516
517 bool newColor = rand() % 2 == 0;
518 if (isInBounds)
519 modules[y][x] = newColor;
520 if (isInBounds && rand() % 2 == 0)
521 setModuleBounded(qrcode, x, y, newColor);
522 else
523 setModuleUnbounded(qrcode, x, y, newColor);
524 }
525 numTestCases++;
526 }
527
528
testIsAlphanumeric(void)529 static void testIsAlphanumeric(void) {
530 struct TestCase {
531 bool answer;
532 const char *text;
533 };
534 const struct TestCase cases[] = {
535 {true, ""},
536 {true, "0"},
537 {true, "A"},
538 {false, "a"},
539 {true, " "},
540 {true, "."},
541 {true, "*"},
542 {false, ","},
543 {false, "|"},
544 {false, "@"},
545 {true, "XYZ"},
546 {false, "XYZ!"},
547 {true, "79068"},
548 {true, "+123 ABC$"},
549 {false, "\x01"},
550 {false, "\x7F"},
551 {false, "\x80"},
552 {false, "\xC0"},
553 {false, "\xFF"},
554 };
555 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
556 assert(qrcodegen_isAlphanumeric(cases[i].text) == cases[i].answer);
557 numTestCases++;
558 }
559 }
560
561
testIsNumeric(void)562 static void testIsNumeric(void) {
563 struct TestCase {
564 bool answer;
565 const char *text;
566 };
567 const struct TestCase cases[] = {
568 {true, ""},
569 {true, "0"},
570 {false, "A"},
571 {false, "a"},
572 {false, " "},
573 {false, "."},
574 {false, "*"},
575 {false, ","},
576 {false, "|"},
577 {false, "@"},
578 {false, "XYZ"},
579 {false, "XYZ!"},
580 {true, "79068"},
581 {false, "+123 ABC$"},
582 {false, "\x01"},
583 {false, "\x7F"},
584 {false, "\x80"},
585 {false, "\xC0"},
586 {false, "\xFF"},
587 };
588 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
589 assert(qrcodegen_isNumeric(cases[i].text) == cases[i].answer);
590 numTestCases++;
591 }
592 }
593
594
testCalcSegmentBufferSize(void)595 static void testCalcSegmentBufferSize(void) {
596 {
597 const size_t cases[][2] = {
598 {0, 0},
599 {1, 1},
600 {2, 1},
601 {3, 2},
602 {4, 2},
603 {5, 3},
604 {6, 3},
605 {1472, 614},
606 {2097, 874},
607 {5326, 2220},
608 {9828, 4095},
609 {9829, 4096},
610 {9830, 4096},
611 {9831, SIZE_MAX},
612 {9832, SIZE_MAX},
613 {12000, SIZE_MAX},
614 {28453, SIZE_MAX},
615 {55555, SIZE_MAX},
616 {SIZE_MAX / 6, SIZE_MAX},
617 {SIZE_MAX / 4, SIZE_MAX},
618 {SIZE_MAX / 2, SIZE_MAX},
619 {SIZE_MAX / 1, SIZE_MAX},
620 };
621 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
622 assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, cases[i][0]) == cases[i][1]);
623 numTestCases++;
624 }
625 }
626 {
627 const size_t cases[][2] = {
628 {0, 0},
629 {1, 1},
630 {2, 2},
631 {3, 3},
632 {4, 3},
633 {5, 4},
634 {6, 5},
635 {1472, 1012},
636 {2097, 1442},
637 {5326, 3662},
638 {5955, 4095},
639 {5956, 4095},
640 {5957, 4096},
641 {5958, SIZE_MAX},
642 {5959, SIZE_MAX},
643 {12000, SIZE_MAX},
644 {28453, SIZE_MAX},
645 {55555, SIZE_MAX},
646 {SIZE_MAX / 10, SIZE_MAX},
647 {SIZE_MAX / 8, SIZE_MAX},
648 {SIZE_MAX / 5, SIZE_MAX},
649 {SIZE_MAX / 2, SIZE_MAX},
650 {SIZE_MAX / 1, SIZE_MAX},
651 };
652 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
653 assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, cases[i][0]) == cases[i][1]);
654 numTestCases++;
655 }
656 }
657 {
658 const size_t cases[][2] = {
659 {0, 0},
660 {1, 1},
661 {2, 2},
662 {3, 3},
663 {1472, 1472},
664 {2097, 2097},
665 {4094, 4094},
666 {4095, 4095},
667 {4096, SIZE_MAX},
668 {4097, SIZE_MAX},
669 {5957, SIZE_MAX},
670 {12000, SIZE_MAX},
671 {28453, SIZE_MAX},
672 {55555, SIZE_MAX},
673 {SIZE_MAX / 16 + 1, SIZE_MAX},
674 {SIZE_MAX / 14, SIZE_MAX},
675 {SIZE_MAX / 9, SIZE_MAX},
676 {SIZE_MAX / 7, SIZE_MAX},
677 {SIZE_MAX / 4, SIZE_MAX},
678 {SIZE_MAX / 3, SIZE_MAX},
679 {SIZE_MAX / 2, SIZE_MAX},
680 {SIZE_MAX / 1, SIZE_MAX},
681 };
682 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
683 assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_BYTE, cases[i][0]) == cases[i][1]);
684 numTestCases++;
685 }
686 }
687 {
688 const size_t cases[][2] = {
689 {0, 0},
690 {1, 2},
691 {2, 4},
692 {3, 5},
693 {1472, 2392},
694 {2097, 3408},
695 {2519, 4094},
696 {2520, 4095},
697 {2521, SIZE_MAX},
698 {5957, SIZE_MAX},
699 {2522, SIZE_MAX},
700 {12000, SIZE_MAX},
701 {28453, SIZE_MAX},
702 {55555, SIZE_MAX},
703 {SIZE_MAX / 13 + 1, SIZE_MAX},
704 {SIZE_MAX / 12, SIZE_MAX},
705 {SIZE_MAX / 9, SIZE_MAX},
706 {SIZE_MAX / 4, SIZE_MAX},
707 {SIZE_MAX / 3, SIZE_MAX},
708 {SIZE_MAX / 2, SIZE_MAX},
709 {SIZE_MAX / 1, SIZE_MAX},
710 };
711 for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) {
712 assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_KANJI, cases[i][0]) == cases[i][1]);
713 numTestCases++;
714 }
715 }
716 {
717 assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ECI, 0) == 3);
718 numTestCases++;
719 }
720 }
721
722
testCalcSegmentBitLength(void)723 static void testCalcSegmentBitLength(void) {
724 struct TestCase {
725 size_t numChars;
726 int result;
727 };
728 {
729 const struct TestCase CASES[] = {
730 {0, 0},
731 {1, 4},
732 {2, 7},
733 {3, 10},
734 {4, 14},
735 {5, 17},
736 {6, 20},
737 {1472, 4907},
738 {2097, 6990},
739 {5326, 17754},
740 {9828, 32760},
741 {9829, 32764},
742 {9830, 32767},
743 {9831, -1},
744 {9832, -1},
745 {12000, -1},
746 {28453, -1},
747 {SIZE_MAX / 6, -1},
748 {SIZE_MAX / 3, -1},
749 {SIZE_MAX / 2, -1},
750 {SIZE_MAX / 1, -1},
751 };
752 for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) {
753 assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, CASES[i].numChars) == CASES[i].result);
754 numTestCases++;
755 }
756 }
757 {
758 const struct TestCase CASES[] = {
759 {0, 0},
760 {1, 6},
761 {2, 11},
762 {3, 17},
763 {4, 22},
764 {5, 28},
765 {6, 33},
766 {1472, 8096},
767 {2097, 11534},
768 {5326, 29293},
769 {5955, 32753},
770 {5956, 32758},
771 {5957, 32764},
772 {5958, -1},
773 {5959, -1},
774 {12000, -1},
775 {28453, -1},
776 {SIZE_MAX / 10, -1},
777 {SIZE_MAX / 5, -1},
778 {SIZE_MAX / 2, -1},
779 {SIZE_MAX / 1, -1},
780 };
781 for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) {
782 assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, CASES[i].numChars) == CASES[i].result);
783 numTestCases++;
784 }
785 }
786 {
787 const struct TestCase CASES[] = {
788 {0, 0},
789 {1, 8},
790 {2, 16},
791 {3, 24},
792 {1472, 11776},
793 {2097, 16776},
794 {4094, 32752},
795 {4095, 32760},
796 {4096, -1},
797 {4097, -1},
798 {5957, -1},
799 {12000, -1},
800 {28453, -1},
801 {SIZE_MAX / 15, -1},
802 {SIZE_MAX / 12, -1},
803 {SIZE_MAX / 7, -1},
804 {SIZE_MAX / 3, -1},
805 {SIZE_MAX / 1, -1},
806 };
807 for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) {
808 assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, CASES[i].numChars) == CASES[i].result);
809 numTestCases++;
810 }
811 }
812 {
813 const struct TestCase CASES[] = {
814 {0, 0},
815 {1, 13},
816 {2, 26},
817 {3, 39},
818 {1472, 19136},
819 {2097, 27261},
820 {2519, 32747},
821 {2520, 32760},
822 {2521, -1},
823 {5957, -1},
824 {2522, -1},
825 {12000, -1},
826 {28453, -1},
827 {SIZE_MAX / 25, -1},
828 {SIZE_MAX / 20, -1},
829 {SIZE_MAX / 11, -1},
830 {SIZE_MAX / 4, -1},
831 {SIZE_MAX / 2, -1},
832 {SIZE_MAX / 1, -1},
833 };
834 for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) {
835 assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, CASES[i].numChars) == CASES[i].result);
836 numTestCases++;
837 }
838 }
839 {
840 assert(calcSegmentBitLength(qrcodegen_Mode_ECI, 0) == 24);
841 numTestCases++;
842 }
843 }
844
845
testMakeBytes(void)846 static void testMakeBytes(void) {
847 {
848 struct qrcodegen_Segment seg = qrcodegen_makeBytes(NULL, 0, NULL);
849 assert(seg.mode == qrcodegen_Mode_BYTE);
850 assert(seg.numChars == 0);
851 assert(seg.bitLength == 0);
852 numTestCases++;
853 }
854 {
855 const uint8_t data[] = {0x00};
856 uint8_t buf[1];
857 struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 1, buf);
858 assert(seg.numChars == 1);
859 assert(seg.bitLength == 8);
860 assert(seg.data[0] == 0x00);
861 numTestCases++;
862 }
863 {
864 const uint8_t data[] = {0xEF, 0xBB, 0xBF};
865 uint8_t buf[3];
866 struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 3, buf);
867 assert(seg.numChars == 3);
868 assert(seg.bitLength == 24);
869 assert(seg.data[0] == 0xEF);
870 assert(seg.data[1] == 0xBB);
871 assert(seg.data[2] == 0xBF);
872 numTestCases++;
873 }
874 }
875
876
testMakeNumeric(void)877 static void testMakeNumeric(void) {
878 {
879 struct qrcodegen_Segment seg = qrcodegen_makeNumeric("", NULL);
880 assert(seg.mode == qrcodegen_Mode_NUMERIC);
881 assert(seg.numChars == 0);
882 assert(seg.bitLength == 0);
883 numTestCases++;
884 }
885 {
886 uint8_t buf[1];
887 struct qrcodegen_Segment seg = qrcodegen_makeNumeric("9", buf);
888 assert(seg.numChars == 1);
889 assert(seg.bitLength == 4);
890 assert(seg.data[0] == 0x90);
891 numTestCases++;
892 }
893 {
894 uint8_t buf[1];
895 struct qrcodegen_Segment seg = qrcodegen_makeNumeric("81", buf);
896 assert(seg.numChars == 2);
897 assert(seg.bitLength == 7);
898 assert(seg.data[0] == 0xA2);
899 numTestCases++;
900 }
901 {
902 uint8_t buf[2];
903 struct qrcodegen_Segment seg = qrcodegen_makeNumeric("673", buf);
904 assert(seg.numChars == 3);
905 assert(seg.bitLength == 10);
906 assert(seg.data[0] == 0xA8);
907 assert(seg.data[1] == 0x40);
908 numTestCases++;
909 }
910 {
911 uint8_t buf[5];
912 struct qrcodegen_Segment seg = qrcodegen_makeNumeric("3141592653", buf);
913 assert(seg.numChars == 10);
914 assert(seg.bitLength == 34);
915 assert(seg.data[0] == 0x4E);
916 assert(seg.data[1] == 0x89);
917 assert(seg.data[2] == 0xF4);
918 assert(seg.data[3] == 0x24);
919 assert(seg.data[4] == 0xC0);
920 numTestCases++;
921 }
922 }
923
924
testMakeAlphanumeric(void)925 static void testMakeAlphanumeric(void) {
926 {
927 struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("", NULL);
928 assert(seg.mode == qrcodegen_Mode_ALPHANUMERIC);
929 assert(seg.numChars == 0);
930 assert(seg.bitLength == 0);
931 numTestCases++;
932 }
933 {
934 uint8_t buf[1];
935 struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("A", buf);
936 assert(seg.numChars == 1);
937 assert(seg.bitLength == 6);
938 assert(seg.data[0] == 0x28);
939 numTestCases++;
940 }
941 {
942 uint8_t buf[2];
943 struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("%:", buf);
944 assert(seg.numChars == 2);
945 assert(seg.bitLength == 11);
946 assert(seg.data[0] == 0xDB);
947 assert(seg.data[1] == 0x40);
948 numTestCases++;
949 }
950 {
951 uint8_t buf[3];
952 struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("Q R", buf);
953 assert(seg.numChars == 3);
954 assert(seg.bitLength == 17);
955 assert(seg.data[0] == 0x96);
956 assert(seg.data[1] == 0xCD);
957 assert(seg.data[2] == 0x80);
958 numTestCases++;
959 }
960 }
961
962
testMakeEci(void)963 static void testMakeEci(void) {
964 {
965 uint8_t buf[1];
966 struct qrcodegen_Segment seg = qrcodegen_makeEci(127, buf);
967 assert(seg.mode == qrcodegen_Mode_ECI);
968 assert(seg.numChars == 0);
969 assert(seg.bitLength == 8);
970 assert(seg.data[0] == 0x7F);
971 numTestCases++;
972 }
973 {
974 uint8_t buf[2];
975 struct qrcodegen_Segment seg = qrcodegen_makeEci(10345, buf);
976 assert(seg.numChars == 0);
977 assert(seg.bitLength == 16);
978 assert(seg.data[0] == 0xA8);
979 assert(seg.data[1] == 0x69);
980 numTestCases++;
981 }
982 {
983 uint8_t buf[3];
984 struct qrcodegen_Segment seg = qrcodegen_makeEci(999999, buf);
985 assert(seg.numChars == 0);
986 assert(seg.bitLength == 24);
987 assert(seg.data[0] == 0xCF);
988 assert(seg.data[1] == 0x42);
989 assert(seg.data[2] == 0x3F);
990 numTestCases++;
991 }
992 }
993
994
testGetTotalBits(void)995 static void testGetTotalBits(void) {
996 {
997 assert(getTotalBits(NULL, 0, 1) == 0);
998 numTestCases++;
999 assert(getTotalBits(NULL, 0, 40) == 0);
1000 numTestCases++;
1001 }
1002 {
1003 struct qrcodegen_Segment segs[] = {
1004 {qrcodegen_Mode_BYTE, 3, NULL, 24},
1005 };
1006 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 2) == 36);
1007 numTestCases++;
1008 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 44);
1009 numTestCases++;
1010 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 39) == 44);
1011 numTestCases++;
1012 }
1013 {
1014 struct qrcodegen_Segment segs[] = {
1015 {qrcodegen_Mode_ECI, 0, NULL, 8},
1016 {qrcodegen_Mode_NUMERIC, 7, NULL, 24},
1017 {qrcodegen_Mode_ALPHANUMERIC, 1, NULL, 6},
1018 {qrcodegen_Mode_KANJI, 4, NULL, 52},
1019 };
1020 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 133);
1021 numTestCases++;
1022 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 21) == 139);
1023 numTestCases++;
1024 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 145);
1025 numTestCases++;
1026 }
1027 {
1028 struct qrcodegen_Segment segs[] = {
1029 {qrcodegen_Mode_BYTE, 4093, NULL, 32744},
1030 };
1031 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1);
1032 numTestCases++;
1033 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32764);
1034 numTestCases++;
1035 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 32764);
1036 numTestCases++;
1037 }
1038 {
1039 struct qrcodegen_Segment segs[] = {
1040 {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824},
1041 {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824},
1042 {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824},
1043 {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824},
1044 {qrcodegen_Mode_NUMERIC, 1617, NULL, 5390},
1045 };
1046 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1);
1047 numTestCases++;
1048 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32766);
1049 numTestCases++;
1050 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == -1);
1051 numTestCases++;
1052 }
1053 {
1054 struct qrcodegen_Segment segs[] = {
1055 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1056 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1057 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1058 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1059 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1060 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1061 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1062 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1063 {qrcodegen_Mode_KANJI, 255, NULL, 3315},
1064 {qrcodegen_Mode_ALPHANUMERIC, 511, NULL, 2811},
1065 };
1066 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 32767);
1067 numTestCases++;
1068 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 26) == -1);
1069 numTestCases++;
1070 assert(getTotalBits(segs, ARRAY_LENGTH(segs), 40) == -1);
1071 numTestCases++;
1072 }
1073 }
1074
1075
1076 /*---- Main runner ----*/
1077
main(void)1078 int main(void) {
1079 srand((unsigned int)time(NULL));
1080 testAppendBitsToBuffer();
1081 testAddEccAndInterleave();
1082 testGetNumDataCodewords();
1083 testGetNumRawDataModules();
1084 testReedSolomonComputeDivisor();
1085 testReedSolomonComputeRemainder();
1086 testReedSolomonMultiply();
1087 testInitializeFunctionModulesEtc();
1088 testGetAlignmentPatternPositions();
1089 testGetSetModule();
1090 testGetSetModuleRandomly();
1091 testIsAlphanumeric();
1092 testIsNumeric();
1093 testCalcSegmentBufferSize();
1094 testCalcSegmentBitLength();
1095 testMakeBytes();
1096 testMakeNumeric();
1097 testMakeAlphanumeric();
1098 testMakeEci();
1099 testGetTotalBits();
1100 printf("All %d test cases passed\n", numTestCases);
1101 return EXIT_SUCCESS;
1102 }
1103