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