-
-
Notifications
You must be signed in to change notification settings - Fork 302
/
QRCodeReaderTestAbstract.php
176 lines (147 loc) · 6.6 KB
/
QRCodeReaderTestAbstract.php
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
<?php
/**
* Class QRCodeReaderTestAbstract
*
* @created 17.01.2021
* @author Smiley <[email protected]>
* @copyright 2021 Smiley
* @license MIT
*
* @noinspection PhpComposerExtensionStubsInspection
*/
namespace chillerlan\QRCodeTest;
use chillerlan\QRCode\{QRCode, QROptions};
use chillerlan\QRCode\Common\{EccLevel, LuminanceSourceInterface, Mode, Version};
use chillerlan\QRCode\Decoder\Decoder;
use chillerlan\QRCode\Output\QRGdImagePNG;
use chillerlan\Settings\SettingsContainerInterface;
use PHPUnit\Framework\Attributes\{DataProvider, Group};
use PHPUnit\Framework\TestCase;
use Exception, Generator;
use function array_map, defined, realpath, sprintf, str_repeat, substr;
/**
* Tests the QR Code reader
*/
abstract class QRCodeReaderTestAbstract extends TestCase{
use QRMaxLengthTrait, QRMatrixDebugTrait;
/** @see https://www.bobrosslipsum.com/ */
protected const loremipsum = 'Just let this happen. We just let this flow right out of our minds. '
.'Anyone can paint. We touch the canvas, the canvas takes what it wants. From all of us here, '
.'I want to wish you happy painting and God bless, my friends. A tree cannot be straight if it has a crooked trunk. '
.'You have to make almighty decisions when you\'re the creator. I guess that would be considered a UFO. '
.'A big cotton ball in the sky. I\'m gonna add just a tiny little amount of Prussian Blue. '
.'They say everything looks better with odd numbers of things. But sometimes I put even numbers—just '
.'to upset the critics. We\'ll lay all these little funky little things in there. ';
protected const samplesDir = __DIR__.'/samples/';
protected SettingsContainerInterface|QROptions $options;
protected function setUp():void{
if(!defined('READER_TEST_MAX_VERSION')){
$this::markTestSkipped('READER_TEST_MAX_VERSION not defined (see phpunit.xml.dist)');
}
$this->options = new QROptions;
$this->options->readerUseImagickIfAvailable = false;
}
public static function qrCodeProvider():array{
return [
'helloworld' => ['hello_world.png', 'Hello world!', false],
// covers mirroring
'mirrored' => ['hello_world_mirrored.png', 'Hello world!', false],
// data modes
'byte' => ['byte.png', 'https://smiley.codes/qrcode/', true],
'numeric' => ['numeric.png', '123456789012345678901234567890', false],
'alphanum' => ['alphanum.png', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', false],
'kanji' => ['kanji.png', '茗荷茗荷茗荷茗荷', false],
// covers most of ReedSolomonDecoder
'damaged' => ['damaged.png', 'https://smiley.codes/qrcode/', false],
// covers attempt to read 2nd (bottom left) version info
'version2' => ['damaged_version.png', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', false],
// covers Binarizer::getHistogramBlackMatrix()
'smol' => ['smol.png', 'https://smiley.codes/qrcode/', false],
// tilted 22° CCW
'tilted' => ['tilted.png', 'Hello world!', false],
// rotated 90° CW
'rotated' => ['rotated.png', 'Hello world!', false],
// color gradient (from old svg example)
'gradient' => ['example_svg.png', 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s', true],
// color gradient (from svg example)
'dots' => ['example_svg_dots.png', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', true],
];
}
abstract protected function getLuminanceSourceFromFile(
string $file,
SettingsContainerInterface|QROptions $options
):LuminanceSourceInterface;
#[Group('slow')]
#[DataProvider('qrCodeProvider')]
public function testReader(string $img, string $expected, bool $grayscale):void{
if($grayscale){
$this->options->readerGrayscale = true;
$this->options->readerIncreaseContrast = true;
}
$luminanceSource = $this->getLuminanceSourceFromFile(realpath($this::samplesDir.$img), $this->options);
$result = (new Decoder)->decode($luminanceSource);
$this->debugMatrix($result->getQRMatrix());
$this::assertSame($expected, (string)$result);
}
public function testReaderMultiMode():void{
$this->options->outputInterface = QRGdImagePNG::class;
$this->options->outputBase64 = false;
$numeric = '123456789012345678901234567890';
$alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:';
$kanji = '漂う花の香り';
$hanzi = '无可奈何燃花作香';
$byte = 'https://smiley.codes/qrcode/';
$qrcode = (new QRCode($this->options))
->addNumericSegment($numeric)
->addAlphaNumSegment($alphanum)
->addKanjiSegment($kanji)
->addHanziSegment($hanzi)
->addByteSegment($byte)
;
$result = $qrcode->readFromBlob($qrcode->render());
$this::assertSame($numeric.$alphanum.$kanji.$hanzi.$byte, $result->data);
}
public static function dataTestProvider():Generator{
$str = str_repeat(self::loremipsum, 5);
$eccLevels = array_map(fn(int $ecc):EccLevel => new EccLevel($ecc), [EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H]);
/**
* @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist
* @phan-suppress-next-next-line PhanUndeclaredConstant
*/
for($v = 1; $v <= READER_TEST_MAX_VERSION; $v++){
$version = new Version($v);
foreach($eccLevels as $eccLevel){
yield 'version: '.$version.$eccLevel => [
$version,
$eccLevel,
substr($str, 0, (self::getMaxLengthForMode(Mode::BYTE, $version, $eccLevel) ?? '')),
];
}
}
}
#[Group('slow')]
#[DataProvider('dataTestProvider')]
public function testReadData(Version $version, EccLevel $ecc, string $expected):void{
$this->options->outputInterface = QRGdImagePNG::class;
$this->options->imageTransparent = false;
$this->options->eccLevel = $ecc->getLevel();
$this->options->version = $version->getVersionNumber();
$this->options->outputBase64 = false;
// what's interesting is that a smaller scale seems to produce fewer reader errors???
// usually from version 20 up, independend of the luminance source
// scale 1-2 produces none, scale 3: 1 error, scale 4: 6 errors, scale 5: 5 errors, scale 10: 10 errors
// @see \chillerlan\QRCode\Detector\GridSampler::checkAndNudgePoints()
$this->options->scale = 2;
try{
$qrcode = new QRCode($this->options);
$imagedata = $qrcode->render($expected);
$result = $qrcode->readFromBlob($imagedata);
}
catch(Exception $e){
$this::markTestSkipped(sprintf('skipped version %s%s: %s', $version, $ecc, $e->getMessage()));
}
$this::assertSame($expected, $result->data);
$this::assertSame($version->getVersionNumber(), $result->version->getVersionNumber());
$this::assertSame($ecc->getLevel(), $result->eccLevel->getLevel());
}
}