summaryrefslogtreecommitdiff
path: root/docs/handbook/genqrcode/testing.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/genqrcode/testing.md')
-rw-r--r--docs/handbook/genqrcode/testing.md398
1 files changed, 398 insertions, 0 deletions
diff --git a/docs/handbook/genqrcode/testing.md b/docs/handbook/genqrcode/testing.md
new file mode 100644
index 0000000000..d606302ee5
--- /dev/null
+++ b/docs/handbook/genqrcode/testing.md
@@ -0,0 +1,398 @@
+# genqrcode / libqrencode — Testing
+
+## Test Infrastructure
+
+### Test Framework
+
+libqrencode uses a custom test framework defined in `tests/common.h`:
+
+```c
+#define testStart(__arg__) { \
+ int tests = 0, failed = 0, assertion; \
+ char *_test_name = (__arg__); \
+ fputs("--- TEST: ", stderr); \
+ fputs(_test_name, stderr); \
+ fputs(" ---\n", stderr);
+
+#define testEnd() \
+ if(failed) fputs("FAILED.\n", stderr); \
+ else fputs("PASSED.\n", stderr); \
+ testFinish(); }
+
+#define testFinish() \
+ printf(" %s: %d assertions, %d failures.\n", \
+ _test_name, tests, failed);
+
+#define assert_equal(__actual__, __expected__, ...) { \
+ tests++; assertion = (__actual__ == __expected__); \
+ if(!assertion) { \
+ failed++; \
+ printf("FAILED: " __VA_ARGS__); \
+ printf(" (%d != %d)\n", __actual__, __expected__); \
+ } }
+
+#define assert_zero(__expr__, ...) \
+ assert_equal((__expr__), 0, __VA_ARGS__)
+
+#define assert_nonzero(__expr__, ...) { \
+ tests++; assertion = (__expr__) != 0; \
+ if(!assertion) { \
+ failed++; \
+ printf("FAILED: " __VA_ARGS__); \
+ printf("\n"); \
+ } }
+
+#define assert_null(__expr__, ...) { \
+ tests++; assertion = (__expr__) == NULL; \
+ if(!assertion) { \
+ failed++; \
+ printf("FAILED: " __VA_ARGS__); \
+ printf("\n"); \
+ } }
+
+#define assert_nonnull(__expr__, ...) { \
+ tests++; assertion = (__expr__) != NULL; \
+ if(!assertion) { \
+ failed++; \
+ printf("FAILED: " __VA_ARGS__); \
+ printf("\n"); \
+ } }
+
+#define assert_nothing(__expr__, ...) { \
+ tests++; __expr__; }
+```
+
+Test functions return void and use `testStart()`/`testEnd()` blocks. Assertions increment the `tests` counter and only print output on failure.
+
+---
+
+## Test Programs
+
+### test_bitstream
+
+Tests the `BitStream` module:
+
+- `test_null()` — New stream has length 0
+- `test_num()` — `BitStream_appendNum()` with various bit widths
+- `test_bytes()` — `BitStream_appendBytes()` correctness
+- `test_toByte()` — Bit packing from 1-bit-per-byte to 8-bits-per-byte
+
+### test_estimatebit
+
+Tests bit estimation functions:
+
+- `test_estimateBitsModeNum()` — Numeric mode bit cost for various lengths
+- `test_estimateBitsModeAn()` — Alphanumeric mode bit cost
+- `test_estimateBitsMode8()` — 8-bit mode bit cost
+- `test_estimateBitsModeKanji()` — Kanji mode bit cost
+
+### test_qrinput
+
+Tests input data management:
+
+- `test_encodeNumeric()` — Numeric data encoding correctness
+- `test_encodeAn()` — Alphanumeric encoding with the AN table
+- `test_encode8()` — 8-bit byte encoding
+- `test_encodeKanji()` — Kanji double-byte encoding
+- `test_encodeTooLong()` — Overflow detection (ERANGE)
+- `test_struct()` — Structured append input handling
+- `test_splitEntry()` — Input entry splitting for version changes
+- `test_parity()` — Parity calculation for structured append
+- `test_padding()` — Pad byte alternation (0xEC/0x11)
+- `test_mqr()` — Micro QR input creation and validation
+
+### test_qrspec
+
+Tests QR Code specification tables:
+
+- `test_getWidth()` — Width = version × 4 + 17
+- `test_getDataLength()` — Data capacity lookup
+- `test_getECCLength()` — ECC length calculation
+- `test_alignment()` — Alignment pattern positions
+- `test_versionPattern()` — Version info BCH codes
+- `test_formatInfo()` — Format info BCH codes
+
+### test_mqrspec
+
+Tests Micro QR spec tables:
+
+- `test_getWidth()` — Width = version × 2 + 9
+- `test_getDataLengthBit()` — Bit-level data capacity
+- `test_getECCLength()` — MQR ECC lengths
+- `test_newFrame()` — Frame creation with single finder pattern
+
+### test_rs
+
+Tests Reed-Solomon encoder:
+
+- Tests `RSECC_encode()` with known data/ECC pairs
+
+### test_qrencode
+
+Tests the high-level encoding pipeline:
+
+- `test_encode()` — Full encode and verify symbol dimensions
+- `test_encodeVersion()` — Version auto-selection
+- `test_encodeMQR()` — Micro QR encoding
+- `test_encode8bitString()` — 8-bit string encoding
+- `test_encodeStructured()` — Structured append output
+- `test_encodeMask()` — Forced mask testing
+- `test_encodeEmpty()` — Empty input handling
+
+### test_split
+
+Tests the string splitter:
+
+- `test_split1()` — Pure numeric string
+- `test_split2()` — Pure alphanumeric
+- `test_split3()` — Mixed numeric/alpha/8-bit
+- `test_split4()` — Kanji detection
+- `test_split5()` — Mode boundary optimization
+
+### test_split_urls
+
+Tests URL splitting efficiency:
+
+- Tests that common URL patterns produce efficient mode sequences
+- Verifies that `http://`, domain names, and paths are encoded optimally
+
+### test_mask
+
+Tests full QR masking:
+
+- Tests all 8 mask patterns (`Mask_mask0` through `Mask_mask7`)
+- `test_eval()` — Penalty evaluation
+- `test_format()` — Format information writing
+- `test_maskSelection()` — Automatic mask selection picks minimum penalty
+
+### test_mmask
+
+Tests Micro QR masking:
+
+- Tests 4 mask patterns (`MMask_mask0` through `MMask_mask3`)
+- `test_eval()` — Micro QR scoring (maximize, not minimize)
+- `test_format()` — MQR format information
+
+### test_monkey
+
+Random/fuzz testing:
+
+- Generates random input data of various sizes and modes
+- Encodes and verifies the result is non-NULL and has valid dimensions
+
+---
+
+## Utility Programs
+
+### prof_qrencode
+
+Profiling harness. Encodes the same string many times for performance measurement:
+
+```c
+int main() {
+ // Repeated encoding for profiling with gprof/gcov
+}
+```
+
+Build with `GPROF` or `COVERAGE` CMake options.
+
+### pthread_qrencode
+
+Thread safety test. Spawns multiple threads encoding simultaneously:
+
+```c
+// Tests concurrent RSECC_encode() calls
+// Verifies no crashes or data corruption
+```
+
+Only built when `HAVE_LIBPTHREAD` is defined.
+
+### view_qrcode
+
+Renders a QR code to the terminal for visual inspection:
+
+```c
+// ASCII art output of encoded symbol
+```
+
+### create_frame_pattern
+
+Generates and prints the function pattern frame (finder, timing, alignment):
+
+```c
+// Used to visually verify frame creation
+```
+
+---
+
+## Running Tests
+
+### Via CMake
+
+```bash
+mkdir build && cd build
+cmake -DWITH_TESTS=ON ..
+make
+make test
+# or:
+ctest --verbose
+```
+
+CMake `tests/CMakeLists.txt` registers test executables:
+
+```cmake
+add_executable(test_qrinput test_qrinput.c common.c)
+target_link_libraries(test_qrinput qrencode_static)
+add_test(test_qrinput test_qrinput)
+
+add_executable(test_bitstream test_bitstream.c common.c)
+target_link_libraries(test_bitstream qrencode_static)
+add_test(test_bitstream test_bitstream)
+
+# ... similar for all test programs
+```
+
+### Via Autotools
+
+```bash
+./configure --with-tests
+make
+make check
+```
+
+Or use the test scripts directly:
+
+```bash
+cd tests
+./test_basic.sh
+```
+
+### test_basic.sh
+
+```bash
+#!/bin/sh
+./test_bitstream && \
+./test_estimatebit && \
+./test_qrinput && \
+./test_qrspec && \
+./test_mqrspec && \
+./test_rs && \
+./test_qrencode && \
+./test_split && \
+./test_split_urls && \
+./test_mask && \
+./test_mmask && \
+./test_monkey
+```
+
+Runs all test executables sequentially. Exits on first failure due to `&&` chaining.
+
+### test_all.sh
+
+Runs `test_basic.sh` plus `test_configure.sh` (which tests the autotools configure/build cycle).
+
+---
+
+## Test Build Macros
+
+When tests are enabled:
+
+- `WITH_TESTS` is defined → `STATIC_IN_RELEASE` is NOT defined
+- All `STATIC_IN_RELEASE` functions become externally visible
+- `qrencode_inner.h` exposes internal types (`RSblock`, `QRRawCode`, etc.)
+
+This allows tests to call internal functions directly:
+
+```c
+// In test_mask.c:
+#include "../qrencode_inner.h"
+
+void test_mask() {
+ testStart("Testing mask evaluation");
+ QRcode *code = QRcode_encodeMask(input, 0); // force mask 0
+ // ... verify mask application ...
+ testEnd();
+}
+```
+
+---
+
+## Code Coverage
+
+### With gcov (Autotools)
+
+```bash
+./configure --enable-gcov --with-tests
+make
+make check
+gcov *.c
+```
+
+### With CMake
+
+```bash
+cmake -DCOVERAGE=ON -DWITH_TESTS=ON ..
+make
+make test
+# generate coverage report
+```
+
+CMake adds `-fprofile-arcs -ftest-coverage` flags when `COVERAGE` is ON.
+
+---
+
+## Address Sanitizer
+
+```bash
+# CMake:
+cmake -DASAN=ON -DWITH_TESTS=ON ..
+
+# Autotools:
+./configure --enable-asan --with-tests
+```
+
+Adds `-fsanitize=address -fno-omit-frame-pointer` for memory error detection.
+
+---
+
+## Adding a New Test
+
+1. Create `tests/test_newmodule.c`:
+```c
+#include <stdio.h>
+#include "common.h"
+#include "../module.h"
+
+void test_something(void)
+{
+ testStart("Testing something");
+ int result = module_function(42);
+ assert_equal(result, 84, "module_function(42)");
+ testEnd();
+}
+
+int main(void)
+{
+ test_something();
+ return 0;
+}
+```
+
+2. Add to `tests/CMakeLists.txt`:
+```cmake
+add_executable(test_newmodule test_newmodule.c common.c)
+target_link_libraries(test_newmodule qrencode_static)
+add_test(test_newmodule test_newmodule)
+```
+
+3. Add to `tests/Makefile.am`:
+```makefile
+noinst_PROGRAMS += test_newmodule
+test_newmodule_SOURCES = test_newmodule.c common.h common.c
+test_newmodule_LDADD = ../libqrencode.la
+```
+
+4. Add to `tests/test_basic.sh`:
+```bash
+./test_newmodule && \
+```