/* * QR Code generator test suite (C) * * When compiling this program, the library qrcodegen.c needs QRCODEGEN_TEST * to be defined. Run this command line program with no arguments. * * Copyright (c) Project Nayuki. (MIT License) * https://www.nayuki.io/page/qr-code-generator-library * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * - The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * - The Software is provided "as is", without warranty of any kind, express or * implied, including but not limited to the warranties of merchantability, * fitness for a particular purpose and noninfringement. In no event shall the * authors or copyright holders be liable for any claim, damages or other * liability, whether in an action of contract, tort or otherwise, arising from, * out of or in connection with the Software or the use or other dealings in the * Software. */ #include #include #include #include #include #include #include #include #include #include "qrcodegen.h" #define ARRAY_LENGTH(name) (sizeof(name) / sizeof(name[0])) // Global variables static int numTestCases = 0; // Prototypes of private functions under test extern const int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; extern const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); int getNumRawDataModules(int version); void reedSolomonComputeDivisor(int degree, uint8_t result[]); void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]); uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); void initializeFunctionModules(int version, uint8_t qrcode[]); int getAlignmentPatternPositions(int version, uint8_t result[7]); bool getModule(const uint8_t qrcode[], int x, int y); void setModule(uint8_t qrcode[], int x, int y, bool isDark); void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark); int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); /*---- Test cases ----*/ static void testAppendBitsToBuffer(void) { { uint8_t buf[1] = {0}; int bitLen = 0; appendBitsToBuffer(0, 0, buf, &bitLen); assert(bitLen == 0); assert(buf[0] == 0); appendBitsToBuffer(1, 1, buf, &bitLen); assert(bitLen == 1); assert(buf[0] == 0x80); appendBitsToBuffer(0, 1, buf, &bitLen); assert(bitLen == 2); assert(buf[0] == 0x80); appendBitsToBuffer(5, 3, buf, &bitLen); assert(bitLen == 5); assert(buf[0] == 0xA8); appendBitsToBuffer(6, 3, buf, &bitLen); assert(bitLen == 8); assert(buf[0] == 0xAE); numTestCases++; } { uint8_t buf[6] = {0}; int bitLen = 0; appendBitsToBuffer(16942, 16, buf, &bitLen); assert(bitLen == 16); assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x00 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00); appendBitsToBuffer(10, 7, buf, &bitLen); assert(bitLen == 23); assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x14 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00); appendBitsToBuffer(15, 4, buf, &bitLen); assert(bitLen == 27); assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xE0 && buf[4] == 0x00 && buf[5] == 0x00); appendBitsToBuffer(26664, 15, buf, &bitLen); assert(bitLen == 42); assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xFA && buf[4] == 0x0A && buf[5] == 0x00); numTestCases++; } } // Ported from the Java version of the code. static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) { // Calculate parameter numbers size_t numBlocks = (size_t)NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; size_t blockEccLen = (size_t)ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; size_t rawCodewords = (size_t)getNumRawDataModules(version) / 8; size_t numShortBlocks = numBlocks - rawCodewords % numBlocks; size_t shortBlockLen = rawCodewords / numBlocks; // Split data into blocks and append ECC to each block uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*)); uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t)); if (blocks == NULL || generator == NULL) { perror("malloc"); exit(EXIT_FAILURE); } reedSolomonComputeDivisor((int)blockEccLen, generator); for (size_t i = 0, k = 0; i < numBlocks; i++) { uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t)); if (block == NULL) { perror("malloc"); exit(EXIT_FAILURE); } size_t datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); memcpy(block, &data[k], datLen * sizeof(uint8_t)); reedSolomonComputeRemainder(&data[k], (int)datLen, generator, (int)blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); k += datLen; blocks[i] = block; } free(generator); // Interleave (not concatenate) the bytes from every block into a single sequence uint8_t *result = malloc(rawCodewords * sizeof(uint8_t)); if (result == NULL) { perror("malloc"); exit(EXIT_FAILURE); } for (size_t i = 0, k = 0; i < shortBlockLen + 1; i++) { for (size_t j = 0; j < numBlocks; j++) { // Skip the padding byte in short blocks if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { result[k] = blocks[j][i]; k++; } } } for (size_t i = 0; i < numBlocks; i++) free(blocks[i]); free(blocks); return result; } static void testAddEccAndInterleave(void) { for (int version = 1; version <= 40; version++) { for (int ecl = 0; ecl < 4; ecl++) { size_t dataLen = (size_t)getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); uint8_t *pureData = malloc(dataLen * sizeof(uint8_t)); if (pureData == NULL) { perror("malloc"); exit(EXIT_FAILURE); } for (size_t i = 0; i < dataLen; i++) pureData[i] = (uint8_t)(rand() % 256); uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl); size_t dataAndEccLen = (size_t)getNumRawDataModules(version) / 8; uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t)); if (paddedData == NULL) { perror("malloc"); exit(EXIT_FAILURE); } memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t)); if (actualOutput == NULL) { perror("malloc"); exit(EXIT_FAILURE); } addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); free(pureData); free(expectOutput); free(paddedData); free(actualOutput); numTestCases++; } } } static void testGetNumDataCodewords(void) { const int cases[][3] = { { 3, 1, 44}, { 3, 2, 34}, { 3, 3, 26}, { 6, 0, 136}, { 7, 0, 156}, { 9, 0, 232}, { 9, 1, 182}, {12, 3, 158}, {15, 0, 523}, {16, 2, 325}, {19, 3, 341}, {21, 0, 932}, {22, 0, 1006}, {22, 1, 782}, {22, 3, 442}, {24, 0, 1174}, {24, 3, 514}, {28, 0, 1531}, {30, 3, 745}, {32, 3, 845}, {33, 0, 2071}, {33, 3, 901}, {35, 0, 2306}, {35, 1, 1812}, {35, 2, 1286}, {36, 3, 1054}, {37, 3, 1096}, {39, 1, 2216}, {40, 1, 2334}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { const int *tc = cases[i]; assert(getNumDataCodewords(tc[0], (enum qrcodegen_Ecc)tc[1]) == tc[2]); numTestCases++; } } static void testGetNumRawDataModules(void) { const int cases[][2] = { { 1, 208}, { 2, 359}, { 3, 567}, { 6, 1383}, { 7, 1568}, {12, 3728}, {15, 5243}, {18, 7211}, {22, 10068}, {26, 13652}, {32, 19723}, {37, 25568}, {40, 29648}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { const int *tc = cases[i]; assert(getNumRawDataModules(tc[0]) == tc[1]); numTestCases++; } } static void testReedSolomonComputeDivisor(void) { uint8_t generator[30]; reedSolomonComputeDivisor(1, generator); assert(generator[0] == 0x01); numTestCases++; reedSolomonComputeDivisor(2, generator); assert(generator[0] == 0x03); assert(generator[1] == 0x02); numTestCases++; reedSolomonComputeDivisor(5, generator); assert(generator[0] == 0x1F); assert(generator[1] == 0xC6); assert(generator[2] == 0x3F); assert(generator[3] == 0x93); assert(generator[4] == 0x74); numTestCases++; reedSolomonComputeDivisor(30, generator); assert(generator[ 0] == 0xD4); assert(generator[ 1] == 0xF6); assert(generator[ 5] == 0xC0); assert(generator[12] == 0x16); assert(generator[13] == 0xD9); assert(generator[20] == 0x12); assert(generator[27] == 0x6A); assert(generator[29] == 0x96); numTestCases++; } static void testReedSolomonComputeRemainder(void) { { uint8_t data[1]; uint8_t generator[3]; uint8_t remainder[ARRAY_LENGTH(generator)]; reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); reedSolomonComputeRemainder(data, 0, generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == 0); assert(remainder[1] == 0); assert(remainder[2] == 0); numTestCases++; } { uint8_t data[2] = {0, 1}; uint8_t generator[4]; uint8_t remainder[ARRAY_LENGTH(generator)]; reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == generator[0]); assert(remainder[1] == generator[1]); assert(remainder[2] == generator[2]); assert(remainder[3] == generator[3]); numTestCases++; } { uint8_t data[5] = {0x03, 0x3A, 0x60, 0x12, 0xC7}; uint8_t generator[5]; uint8_t remainder[ARRAY_LENGTH(generator)]; reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == 0xCB); assert(remainder[1] == 0x36); assert(remainder[2] == 0x16); assert(remainder[3] == 0xFA); assert(remainder[4] == 0x9D); numTestCases++; } { uint8_t data[43] = { 0x38, 0x71, 0xDB, 0xF9, 0xD7, 0x28, 0xF6, 0x8E, 0xFE, 0x5E, 0xE6, 0x7D, 0x7D, 0xB2, 0xA5, 0x58, 0xBC, 0x28, 0x23, 0x53, 0x14, 0xD5, 0x61, 0xC0, 0x20, 0x6C, 0xDE, 0xDE, 0xFC, 0x79, 0xB0, 0x8B, 0x78, 0x6B, 0x49, 0xD0, 0x1A, 0xAD, 0xF3, 0xEF, 0x52, 0x7D, 0x9A, }; uint8_t generator[30]; uint8_t remainder[ARRAY_LENGTH(generator)]; reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[ 0] == 0xCE); assert(remainder[ 1] == 0xF0); assert(remainder[ 2] == 0x31); assert(remainder[ 3] == 0xDE); assert(remainder[ 8] == 0xE1); assert(remainder[12] == 0xCA); assert(remainder[17] == 0xE3); assert(remainder[19] == 0x85); assert(remainder[20] == 0x50); assert(remainder[24] == 0xBE); assert(remainder[29] == 0xB3); numTestCases++; } } static void testReedSolomonMultiply(void) { const uint8_t cases[][3] = { {0x00, 0x00, 0x00}, {0x01, 0x01, 0x01}, {0x02, 0x02, 0x04}, {0x00, 0x6E, 0x00}, {0xB2, 0xDD, 0xE6}, {0x41, 0x11, 0x25}, {0xB0, 0x1F, 0x11}, {0x05, 0x75, 0xBC}, {0x52, 0xB5, 0xAE}, {0xA8, 0x20, 0xA4}, {0x0E, 0x44, 0x9F}, {0xD4, 0x13, 0xA0}, {0x31, 0x10, 0x37}, {0x6C, 0x58, 0xCB}, {0xB6, 0x75, 0x3E}, {0xFF, 0xFF, 0xE2}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { const uint8_t *tc = cases[i]; assert(reedSolomonMultiply(tc[0], tc[1]) == tc[2]); numTestCases++; } } static void testInitializeFunctionModulesEtc(void) { for (int ver = 1; ver <= 40; ver++) { uint8_t *qrcode = malloc((size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(ver) * sizeof(uint8_t)); if (qrcode == NULL) { perror("malloc"); exit(EXIT_FAILURE); } initializeFunctionModules(ver, qrcode); int size = qrcodegen_getSize(qrcode); if (ver == 1) assert(size == 21); else if (ver == 40) assert(size == 177); else assert(size == ver * 4 + 17); bool hasLight = false; bool hasDark = false; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { bool color = qrcodegen_getModule(qrcode, x, y); if (color) hasDark = true; else hasLight = true; } } assert(hasLight && hasDark); free(qrcode); numTestCases++; } } static void testGetAlignmentPatternPositions(void) { const int cases[][9] = { { 1, 0, -1, -1, -1, -1, -1, -1, -1}, { 2, 2, 6, 18, -1, -1, -1, -1, -1}, { 3, 2, 6, 22, -1, -1, -1, -1, -1}, { 6, 2, 6, 34, -1, -1, -1, -1, -1}, { 7, 3, 6, 22, 38, -1, -1, -1, -1}, { 8, 3, 6, 24, 42, -1, -1, -1, -1}, {16, 4, 6, 26, 50, 74, -1, -1, -1}, {25, 5, 6, 32, 58, 84, 110, -1, -1}, {32, 6, 6, 34, 60, 86, 112, 138, -1}, {33, 6, 6, 30, 58, 86, 114, 142, -1}, {39, 7, 6, 26, 54, 82, 110, 138, 166}, {40, 7, 6, 30, 58, 86, 114, 142, 170}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { const int *tc = cases[i]; uint8_t pos[7]; int num = getAlignmentPatternPositions(tc[0], pos); assert(num == tc[1]); for (int j = 0; j < num; j++) assert(pos[j] == tc[2 + j]); numTestCases++; } } static void testGetSetModule(void) { uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(23)]; initializeFunctionModules(23, qrcode); int size = qrcodegen_getSize(qrcode); for (int y = 0; y < size; y++) { // Clear all to light for (int x = 0; x < size; x++) setModule(qrcode, x, y, false); } for (int y = 0; y < size; y++) { // Check all light for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == false); } for (int y = 0; y < size; y++) { // Set all to dark for (int x = 0; x < size; x++) setModule(qrcode, x, y, true); } for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } // Set some out of bounds modules to light setModuleUnbounded(qrcode, -1, -1, false); setModuleUnbounded(qrcode, -1, 0, false); setModuleUnbounded(qrcode, 0, -1, false); setModuleUnbounded(qrcode, size, 5, false); setModuleUnbounded(qrcode, 72, size, false); setModuleUnbounded(qrcode, size, size, false); for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } // Set some modules to light setModule(qrcode, 3, 8, false); setModule(qrcode, 61, 49, false); for (int y = 0; y < size; y++) { // Check most dark for (int x = 0; x < size; x++) { bool light = (x == 3 && y == 8) || (x == 61 && y == 49); assert(qrcodegen_getModule(qrcode, x, y) != light); } } numTestCases++; } static void testGetSetModuleRandomly(void) { uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(1)]; initializeFunctionModules(1, qrcode); int size = qrcodegen_getSize(qrcode); bool modules[21][21]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) modules[y][x] = qrcodegen_getModule(qrcode, x, y); } long trials = 100000; for (long i = 0; i < trials; i++) { int x = rand() % (size * 2) - size / 2; int y = rand() % (size * 2) - size / 2; bool isInBounds = 0 <= x && x < size && 0 <= y && y < size; bool oldColor = isInBounds && modules[y][x]; if (isInBounds) assert(getModule(qrcode, x, y) == oldColor); assert(qrcodegen_getModule(qrcode, x, y) == oldColor); bool newColor = rand() % 2 == 0; if (isInBounds) modules[y][x] = newColor; if (isInBounds && rand() % 2 == 0) setModule(qrcode, x, y, newColor); else setModuleUnbounded(qrcode, x, y, newColor); } numTestCases++; } static void testIsAlphanumeric(void) { struct TestCase { bool answer; const char *text; }; const struct TestCase cases[] = { {true, ""}, {true, "0"}, {true, "A"}, {false, "a"}, {true, " "}, {true, "."}, {true, "*"}, {false, ","}, {false, "|"}, {false, "@"}, {true, "XYZ"}, {false, "XYZ!"}, {true, "79068"}, {true, "+123 ABC$"}, {false, "\x01"}, {false, "\x7F"}, {false, "\x80"}, {false, "\xC0"}, {false, "\xFF"}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_isAlphanumeric(cases[i].text) == cases[i].answer); numTestCases++; } } static void testIsNumeric(void) { struct TestCase { bool answer; const char *text; }; const struct TestCase cases[] = { {true, ""}, {true, "0"}, {false, "A"}, {false, "a"}, {false, " "}, {false, "."}, {false, "*"}, {false, ","}, {false, "|"}, {false, "@"}, {false, "XYZ"}, {false, "XYZ!"}, {true, "79068"}, {false, "+123 ABC$"}, {false, "\x01"}, {false, "\x7F"}, {false, "\x80"}, {false, "\xC0"}, {false, "\xFF"}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_isNumeric(cases[i].text) == cases[i].answer); numTestCases++; } } static void testCalcSegmentBufferSize(void) { { const size_t cases[][2] = { {0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 2}, {5, 3}, {6, 3}, {1472, 614}, {2097, 874}, {5326, 2220}, {9828, 4095}, {9829, 4096}, {9830, 4096}, {9831, SIZE_MAX}, {9832, SIZE_MAX}, {12000, SIZE_MAX}, {28453, SIZE_MAX}, {55555, SIZE_MAX}, {SIZE_MAX / 6, SIZE_MAX}, {SIZE_MAX / 4, SIZE_MAX}, {SIZE_MAX / 2, SIZE_MAX}, {SIZE_MAX / 1, SIZE_MAX}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, cases[i][0]) == cases[i][1]); numTestCases++; } } { const size_t cases[][2] = { {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 3}, {5, 4}, {6, 5}, {1472, 1012}, {2097, 1442}, {5326, 3662}, {5955, 4095}, {5956, 4095}, {5957, 4096}, {5958, SIZE_MAX}, {5959, SIZE_MAX}, {12000, SIZE_MAX}, {28453, SIZE_MAX}, {55555, SIZE_MAX}, {SIZE_MAX / 10, SIZE_MAX}, {SIZE_MAX / 8, SIZE_MAX}, {SIZE_MAX / 5, SIZE_MAX}, {SIZE_MAX / 2, SIZE_MAX}, {SIZE_MAX / 1, SIZE_MAX}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, cases[i][0]) == cases[i][1]); numTestCases++; } } { const size_t cases[][2] = { {0, 0}, {1, 1}, {2, 2}, {3, 3}, {1472, 1472}, {2097, 2097}, {4094, 4094}, {4095, 4095}, {4096, SIZE_MAX}, {4097, SIZE_MAX}, {5957, SIZE_MAX}, {12000, SIZE_MAX}, {28453, SIZE_MAX}, {55555, SIZE_MAX}, {SIZE_MAX / 16 + 1, SIZE_MAX}, {SIZE_MAX / 14, SIZE_MAX}, {SIZE_MAX / 9, SIZE_MAX}, {SIZE_MAX / 7, SIZE_MAX}, {SIZE_MAX / 4, SIZE_MAX}, {SIZE_MAX / 3, SIZE_MAX}, {SIZE_MAX / 2, SIZE_MAX}, {SIZE_MAX / 1, SIZE_MAX}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_BYTE, cases[i][0]) == cases[i][1]); numTestCases++; } } { const size_t cases[][2] = { {0, 0}, {1, 2}, {2, 4}, {3, 5}, {1472, 2392}, {2097, 3408}, {2519, 4094}, {2520, 4095}, {2521, SIZE_MAX}, {5957, SIZE_MAX}, {2522, SIZE_MAX}, {12000, SIZE_MAX}, {28453, SIZE_MAX}, {55555, SIZE_MAX}, {SIZE_MAX / 13 + 1, SIZE_MAX}, {SIZE_MAX / 12, SIZE_MAX}, {SIZE_MAX / 9, SIZE_MAX}, {SIZE_MAX / 4, SIZE_MAX}, {SIZE_MAX / 3, SIZE_MAX}, {SIZE_MAX / 2, SIZE_MAX}, {SIZE_MAX / 1, SIZE_MAX}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_KANJI, cases[i][0]) == cases[i][1]); numTestCases++; } } { assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ECI, 0) == 3); numTestCases++; } } static void testCalcSegmentBitLength(void) { struct TestCase { size_t numChars; int result; }; { const struct TestCase CASES[] = { {0, 0}, {1, 4}, {2, 7}, {3, 10}, {4, 14}, {5, 17}, {6, 20}, {1472, 4907}, {2097, 6990}, {5326, 17754}, {9828, 32760}, {9829, 32764}, {9830, 32767}, {9831, -1}, {9832, -1}, {12000, -1}, {28453, -1}, {SIZE_MAX / 6, -1}, {SIZE_MAX / 3, -1}, {SIZE_MAX / 2, -1}, {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { const struct TestCase CASES[] = { {0, 0}, {1, 6}, {2, 11}, {3, 17}, {4, 22}, {5, 28}, {6, 33}, {1472, 8096}, {2097, 11534}, {5326, 29293}, {5955, 32753}, {5956, 32758}, {5957, 32764}, {5958, -1}, {5959, -1}, {12000, -1}, {28453, -1}, {SIZE_MAX / 10, -1}, {SIZE_MAX / 5, -1}, {SIZE_MAX / 2, -1}, {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { const struct TestCase CASES[] = { {0, 0}, {1, 8}, {2, 16}, {3, 24}, {1472, 11776}, {2097, 16776}, {4094, 32752}, {4095, 32760}, {4096, -1}, {4097, -1}, {5957, -1}, {12000, -1}, {28453, -1}, {SIZE_MAX / 15, -1}, {SIZE_MAX / 12, -1}, {SIZE_MAX / 7, -1}, {SIZE_MAX / 3, -1}, {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { const struct TestCase CASES[] = { {0, 0}, {1, 13}, {2, 26}, {3, 39}, {1472, 19136}, {2097, 27261}, {2519, 32747}, {2520, 32760}, {2521, -1}, {5957, -1}, {2522, -1}, {12000, -1}, {28453, -1}, {SIZE_MAX / 25, -1}, {SIZE_MAX / 20, -1}, {SIZE_MAX / 11, -1}, {SIZE_MAX / 4, -1}, {SIZE_MAX / 2, -1}, {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { assert(calcSegmentBitLength(qrcodegen_Mode_ECI, 0) == 24); numTestCases++; } } static void testMakeBytes(void) { { struct qrcodegen_Segment seg = qrcodegen_makeBytes(NULL, 0, NULL); assert(seg.mode == qrcodegen_Mode_BYTE); assert(seg.numChars == 0); assert(seg.bitLength == 0); numTestCases++; } { const uint8_t data[] = {0x00}; uint8_t buf[1]; struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 1, buf); assert(seg.numChars == 1); assert(seg.bitLength == 8); assert(seg.data[0] == 0x00); numTestCases++; } { const uint8_t data[] = {0xEF, 0xBB, 0xBF}; uint8_t buf[3]; struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 3, buf); assert(seg.numChars == 3); assert(seg.bitLength == 24); assert(seg.data[0] == 0xEF); assert(seg.data[1] == 0xBB); assert(seg.data[2] == 0xBF); numTestCases++; } } static void testMakeNumeric(void) { { struct qrcodegen_Segment seg = qrcodegen_makeNumeric("", NULL); assert(seg.mode == qrcodegen_Mode_NUMERIC); assert(seg.numChars == 0); assert(seg.bitLength == 0); numTestCases++; } { uint8_t buf[1]; struct qrcodegen_Segment seg = qrcodegen_makeNumeric("9", buf); assert(seg.numChars == 1); assert(seg.bitLength == 4); assert(seg.data[0] == 0x90); numTestCases++; } { uint8_t buf[1]; struct qrcodegen_Segment seg = qrcodegen_makeNumeric("81", buf); assert(seg.numChars == 2); assert(seg.bitLength == 7); assert(seg.data[0] == 0xA2); numTestCases++; } { uint8_t buf[2]; struct qrcodegen_Segment seg = qrcodegen_makeNumeric("673", buf); assert(seg.numChars == 3); assert(seg.bitLength == 10); assert(seg.data[0] == 0xA8); assert(seg.data[1] == 0x40); numTestCases++; } { uint8_t buf[5]; struct qrcodegen_Segment seg = qrcodegen_makeNumeric("3141592653", buf); assert(seg.numChars == 10); assert(seg.bitLength == 34); assert(seg.data[0] == 0x4E); assert(seg.data[1] == 0x89); assert(seg.data[2] == 0xF4); assert(seg.data[3] == 0x24); assert(seg.data[4] == 0xC0); numTestCases++; } } static void testMakeAlphanumeric(void) { { struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("", NULL); assert(seg.mode == qrcodegen_Mode_ALPHANUMERIC); assert(seg.numChars == 0); assert(seg.bitLength == 0); numTestCases++; } { uint8_t buf[1]; struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("A", buf); assert(seg.numChars == 1); assert(seg.bitLength == 6); assert(seg.data[0] == 0x28); numTestCases++; } { uint8_t buf[2]; struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("%:", buf); assert(seg.numChars == 2); assert(seg.bitLength == 11); assert(seg.data[0] == 0xDB); assert(seg.data[1] == 0x40); numTestCases++; } { uint8_t buf[3]; struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("Q R", buf); assert(seg.numChars == 3); assert(seg.bitLength == 17); assert(seg.data[0] == 0x96); assert(seg.data[1] == 0xCD); assert(seg.data[2] == 0x80); numTestCases++; } } static void testMakeEci(void) { { uint8_t buf[1]; struct qrcodegen_Segment seg = qrcodegen_makeEci(127, buf); assert(seg.mode == qrcodegen_Mode_ECI); assert(seg.numChars == 0); assert(seg.bitLength == 8); assert(seg.data[0] == 0x7F); numTestCases++; } { uint8_t buf[2]; struct qrcodegen_Segment seg = qrcodegen_makeEci(10345, buf); assert(seg.numChars == 0); assert(seg.bitLength == 16); assert(seg.data[0] == 0xA8); assert(seg.data[1] == 0x69); numTestCases++; } { uint8_t buf[3]; struct qrcodegen_Segment seg = qrcodegen_makeEci(999999, buf); assert(seg.numChars == 0); assert(seg.bitLength == 24); assert(seg.data[0] == 0xCF); assert(seg.data[1] == 0x42); assert(seg.data[2] == 0x3F); numTestCases++; } } static void testGetTotalBits(void) { { assert(getTotalBits(NULL, 0, 1) == 0); numTestCases++; assert(getTotalBits(NULL, 0, 40) == 0); numTestCases++; } { struct qrcodegen_Segment segs[] = { {qrcodegen_Mode_BYTE, 3, NULL, 24}, }; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 2) == 36); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 44); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 39) == 44); numTestCases++; } { struct qrcodegen_Segment segs[] = { {qrcodegen_Mode_ECI, 0, NULL, 8}, {qrcodegen_Mode_NUMERIC, 7, NULL, 24}, {qrcodegen_Mode_ALPHANUMERIC, 1, NULL, 6}, {qrcodegen_Mode_KANJI, 4, NULL, 52}, }; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 133); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 21) == 139); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 145); numTestCases++; } { struct qrcodegen_Segment segs[] = { {qrcodegen_Mode_BYTE, 4093, NULL, 32744}, }; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32764); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 32764); numTestCases++; } { struct qrcodegen_Segment segs[] = { {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, {qrcodegen_Mode_NUMERIC, 1617, NULL, 5390}, }; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32766); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == -1); numTestCases++; } { struct qrcodegen_Segment segs[] = { {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_KANJI, 255, NULL, 3315}, {qrcodegen_Mode_ALPHANUMERIC, 511, NULL, 2811}, }; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 32767); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 26) == -1); numTestCases++; assert(getTotalBits(segs, ARRAY_LENGTH(segs), 40) == -1); numTestCases++; } } /*---- Main runner ----*/ int main(void) { srand((unsigned int)time(NULL)); testAppendBitsToBuffer(); testAddEccAndInterleave(); testGetNumDataCodewords(); testGetNumRawDataModules(); testReedSolomonComputeDivisor(); testReedSolomonComputeRemainder(); testReedSolomonMultiply(); testInitializeFunctionModulesEtc(); testGetAlignmentPatternPositions(); testGetSetModule(); testGetSetModuleRandomly(); testIsAlphanumeric(); testIsNumeric(); testCalcSegmentBufferSize(); testCalcSegmentBitLength(); testMakeBytes(); testMakeNumeric(); testMakeAlphanumeric(); testMakeEci(); testGetTotalBits(); printf("All %d test cases passed\n", numTestCases); return EXIT_SUCCESS; }