summaryrefslogtreecommitdiff
path: root/docs/handbook/genqrcode/testing.md
blob: d606302ee5d1e37aa4a12735b97d550812eff808 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
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 && \
```