Subiendo proyecto completo sin restricciones de git ignore

This commit is contained in:
Jose Sanchez
2023-08-17 11:44:02 -04:00
parent a0d4f5ba3b
commit 20f1c60600
19921 changed files with 2509159 additions and 45 deletions

View File

@@ -0,0 +1,222 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\BitArray;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\Version as PHPUnitVersion;
final class BitArrayTest extends TestCase
{
private function getPhpUnitMajorVersion(): int
{
return (int) explode('.', PHPUnitVersion::id())[0];
}
public function testGetSet() : void
{
$array = new BitArray(33);
for ($i = 0; $i < 33; ++$i) {
$this->assertFalse($array->get($i));
$array->set($i);
$this->assertTrue($array->get($i));
}
}
public function testGetNextSet1() : void
{
$array = new BitArray(32);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, 32, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, 32, $array->getNextSet($i));
}
}
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, 33, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, 33, $array->getNextSet($i));
}
}
}
public function testGetNextSet2() : void
{
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, $i <= 31 ? 31 : 33, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, $i <= 31 ? 31 : 33, $array->getNextSet($i));
}
}
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, 32, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, 32, $array->getNextSet($i));
}
}
}
public function testGetNextSet3() : void
{
$array = new BitArray(63);
$array->set(31);
$array->set(32);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($i <= 31) {
$expected = 31;
} elseif ($i <= 32) {
$expected = 32;
} else {
$expected = 63;
}
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, $expected, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, $expected, $array->getNextSet($i));
}
}
}
public function testGetNextSet4() : void
{
$array = new BitArray(63);
$array->set(33);
$array->set(40);
for ($i = 0; $i < $array->getSize(); ++$i) {
if ($i <= 33) {
$expected = 33;
} elseif ($i <= 40) {
$expected = 40;
} else {
$expected = 63;
}
if ($this->getPhpUnitMajorVersion() === 7) {
$this->assertEquals($i, $expected, '', $array->getNextSet($i));
} else {
$this->assertEqualsWithDelta($i, $expected, $array->getNextSet($i));
}
}
}
public function testGetNextSet5() : void
{
mt_srand(0xdeadbeef, MT_RAND_PHP);
for ($i = 0; $i < 10; ++$i) {
$array = new BitArray(mt_rand(1, 100));
$numSet = mt_rand(0, 19);
for ($j = 0; $j < $numSet; ++$j) {
$array->set(mt_rand(0, $array->getSize() - 1));
}
$numQueries = mt_rand(0, 19);
for ($j = 0; $j < $numQueries; ++$j) {
$query = mt_rand(0, $array->getSize() - 1);
$expected = $query;
while ($expected < $array->getSize() && ! $array->get($expected)) {
++$expected;
}
$actual = $array->getNextSet($query);
if ($actual !== $expected) {
$array->getNextSet($query);
}
$this->assertEquals($expected, $actual);
}
}
}
public function testSetBulk() : void
{
$array = new BitArray(64);
$array->setBulk(32, 0xFFFF0000);
for ($i = 0; $i < 48; ++$i) {
$this->assertFalse($array->get($i));
}
for ($i = 48; $i < 64; ++$i) {
$this->assertTrue($array->get($i));
}
}
public function testClear() : void
{
$array = new BitArray(32);
for ($i = 0; $i < 32; ++$i) {
$array->set($i);
}
$array->clear();
for ($i = 0; $i < 32; ++$i) {
$this->assertFalse($array->get($i));
}
}
public function testGetArray() : void
{
$array = new BitArray(64);
$array->set(0);
$array->set(63);
$ints = $array->getBitArray();
$this->assertSame(1, $ints[0]);
$this->assertSame(0x80000000, $ints[1]);
}
public function testIsRange() : void
{
$array = new BitArray(64);
$this->assertTrue($array->isRange(0, 64, false));
$this->assertFalse($array->isRange(0, 64, true));
$array->set(32);
$this->assertTrue($array->isRange(32, 33, true));
$array->set(31);
$this->assertTrue($array->isRange(31, 33, true));
$array->set(34);
$this->assertFalse($array->isRange(31, 35, true));
for ($i = 0; $i < 31; ++$i) {
$array->set($i);
}
$this->assertTrue($array->isRange(0, 33, true));
for ($i = 33; $i < 64; ++$i) {
$array->set($i);
}
$this->assertTrue($array->isRange(0, 64, true));
$this->assertFalse($array->isRange(0, 64, false));
}
}

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\BitMatrix;
use PHPUnit\Framework\TestCase;
class BitMatrixTest extends TestCase
{
public function testGetSet() : void
{
$matrix = new BitMatrix(33);
$this->assertEquals(33, $matrix->getHeight());
for ($y = 0; $y < 33; ++$y) {
for ($x = 0; $x < 33; ++$x) {
if ($y * $x % 3 === 0) {
$matrix->set($x, $y);
}
}
}
for ($y = 0; $y < 33; $y++) {
for ($x = 0; $x < 33; ++$x) {
$this->assertSame(0 === $x * $y % 3, $matrix->get($x, $y));
}
}
}
public function testSetRegion() : void
{
$matrix = new BitMatrix(5);
$matrix->setRegion(1, 1, 3, 3);
for ($y = 0; $y < 5; ++$y) {
for ($x = 0; $x < 5; ++$x) {
$this->assertSame($y >= 1 && $y <= 3 && $x >= 1 && $x <= 3, $matrix->get($x, $y));
}
}
}
public function testRectangularMatrix() : void
{
$matrix = new BitMatrix(75, 20);
$this->assertSame(75, $matrix->getWidth());
$this->assertSame(20, $matrix->getHeight());
$matrix->set(10, 0);
$matrix->set(11, 1);
$matrix->set(50, 2);
$matrix->set(51, 3);
$matrix->flip(74, 4);
$matrix->flip(0, 5);
$this->assertTrue($matrix->get(10, 0));
$this->assertTrue($matrix->get(11, 1));
$this->assertTrue($matrix->get(50, 2));
$this->assertTrue($matrix->get(51, 3));
$this->assertTrue($matrix->get(74, 4));
$this->assertTrue($matrix->get(0, 5));
$matrix->flip(50, 2);
$matrix->flip(51, 3);
$this->assertFalse($matrix->get(50, 2));
$this->assertFalse($matrix->get(51, 3));
}
public function testRectangularSetRegion() : void
{
$matrix = new BitMatrix(320, 240);
$this->assertSame(320, $matrix->getWidth());
$this->assertSame(240, $matrix->getHeight());
$matrix->setRegion(105, 22, 80, 12);
for ($y = 0; $y < 240; ++$y) {
for ($x = 0; $x < 320; ++$x) {
$this->assertEquals($y >= 22 && $y < 34 && $x >= 105 && $x < 185, $matrix->get($x, $y));
}
}
}
public function testGetRow() : void
{
$matrix = new BitMatrix(102, 5);
for ($x = 0; $x < 102; ++$x) {
if (0 === ($x & 3)) {
$matrix->set($x, 2);
}
}
$array1 = $matrix->getRow(2, null);
$this->assertSame(102, $array1->getSize());
$array2 = new BitArray(60);
$array2 = $matrix->getRow(2, $array2);
$this->assertSame(102, $array2->getSize());
$array3 = new BitArray(200);
$array3 = $matrix->getRow(2, $array3);
$this->assertSame(200, $array3->getSize());
for ($x = 0; $x < 102; ++$x) {
$on = (0 === ($x & 3));
$this->assertSame($on, $array1->get($x));
$this->assertSame($on, $array2->get($x));
$this->assertSame($on, $array3->get($x));
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\BitUtils;
use PHPUnit\Framework\TestCase;
class BitUtilsTest extends TestCase
{
public function testUnsignedRightShift() : void
{
$this->assertSame(1, BitUtils::unsignedRightShift(1, 0));
$this->assertSame(1, BitUtils::unsignedRightShift(10, 3));
$this->assertSame(536870910, BitUtils::unsignedRightShift(-10, 3));
}
public function testNumberOfTrailingZeros() : void
{
$this->assertSame(32, BitUtils::numberOfTrailingZeros(0));
$this->assertSame(1, BitUtils::numberOfTrailingZeros(10));
$this->assertSame(0, BitUtils::numberOfTrailingZeros(15));
$this->assertSame(2, BitUtils::numberOfTrailingZeros(20));
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Exception\OutOfBoundsException;
use PHPUnit\Framework\TestCase;
class ErrorCorrectionLevelTest extends TestCase
{
public function testBitsMatchConstants() : void
{
$this->assertSame(0x0, ErrorCorrectionLevel::M()->getBits());
$this->assertSame(0x1, ErrorCorrectionLevel::L()->getBits());
$this->assertSame(0x2, ErrorCorrectionLevel::H()->getBits());
$this->assertSame(0x3, ErrorCorrectionLevel::Q()->getBits());
}
public function testInvalidErrorCorrectionLevelThrowsException() : void
{
$this->expectException(OutOfBoundsException::class);
ErrorCorrectionLevel::forBits(4);
}
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\FormatInformation;
use PHPUnit\Framework\TestCase;
class FormatInformationTest extends TestCase
{
private const MASKED_TEST_FORMAT_INFO = 0x2bed;
private const UNMAKSED_TEST_FORMAT_INFO = self::MASKED_TEST_FORMAT_INFO ^ 0x5412;
public function testBitsDiffering() : void
{
$this->assertSame(0, FormatInformation::numBitsDiffering(1, 1));
$this->assertSame(1, FormatInformation::numBitsDiffering(0, 2));
$this->assertSame(2, FormatInformation::numBitsDiffering(1, 2));
$this->assertEquals(32, FormatInformation::numBitsDiffering(-1, 0));
}
public function testDecode() : void
{
$expected = FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO,
self::MASKED_TEST_FORMAT_INFO
);
$this->assertNotNull($expected);
$this->assertSame(7, $expected->getDataMask());
$this->assertSame(ErrorCorrectionLevel::Q(), $expected->getErrorCorrectionLevel());
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
self::UNMAKSED_TEST_FORMAT_INFO,
self::MASKED_TEST_FORMAT_INFO
)
);
}
public function testDecodeWithBitDifference() : void
{
$expected = FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO,
self::MASKED_TEST_FORMAT_INFO
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO ^ 0x1,
self::MASKED_TEST_FORMAT_INFO ^ 0x1
)
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO ^ 0x3,
self::MASKED_TEST_FORMAT_INFO ^ 0x3
)
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO ^ 0x7,
self::MASKED_TEST_FORMAT_INFO ^ 0x7
)
);
$this->assertNull(
FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO ^ 0xf,
self::MASKED_TEST_FORMAT_INFO ^ 0xf
)
);
}
public function testDecodeWithMisRead() : void
{
$expected = FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO,
self::MASKED_TEST_FORMAT_INFO
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
self::MASKED_TEST_FORMAT_INFO ^ 0x3,
self::MASKED_TEST_FORMAT_INFO ^ 0xf
)
);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\Mode;
use PHPUnit\Framework\TestCase;
class ModeTest extends TestCase
{
public function testBitsMatchConstants() : void
{
$this->assertSame(0x0, Mode::TERMINATOR()->getBits());
$this->assertSame(0x1, Mode::NUMERIC()->getBits());
$this->assertSame(0x2, Mode::ALPHANUMERIC()->getBits());
$this->assertSame(0x4, Mode::BYTE()->getBits());
$this->assertSame(0x8, Mode::KANJI()->getBits());
}
}

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\ReedSolomonCodec;
use PHPUnit\Framework\TestCase;
use SplFixedArray;
class ReedSolomonTest extends TestCase
{
public function tabs() : array
{
return [
[2, 0x7, 1, 1, 1],
[3, 0xb, 1, 1, 2],
[4, 0x13, 1, 1, 4],
[5, 0x25, 1, 1, 6],
[6, 0x43, 1, 1, 8],
[7, 0x89, 1, 1, 10],
[8, 0x11d, 1, 1, 32],
];
}
/**
* @dataProvider tabs
*/
public function testCodec(int $symbolSize, int $generatorPoly, int $firstRoot, int $primitive, int $numRoots) : void
{
mt_srand(0xdeadbeef, MT_RAND_PHP);
$blockSize = (1 << $symbolSize) - 1;
$dataSize = $blockSize - $numRoots;
$codec = new ReedSolomonCodec($symbolSize, $generatorPoly, $firstRoot, $primitive, $numRoots, 0);
for ($errors = 0; $errors <= $numRoots / 2; ++$errors) {
// Load block with random data and encode
$block = SplFixedArray::fromArray(array_fill(0, $blockSize, 0), false);
for ($i = 0; $i < $dataSize; ++$i) {
$block[$i] = mt_rand(0, $blockSize);
}
// Make temporary copy
$tBlock = clone $block;
$parity = SplFixedArray::fromArray(array_fill(0, $numRoots, 0), false);
$errorLocations = SplFixedArray::fromArray(array_fill(0, $blockSize, 0), false);
$erasures = [];
// Create parity
$codec->encode($block, $parity);
// Copy parity into test blocks
for ($i = 0; $i < $numRoots; ++$i) {
$block[$i + $dataSize] = $parity[$i];
$tBlock[$i + $dataSize] = $parity[$i];
}
// Seed with errors
for ($i = 0; $i < $errors; ++$i) {
$errorValue = mt_rand(1, $blockSize);
do {
$errorLocation = mt_rand(0, $blockSize);
} while (0 !== $errorLocations[$errorLocation]);
$errorLocations[$errorLocation] = 1;
if (mt_rand(0, 1)) {
$erasures[] = $errorLocation;
}
$tBlock[$errorLocation] ^= $errorValue;
}
$erasures = SplFixedArray::fromArray($erasures, false);
// Decode the errored block
$foundErrors = $codec->decode($tBlock, $erasures);
if ($errors > 0 && null === $foundErrors) {
$this->assertSame($block, $tBlock, 'Decoder failed to correct errors');
}
$this->assertSame($errors, $foundErrors, 'Found errors do not equal expected errors');
for ($i = 0; $i < $foundErrors; ++$i) {
if (0 === $errorLocations[$erasures[$i]]) {
$this->fail(sprintf('Decoder indicates error in location %d without error', $erasures[$i]));
}
}
$this->assertEquals($block, $tBlock, 'Decoder did not correct errors');
}
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Common;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use PHPUnit\Framework\TestCase;
class VersionTest extends TestCase
{
public function versions() : array
{
$array = [];
for ($i = 1; $i <= 40; ++$i) {
$array[] = [$i, 4 * $i + 17];
}
return $array;
}
public function decodeInformation() : array
{
return [
[7, 0x07c94],
[12, 0x0c762],
[17, 0x1145d],
[22, 0x168c9],
[27, 0x1b08e],
[32, 0x209d5],
];
}
/**
* @dataProvider versions
*/
public function testVersionForNumber(int $versionNumber, int $dimension) : void
{
$version = Version::getVersionForNumber($versionNumber);
$this->assertNotNull($version);
$this->assertEquals($versionNumber, $version->getVersionNumber());
$this->assertNotNull($version->getAlignmentPatternCenters());
if ($versionNumber > 1) {
$this->assertTrue(count($version->getAlignmentPatternCenters()) > 0);
}
$this->assertEquals($dimension, $version->getDimensionForVersion());
$this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::H()));
$this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::L()));
$this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::M()));
$this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::Q()));
$this->assertNotNull($version->buildFunctionPattern());
}
/**
* @dataProvider versions
*/
public function testGetProvisionalVersionForDimension(int $versionNumber, int $dimension) : void
{
$this->assertSame(
$versionNumber,
Version::getProvisionalVersionForDimension($dimension)->getVersionNumber()
);
}
/**
* @dataProvider decodeInformation
*/
public function testDecodeVersionInformation(int $expectedVersion, int $mask) : void
{
$version = Version::decodeVersionInformation($mask);
$this->assertNotNull($version);
$this->assertSame($expectedVersion, $version->getVersionNumber());
}
}

View File

@@ -0,0 +1,487 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\Version;
use BaconQrCode\Encoder\Encoder;
use BaconQrCode\Exception\WriterException;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionMethod;
use SplFixedArray;
final class EncoderTest extends TestCase
{
/**
* @var ReflectionMethod[]
*/
protected $methods = [];
public function setUp() : void
{
// Hack to be able to test protected methods
$reflection = new ReflectionClass(Encoder::class);
foreach ($reflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
$method->setAccessible(true);
$this->methods[$method->getName()] = $method;
}
}
public function testGetAlphanumericCode() : void
{
// The first ten code points are numbers.
for ($i = 0; $i < 10; ++$i) {
$this->assertSame($i, $this->methods['getAlphanumericCode']->invoke(null, ord('0') + $i));
}
// The next 26 code points are capital alphabet letters.
for ($i = 10; $i < 36; ++$i) {
// The first ten code points are numbers
$this->assertSame($i, $this->methods['getAlphanumericCode']->invoke(null, ord('A') + $i - 10));
}
// Others are symbol letters.
$this->assertSame(36, $this->methods['getAlphanumericCode']->invoke(null, ord(' ')));
$this->assertSame(37, $this->methods['getAlphanumericCode']->invoke(null, ord('$')));
$this->assertSame(38, $this->methods['getAlphanumericCode']->invoke(null, ord('%')));
$this->assertSame(39, $this->methods['getAlphanumericCode']->invoke(null, ord('*')));
$this->assertSame(40, $this->methods['getAlphanumericCode']->invoke(null, ord('+')));
$this->assertSame(41, $this->methods['getAlphanumericCode']->invoke(null, ord('-')));
$this->assertSame(42, $this->methods['getAlphanumericCode']->invoke(null, ord('.')));
$this->assertSame(43, $this->methods['getAlphanumericCode']->invoke(null, ord('/')));
$this->assertSame(44, $this->methods['getAlphanumericCode']->invoke(null, ord(':')));
// Should return -1 for other letters.
$this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord('a')));
$this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord('#')));
$this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord("\0")));
}
public function testChooseMode() : void
{
// Numeric mode
$this->assertSame(Mode::NUMERIC(), $this->methods['chooseMode']->invoke(null, '0'));
$this->assertSame(Mode::NUMERIC(), $this->methods['chooseMode']->invoke(null, '0123456789'));
// Alphanumeric mode
$this->assertSame(Mode::ALPHANUMERIC(), $this->methods['chooseMode']->invoke(null, 'A'));
$this->assertSame(
Mode::ALPHANUMERIC(),
$this->methods['chooseMode']->invoke(null, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:')
);
// 8-bit byte mode
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, 'a'));
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, '#'));
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, ''));
// AIUE in Hiragana in SHIFT-JIS
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\x8\xa\x8\xa\x8\xa\x8\xa6"));
// Nihon in Kanji in SHIFT-JIS
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\x9\xf\x9\x7b"));
// Sou-Utso-Byou in Kanji in SHIFT-JIS
$this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\xe\x4\x9\x5\x9\x61"));
}
public function testEncode() : void
{
$qrCode = Encoder::encode('ABCDEF', ErrorCorrectionLevel::H());
$expected = "<<\n"
. " mode: ALPHANUMERIC\n"
. " ecLevel: H\n"
. " version: 1\n"
. " maskPattern: 0\n"
. " matrix:\n"
. " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n"
. " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n"
. " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n"
. " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n"
. " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n"
. " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n"
. " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n"
. " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n"
. ">>\n";
$this->assertSame($expected, (string) $qrCode);
}
public function testSimpleUtf8Eci() : void
{
$qrCode = Encoder::encode('hello', ErrorCorrectionLevel::H(), 'utf-8');
$expected = "<<\n"
. " mode: BYTE\n"
. " ecLevel: H\n"
. " version: 1\n"
. " maskPattern: 3\n"
. " matrix:\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n"
. " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n"
. " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n"
. " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n"
. " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n"
. " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n"
. " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n"
. " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n"
. ">>\n";
$this->assertSame($expected, (string) $qrCode);
}
public function testAppendModeInfo() : void
{
$bits = new BitArray();
$this->methods['appendModeInfo']->invoke(null, Mode::NUMERIC(), $bits);
$this->assertSame(' ...X', (string) $bits);
}
public function testAppendLengthInfo() : void
{
// 1 letter (1/1), 10 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
1,
Version::getVersionForNumber(1),
Mode::NUMERIC(),
$bits
);
$this->assertSame(' ........ .X', (string) $bits);
// 2 letters (2/1), 11 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
2,
Version::getVersionForNumber(10),
Mode::ALPHANUMERIC(),
$bits
);
$this->assertSame(' ........ .X.', (string) $bits);
// 255 letters (255/1), 16 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
255,
Version::getVersionForNumber(27),
Mode::BYTE(),
$bits
);
$this->assertSame(' ........ XXXXXXXX', (string) $bits);
// 512 letters (1024/2), 12 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
512,
Version::getVersionForNumber(40),
Mode::KANJI(),
$bits
);
$this->assertSame(' ..X..... ....', (string) $bits);
}
public function testAppendBytes() : void
{
// Should use appendNumericBytes.
// 1 = 01 = 0001 in 4 bits.
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'1',
Mode::NUMERIC(),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertSame(' ...X', (string) $bits);
// Should use appendAlphaNumericBytes.
// A = 10 = 0xa = 001010 in 6 bits.
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'A',
Mode::ALPHANUMERIC(),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertSame(' ..X.X.', (string) $bits);
// Should use append8BitBytes.
// 0x61, 0x62, 0x63
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'abc',
Mode::BYTE(),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertSame(' .XX....X .XX...X. .XX...XX', (string) $bits);
// Should use appendKanjiBytes.
// 0x93, 0x5f
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
"\x93\x5f",
Mode::KANJI(),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertSame(' .XX.XX.. XXXXX', (string) $bits);
// Lower letters such as 'a' cannot be encoded in alphanumeric mode.
$this->expectException(WriterException::class);
$this->methods['appendBytes']->invoke(
null,
'a',
Mode::ALPHANUMERIC(),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
}
public function testTerminateBits() : void
{
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 0, $bits);
$this->assertSame('', (string) $bits);
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertSame(' ........', (string) $bits);
$bits = new BitArray();
$bits->appendBits(0, 3);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertSame(' ........', (string) $bits);
$bits = new BitArray();
$bits->appendBits(0, 5);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertSame(' ........', (string) $bits);
$bits = new BitArray();
$bits->appendBits(0, 8);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertSame(' ........', (string) $bits);
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 2, $bits);
$this->assertSame(' ........ XXX.XX..', (string) $bits);
$bits = new BitArray();
$bits->appendBits(0, 1);
$this->methods['terminateBits']->invoke(null, 3, $bits);
$this->assertSame(' ........ XXX.XX.. ...X...X', (string) $bits);
}
public function testGetNumDataBytesAndNumEcBytesForBlockId() : void
{
// Version 1-H.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 26, 9, 1, 0);
$this->assertSame(9, $numDataBytes);
$this->assertSame(17, $numEcBytes);
// Version 3-H. 2 blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 70, 26, 2, 0);
$this->assertSame(13, $numDataBytes);
$this->assertSame(22, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 70, 26, 2, 1);
$this->assertSame(13, $numDataBytes);
$this->assertSame(22, $numEcBytes);
// Version 7-H. (4 + 1) blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 196, 66, 5, 0);
$this->assertSame(13, $numDataBytes);
$this->assertSame(26, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 196, 66, 5, 4);
$this->assertSame(14, $numDataBytes);
$this->assertSame(26, $numEcBytes);
// Version 40-H. (20 + 61) blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 3706, 1276, 81, 0);
$this->assertSame(15, $numDataBytes);
$this->assertSame(30, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 3706, 1276, 81, 20);
$this->assertSame(16, $numDataBytes);
$this->assertSame(30, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']
->invoke(null, 3706, 1276, 81, 80);
$this->assertSame(16, $numDataBytes);
$this->assertSame(30, $numEcBytes);
}
public function testInterleaveWithEcBytes() : void
{
$dataBytes = SplFixedArray::fromArray([32, 65, 205, 69, 41, 220, 46, 128, 236], false);
$in = new BitArray();
foreach ($dataBytes as $dataByte) {
$in->appendBits($dataByte, 8);
}
$outBits = $this->methods['interleaveWithEcBytes']->invoke(null, $in, 26, 9, 1);
$expected = SplFixedArray::fromArray([
// Data bytes.
32, 65, 205, 69, 41, 220, 46, 128, 236,
// Error correction bytes.
42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61,
], false);
$out = $outBits->toBytes(0, count($expected));
$this->assertEquals($expected, $out);
}
public function testAppendNumericBytes() : void
{
// 1 = 01 = 0001 in 4 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '1', $bits);
$this->assertSame(' ...X', (string) $bits);
// 12 = 0xc = 0001100 in 7 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '12', $bits);
$this->assertSame(' ...XX..', (string) $bits);
// 123 = 0x7b = 0001111011 in 10 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '123', $bits);
$this->assertSame(' ...XXXX. XX', (string) $bits);
// 1234 = "123" + "4" = 0001111011 + 0100 in 14 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '1234', $bits);
$this->assertSame(' ...XXXX. XX.X..', (string) $bits);
// Empty
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '', $bits);
$this->assertSame('', (string) $bits);
}
public function testAppendAlphanumericBytes() : void
{
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'A', $bits);
$this->assertSame(' ..X.X.', (string) $bits);
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'AB', $bits);
$this->assertSame(' ..XXX..X X.X', (string) $bits);
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'ABC', $bits);
$this->assertSame(' ..XXX..X X.X..XX. .', (string) $bits);
// Empty
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, '', $bits);
$this->assertSame('', (string) $bits);
// Invalid data
$this->expectException(WriterException::class);
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'abc', $bits);
}
public function testAppend8BitBytes() : void
{
// 0x61, 0x62, 0x63
$bits = new BitArray();
$this->methods['append8BitBytes']->invoke(null, 'abc', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING);
$this->assertSame(' .XX....X .XX...X. .XX...XX', (string) $bits);
// Empty
$bits = new BitArray();
$this->methods['append8BitBytes']->invoke(null, '', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING);
$this->assertSame('', (string) $bits);
}
public function testAppendKanjiBytes() : void
{
// Numbers are from page 21 of JISX0510:2004
$bits = new BitArray();
$this->methods['appendKanjiBytes']->invoke(null, "\x93\x5f", $bits);
$this->assertSame(' .XX.XX.. XXXXX', (string) $bits);
$this->methods['appendKanjiBytes']->invoke(null, "\xe4\xaa", $bits);
$this->assertSame(' .XX.XX.. XXXXXXX. X.X.X.X. X.', (string) $bits);
}
public function testGenerateEcBytes() : void
{
// Numbers are from http://www.swetake.com/qr/qr3.html and
// http://www.swetake.com/qr/qr9.html
$dataBytes = SplFixedArray::fromArray([32, 65, 205, 69, 41, 220, 46, 128, 236], false);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17);
$expected = SplFixedArray::fromArray(
[42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61],
false
);
$this->assertEquals($expected, $ecBytes);
$dataBytes = SplFixedArray::fromArray(
[67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214],
false
);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 18);
$expected = SplFixedArray::fromArray(
[175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187],
false
);
$this->assertEquals($expected, $ecBytes);
// High-order zero coefficient case.
$dataBytes = SplFixedArray::fromArray([32, 49, 205, 69, 42, 20, 0, 236, 17], false);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17);
$expected = SplFixedArray::fromArray(
[0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213],
false
);
$this->assertEquals($expected, $ecBytes);
}
}

View File

@@ -0,0 +1,251 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Encoder;
use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Encoder\MaskUtil;
use PHPUnit\Framework\TestCase;
class MaskUtilTest extends TestCase
{
public function dataMaskBits() : array
{
return [
[0, [
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1],
]],
[1, [
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
]],
[2, [
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 1, 0, 0],
]],
[3, [
[1, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 1, 0],
]],
[4, [
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 1],
[0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
]],
[5, [
[1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0],
]],
[6, [
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0],
[1, 1, 0, 1, 1, 0],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 1],
]],
[7, [
[1, 0, 1, 0, 1, 0],
[0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 0, 1],
[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
]],
];
}
/**
* @dataProvider dataMaskBits
*/
public function testGetDatMaskBit(int $maskPattern, array $expected) : void
{
for ($x = 0; $x < 6; ++$x) {
for ($y = 0; $y < 6; ++$y) {
$this->assertSame(
1 === $expected[$y][$x],
MaskUtil::getDataMaskBit($maskPattern, $x, $y)
);
}
}
}
public function testApplyMaskPenaltyRule1() : void
{
$matrix = new ByteMatrix(4, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$this->assertSame(0, MaskUtil::applyMaskPenaltyRule1($matrix));
// Horizontal
$matrix = new ByteMatrix(6, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$matrix->set(4, 0, 0);
$matrix->set(5, 0, 1);
$this->assertSame(3, MaskUtil::applyMaskPenaltyRule1($matrix));
$matrix->set(5, 0, 0);
$this->assertSame(4, MaskUtil::applyMaskPenaltyRule1($matrix));
// Vertical
$matrix = new ByteMatrix(1, 6);
$matrix->set(0, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(0, 3, 0);
$matrix->set(0, 4, 0);
$matrix->set(0, 5, 1);
$this->assertSame(3, MaskUtil::applyMaskPenaltyRule1($matrix));
$matrix->set(0, 5, 0);
$this->assertSame(4, MaskUtil::applyMaskPenaltyRule1($matrix));
}
public function testApplyMaskPenaltyRule2() : void
{
$matrix = new ByteMatrix(1, 1);
$matrix->set(0, 0, 0);
$this->assertSame(0, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(2, 2);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 1);
$this->assertSame(0, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(2, 2);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 0);
$this->assertSame(3, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(3, 3);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 0);
$matrix->set(2, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(1, 2, 0);
$matrix->set(2, 2, 0);
$this->assertSame(3 * 4, MaskUtil::applyMaskPenaltyRule2($matrix));
}
public function testApplyMaskPenalty3() : void
{
// Horizontal 00001011101
$matrix = new ByteMatrix(11, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$matrix->set(6, 0, 1);
$matrix->set(7, 0, 1);
$matrix->set(8, 0, 1);
$matrix->set(9, 0, 0);
$matrix->set(10, 0, 1);
$this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Horizontal 10111010000
$matrix = new ByteMatrix(11, 1);
$matrix->set(0, 0, 1);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 1);
$matrix->set(3, 0, 1);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$matrix->set(6, 0, 1);
$matrix->set(7, 0, 0);
$matrix->set(8, 0, 0);
$matrix->set(9, 0, 0);
$matrix->set(10, 0, 0);
$this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Vertical 00001011101
$matrix = new ByteMatrix(1, 11);
$matrix->set(0, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(0, 3, 0);
$matrix->set(0, 4, 1);
$matrix->set(0, 5, 0);
$matrix->set(0, 6, 1);
$matrix->set(0, 7, 1);
$matrix->set(0, 8, 1);
$matrix->set(0, 9, 0);
$matrix->set(0, 10, 1);
$this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Vertical 10111010000
$matrix = new ByteMatrix(1, 11);
$matrix->set(0, 0, 1);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 1);
$matrix->set(0, 3, 1);
$matrix->set(0, 4, 1);
$matrix->set(0, 5, 0);
$matrix->set(0, 6, 1);
$matrix->set(0, 7, 0);
$matrix->set(0, 8, 0);
$matrix->set(0, 9, 0);
$matrix->set(0, 10, 0);
$this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix));
}
public function testApplyMaskPenaltyRule4() : void
{
// Dark cell ratio = 0%
$matrix = new ByteMatrix(1, 1);
$matrix->set(0, 0, 0);
$this->assertSame(100, MaskUtil::applyMaskPenaltyRule4($matrix));
// Dark cell ratio = 5%
$matrix = new ByteMatrix(2, 1);
$matrix->set(0, 0, 0);
$matrix->set(0, 0, 1);
$this->assertSame(0, MaskUtil::applyMaskPenaltyRule4($matrix));
// Dark cell ratio = 66.67%
$matrix = new ByteMatrix(6, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 1);
$matrix->set(2, 0, 1);
$matrix->set(3, 0, 1);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$this->assertSame(30, MaskUtil::applyMaskPenaltyRule4($matrix));
}
}

View File

@@ -0,0 +1,335 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Encoder\MatrixUtil;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionMethod;
class MatrixUtilTest extends TestCase
{
/**
* @var ReflectionMethod[]
*/
protected $methods = [];
public function setUp() : void
{
// Hack to be able to test protected methods
$reflection = new ReflectionClass(MatrixUtil::class);
foreach ($reflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
$method->setAccessible(true);
$this->methods[$method->getName()] = $method;
}
}
public function testToString() : void
{
$matrix = new ByteMatrix(3, 3);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 1);
$matrix->set(2, 0, 0);
$matrix->set(0, 1, 1);
$matrix->set(1, 1, 0);
$matrix->set(2, 1, 1);
$matrix->set(0, 2, -1);
$matrix->set(1, 2, -1);
$matrix->set(2, 2, -1);
$expected = " 0 1 0\n 1 0 1\n \n";
$this->assertSame($expected, (string) $matrix);
}
public function testClearMatrix() : void
{
$matrix = new ByteMatrix(2, 2);
MatrixUtil::clearMatrix($matrix);
$this->assertSame(-1, $matrix->get(0, 0));
$this->assertSame(-1, $matrix->get(1, 0));
$this->assertSame(-1, $matrix->get(0, 1));
$this->assertSame(-1, $matrix->get(1, 1));
}
public function testEmbedBasicPatterns1() : void
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(1),
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 0 0 0 0 0 0 0 1 \n"
. " 1 1 1 1 1 1 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 1 1 1 1 1 1 0 \n";
$this->assertSame($expected, (string) $matrix);
}
public function testEmbedBasicPatterns2() : void
{
$matrix = new ByteMatrix(25, 25);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(2),
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 1 1 1 1 1 \n"
. " 0 0 0 0 0 0 0 0 1 1 0 0 0 1 \n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 \n"
. " 1 0 0 0 0 0 1 0 1 0 0 0 1 \n"
. " 1 0 1 1 1 0 1 0 1 1 1 1 1 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 1 1 1 1 1 1 0 \n";
$this->assertSame($expected, (string) $matrix);
}
public function testEmbedTypeInfo() : void
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedTypeInfo']->invoke(
null,
ErrorCorrectionLevel::M(),
5,
$matrix
);
$expected = " 0 \n"
. " 1 \n"
. " 1 \n"
. " 1 \n"
. " 0 \n"
. " 0 \n"
. " \n"
. " 1 \n"
. " 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0\n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 1 \n";
$this->assertSame($expected, (string) $matrix);
}
public function testEmbedVersionInfo() : void
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['maybeEmbedVersionInfo']->invoke(
null,
Version::getVersionForNumber(7),
$matrix
);
$expected = " 0 0 1 \n"
. " 0 1 0 \n"
. " 0 1 0 \n"
. " 0 1 1 \n"
. " 1 1 1 \n"
. " 0 0 0 \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " 0 0 0 0 1 0 \n"
. " 0 1 1 1 1 0 \n"
. " 1 0 0 1 1 0 \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n";
$this->assertSame($expected, (string) $matrix);
}
public function testEmbedDataBits() : void
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(1),
$matrix
);
$bits = new BitArray();
$this->methods['embedDataBits']->invoke(
null,
$bits,
-1,
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
$this->assertSame($expected, (string) $matrix);
}
public function testBuildMatrix() : void
{
$bytes = [
32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169,
239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61
];
$bits = new BitArray();
foreach ($bytes as $byte) {
$bits->appendBits($byte, 8);
}
$matrix = new ByteMatrix(21, 21);
MatrixUtil::buildMatrix(
$bits,
ErrorCorrectionLevel::H(),
Version::getVersionForNumber(1),
3,
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0\n"
. " 1 0 1 0 1 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0\n"
. " 1 1 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0\n"
. " 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0\n"
. " 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 1 1\n"
. " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1\n"
. " 1 0 1 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 1 0\n"
. " 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 0 1 0 0 1 0\n";
$this->assertSame($expected, (string) $matrix);
}
public function testFindMsbSet() : void
{
$this->assertSame(0, $this->methods['findMsbSet']->invoke(null, 0));
$this->assertSame(1, $this->methods['findMsbSet']->invoke(null, 1));
$this->assertSame(8, $this->methods['findMsbSet']->invoke(null, 0x80));
$this->assertSame(32, $this->methods['findMsbSet']->invoke(null, 0x80000000));
}
public function testCalculateBchCode() : void
{
// Encoding of type information.
// From Appendix C in JISX0510:2004 (p 65)
$this->assertSame(0xdc, $this->methods['calculateBchCode']->invoke(null, 5, 0x537));
// From http://www.swetake.com/qr/qr6.html
$this->assertSame(0x1c2, $this->methods['calculateBchCode']->invoke(null, 0x13, 0x537));
// From http://www.swetake.com/qr/qr11.html
$this->assertSame(0x214, $this->methods['calculateBchCode']->invoke(null, 0x1b, 0x537));
// Encoding of version information.
// From Appendix D in JISX0510:2004 (p 68)
$this->assertSame(0xc94, $this->methods['calculateBchCode']->invoke(null, 7, 0x1f25));
$this->assertSame(0x5bc, $this->methods['calculateBchCode']->invoke(null, 8, 0x1f25));
$this->assertSame(0xa99, $this->methods['calculateBchCode']->invoke(null, 9, 0x1f25));
$this->assertSame(0x4d3, $this->methods['calculateBchCode']->invoke(null, 10, 0x1f25));
$this->assertSame(0x9a6, $this->methods['calculateBchCode']->invoke(null, 20, 0x1f25));
$this->assertSame(0xd75, $this->methods['calculateBchCode']->invoke(null, 30, 0x1f25));
$this->assertSame(0xc69, $this->methods['calculateBchCode']->invoke(null, 40, 0x1f25));
}
public function testMakeVersionInfoBits() : void
{
// From Appendix D in JISX0510:2004 (p 68)
$bits = new BitArray();
$this->methods['makeVersionInfoBits']->invoke(null, Version::getVersionForNumber(7), $bits);
$this->assertSame(' ...XXXXX ..X..X.X ..', (string) $bits);
}
public function testMakeTypeInfoBits() : void
{
// From Appendix D in JISX0510:2004 (p 68)
$bits = new BitArray();
$this->methods['makeTypeInfoBits']->invoke(null, ErrorCorrectionLevel::M(), 5, $bits);
$this->assertSame(' X......X X..XXX.', (string) $bits);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types = 1);
namespace BaconQrCodeTest\Integration;
use BaconQrCode\Renderer\Color\Rgb;
use BaconQrCode\Renderer\Eye\SquareEye;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Module\SquareModule;
use BaconQrCode\Renderer\RendererStyle\EyeFill;
use BaconQrCode\Renderer\RendererStyle\Fill;
use BaconQrCode\Renderer\RendererStyle\Gradient;
use BaconQrCode\Renderer\RendererStyle\GradientType;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use PHPUnit\Framework\TestCase;
use Spatie\Snapshots\MatchesSnapshots;
/**
* @group integration
*/
final class ImagickRenderingTest extends TestCase
{
use MatchesSnapshots;
/**
* @requires extension imagick
*/
public function testGenericQrCode() : void
{
$renderer = new ImageRenderer(
new RendererStyle(400),
new ImagickImageBackEnd()
);
$writer = new Writer($renderer);
$tempName = tempnam(sys_get_temp_dir(), 'test') . '.png';
$writer->writeFile('Hello World!', $tempName);
$this->assertMatchesFileSnapshot($tempName);
unlink($tempName);
}
/**
* @requires extension imagick
*/
public function testIssue79() : void
{
$eye = SquareEye::instance();
$squareModule = SquareModule::instance();
$eyeFill = new EyeFill(new Rgb(100, 100, 55), new Rgb(100, 100, 255));
$gradient = new Gradient(new Rgb(100, 100, 55), new Rgb(100, 100, 255), GradientType::HORIZONTAL());
$renderer = new ImageRenderer(
new RendererStyle(
400,
2,
$squareModule,
$eye,
Fill::withForegroundGradient(new Rgb(255, 255, 255), $gradient, $eyeFill, $eyeFill, $eyeFill)
),
new ImagickImageBackEnd()
);
$writer = new Writer($renderer);
$tempName = tempnam(sys_get_temp_dir(), 'test') . '.png';
$writer->writeFile('https://apiroad.net/very-long-url', $tempName);
$this->assertMatchesFileSnapshot($tempName);
unlink($tempName);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB