Просмотр исходного кода

:fire: v6.0 third pass: test clean-up (again)

smiley 2 лет назад
Родитель
Сommit
2d2a00fabb
37 измененных файлов с 398 добавлено и 249 удалено
  1. 2 0
      phpunit.xml.dist
  2. 5 3
      tests/Common/BitBufferTest.php
  3. 1 1
      tests/Common/ECICharsetTest.php
  4. 1 2
      tests/Common/EccLevelTest.php
  5. 2 2
      tests/Common/MaskPatternTest.php
  6. 1 1
      tests/Common/ModeTest.php
  7. 1 2
      tests/Common/VersionTest.php
  8. 6 2
      tests/Data/AlphaNumTest.php
  9. 6 2
      tests/Data/ByteTest.php
  10. 28 47
      tests/Data/DataInterfaceTestAbstract.php
  11. 9 12
      tests/Data/ECITest.php
  12. 14 6
      tests/Data/HanziTest.php
  13. 21 5
      tests/Data/KanjiTest.php
  14. 6 2
      tests/Data/NumberTest.php
  15. 3 1
      tests/Data/QRDataTest.php
  16. 3 68
      tests/Data/QRMatrixTest.php
  17. 11 4
      tests/Output/QREpsTest.php
  18. 13 7
      tests/Output/QRFpdfTest.php
  19. 10 2
      tests/Output/QRGdImageBMPTest.php
  20. 10 2
      tests/Output/QRGdImageGIFTest.php
  21. 10 2
      tests/Output/QRGdImageJPGTest.php
  22. 10 2
      tests/Output/QRGdImagePNGTest.php
  23. 4 3
      tests/Output/QRGdImageTestAbstract.php
  24. 10 2
      tests/Output/QRGdImageWEBPTest.php
  25. 13 5
      tests/Output/QRImagickTest.php
  26. 10 2
      tests/Output/QRMarkupHTMLTest.php
  27. 10 2
      tests/Output/QRMarkupSVGTest.php
  28. 1 1
      tests/Output/QRMarkupTestAbstract.php
  29. 17 23
      tests/Output/QROutputTestAbstract.php
  30. 11 3
      tests/Output/QRStringJSONTest.php
  31. 10 3
      tests/Output/QRStringTEXTTest.php
  32. 9 2
      tests/QRCodeReaderGDTest.php
  33. 14 10
      tests/QRCodeReaderImagickTest.php
  34. 17 12
      tests/QRCodeReaderTestAbstract.php
  35. 3 2
      tests/QRCodeTest.php
  36. 94 0
      tests/QRMatrixDebugTrait.php
  37. 2 4
      tests/QROptionsTest.php

+ 2 - 0
phpunit.xml.dist

@@ -37,5 +37,7 @@
 		<const name="TEST_IS_CI" value="true"/>
 		<!-- limit the maximum version for the reader test to speed up things -->
 		<const name="READER_TEST_MAX_VERSION" value="40"/>
+		<!-- the QR version to display the matrix in when called from the shortcut method in QRMatrixDebugTrait -->
+		<const name="MATRIX_DEBUG_VERSION" value="7"/>
 	</php>
 </phpunit>

+ 5 - 3
tests/Common/BitBufferTest.php

@@ -10,8 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\{BitBuffer, Mode};
 use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
 
@@ -32,13 +32,15 @@ final class BitBufferTest extends TestCase{
 			'alphanum' => [Mode::ALPHANUM, 32],
 			'byte'     => [Mode::BYTE, 64],
 			'kanji'    => [Mode::KANJI, 128],
+			'hanzi'    => [Mode::HANZI, 208],
 		];
 	}
 
 	#[DataProvider('bitProvider')]
-	public function testPut(int $data, int $value):void{
+	public function testPut(int $data, int $expected):void{
 		$this->bitBuffer->put($data, 4);
-		$this::assertSame($value, $this->bitBuffer->getBuffer()[0]);
+
+		$this::assertSame($expected, $this->bitBuffer->getBuffer()[0]);
 		$this::assertSame(4, $this->bitBuffer->getLength());
 	}
 

+ 1 - 1
tests/Common/ECICharsetTest.php

@@ -10,8 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\ECICharset;
 use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Common\ECICharset;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
 

+ 1 - 2
tests/Common/EccLevelTest.php

@@ -10,9 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\EccLevel;
-use chillerlan\QRCode\Common\MaskPattern;
 use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Common\{EccLevel, MaskPattern};
 use PHPUnit\Framework\TestCase;
 
 /**

+ 2 - 2
tests/Common/MaskPatternTest.php

@@ -13,11 +13,11 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\MaskPattern;
 use chillerlan\QRCode\QRCodeException;
-use Closure;
+use chillerlan\QRCode\Common\MaskPattern;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
+use Closure;
 
 /**
  * @see https://github.com/zxing/zxing/blob/f4f3c2971dc794346d8b6e14752200008cb90716/core/src/test/java/com/google/zxing/qrcode/encoder/MaskUtilTestCase.java

+ 1 - 1
tests/Common/ModeTest.php

@@ -10,8 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\Mode;
 use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Common\Mode;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
 

+ 1 - 2
tests/Common/VersionTest.php

@@ -10,9 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Common;
 
-use chillerlan\QRCode\Common\EccLevel;
-use chillerlan\QRCode\Common\Version;
 use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Common\{EccLevel, Version};
 use PHPUnit\Framework\TestCase;
 
 /**

+ 6 - 2
tests/Data/AlphaNumTest.php

@@ -11,14 +11,18 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\AlphaNum;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 
 /**
  * Tests the AlphaNum class
  */
 final class AlphaNumTest extends DataInterfaceTestAbstract{
 
-	protected static string $FQN      = AlphaNum::class;
-	protected static string $testdata = '0 $%*+-./:';
+	protected const testData = '0 $%*+-./:';
+
+	protected static function getDataModeInterface(string $data):QRDataModeInterface{
+		return new AlphaNum($data);
+	}
 
 	/**
 	 * isAlphaNum() should pass on the 45 defined characters and fail on anything else (e.g. lowercase)

+ 6 - 2
tests/Data/ByteTest.php

@@ -11,14 +11,18 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\Byte;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 
 /**
  * Tests the Byte class
  */
 final class ByteTest extends DataInterfaceTestAbstract{
 
-	protected static string $FQN      = Byte::class;
-	protected static string $testdata = '[¯\_(ツ)_/¯]';
+	protected const testData = '[¯\_(ツ)_/¯]';
+
+	protected static function getDataModeInterface(string $data):QRDataModeInterface{
+		return new Byte($data);
+	}
 
 	/**
 	 * isByte() passses any binary string and only fails on empty strings

+ 28 - 47
tests/Data/DataInterfaceTestAbstract.php

@@ -11,21 +11,13 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Mode, Version};
-use PHPUnit\Framework\Attributes\DataProvider;
 use chillerlan\QRCode\Data\{QRCodeDataException, QRData, QRDataModeInterface, QRMatrix};
 use chillerlan\QRCode\QROptions;
 use chillerlan\QRCodeTest\QRMaxLengthTrait;
-use Exception,  Generator;
+use Exception, Generator;
+use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;
-
-use function array_map;
-use function hex2bin;
-use function mb_strlen;
-use function mb_substr;
-use function sprintf;
-use function str_repeat;
-use function strlen;
-use function substr;
+use function array_map, hex2bin, mb_strlen, mb_substr, sprintf, str_repeat, strlen, substr;
 
 /**
  * The data interface test abstract
@@ -35,26 +27,15 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 
 	protected QRData              $QRData;
 	protected QRDataModeInterface $dataMode;
-	protected static string       $FQN;
-	protected static string       $testdata;
+
+	protected const testData = '';
 
 	protected function setUp():void{
-		$this->QRData = new QRData(new QROptions);
+		$this->QRData   = new QRData(new QROptions);
+		$this->dataMode = static::getDataModeInterface(static::testData);
 	}
 
-	/**
-	 * Verifies the QRData instance
-	 */
-	public function testInstance():void{
-		$this::assertInstanceOf(QRData::class, $this->QRData);
-	}
-
-	/**
-	 * Verifies the QRDataModeInterface instance
-	 */
-	public function testDataModeInstance():void{
-		$this::assertInstanceOf(QRDataModeInterface::class, new static::$FQN(static::$testdata));
-	}
+	abstract protected static function getDataModeInterface(string $data):QRDataModeInterface;
 
 	/**
 	 * @return int[][]
@@ -70,7 +51,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	public function testInitMatrix(int $pattern):void{
 		$maskPattern = new MaskPattern($pattern);
 
-		$this->QRData->setData([new static::$FQN(static::$testdata)]);
+		$this->QRData->setData([$this->dataMode]);
 
 		$matrix = $this->QRData->writeMatrix()->setFormatInfo($maskPattern)->mask($maskPattern);
 
@@ -85,18 +66,16 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	 */
 	#[DataProvider('stringValidateProvider')]
 	public function testValidateString(string $string, bool $expected):void{
-		/** @noinspection PhpUndefinedMethodInspection */
-		$this::assertSame($expected, static::$FQN::validateString($string));
+		$this::assertSame($expected, $this->dataMode::validateString($string));
 	}
 
 	/**
-	 * Tests if a binary string is properly validated as false
+	 * Tests if a random binary string is properly validated as false
 	 *
 	 * @see https://github.com/chillerlan/php-qrcode/issues/182
 	 */
 	public function testBinaryStringInvalid():void{
-		/** @noinspection PhpUndefinedMethodInspection */
-		$this::assertFalse(static::$FQN::validateString(hex2bin('01015989f47dff8e852122117e04c90b9f15defc1c36477b1fe1')));
+		$this::assertFalse($this->dataMode::validateString(hex2bin('01015989f47dff8e852122117e04c90b9f15defc1c36477b1fe1')));
 	}
 
 	/**
@@ -114,8 +93,6 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$options          = new QROptions;
 		$options->version = $version;
 
-		// invoke a datamode interface
-		$this->dataMode = new static::$FQN(static::$testdata);
 		// invoke a QRData instance and write data
 		$this->QRData = new QRData($options, [$this->dataMode]);
 		// get the filled bitbuffer
@@ -123,7 +100,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		// read the first 4 bits
 		$this::assertSame($this->dataMode::DATAMODE, $bitBuffer->read(4));
 		// decode the data
-		$this::assertSame(static::$testdata, $this->dataMode::decodeSegment($bitBuffer, $options->version));
+		$this::assertSame(static::testData, $this->dataMode::decodeSegment($bitBuffer, $options->version));
 	}
 
 	/**
@@ -139,15 +116,17 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	 */
 	public static function maxLengthProvider():Generator{
 		$eccLevels = array_map(fn(int $ecc):EccLevel => new EccLevel($ecc), [EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H]);
-		$str       = str_repeat(static::$testdata, 1000);
-		$mb        = (static::$FQN::DATAMODE === Mode::KANJI || static::$FQN::DATAMODE === Mode::HANZI);
+		$str       = str_repeat(static::testData, 1000);
+		/** @phan-suppress-next-line PhanAbstractStaticMethodCallInStatic */
+		$dataMode  = static::getDataModeInterface(static::testData)::DATAMODE;
+		$mb        = ($dataMode === Mode::KANJI || $dataMode === Mode::HANZI);
 
 		for($v = 1; $v <= 40; $v++){
 			$version = new Version($v);
 
 			foreach($eccLevels as $eccLevel){
 				// maximum characters per ecc/mode
-				$len  = static::getMaxLengthForMode(static::$FQN::DATAMODE, $version, $eccLevel);
+				$len  = static::getMaxLengthForMode($dataMode, $version, $eccLevel);
 				// a string that contains the maximum amount of characters for the given mode
 				$val  = ($mb) ? mb_substr($str, 0, $len) : substr($str, 0, $len);
 				// same as above but character count exceeds
@@ -171,8 +150,10 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$options           = new QROptions;
 		$options->version  = $version->getVersionNumber();
 		$options->eccLevel = $eccLevel->getLevel();
-		$this->dataMode    = new static::$FQN($str);
+
+		$this->dataMode    = static::getDataModeInterface($str);
 		$this->QRData      = new QRData($options, [$this->dataMode]);
+
 		$bitBuffer         = $this->QRData->getBitBuffer();
 
 		$this::assertSame($this->dataMode::DATAMODE, $bitBuffer->read(4));
@@ -187,8 +168,10 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$options           = new QROptions;
 		$options->version  = Version::AUTO;
 		$options->eccLevel = $eccLevel->getLevel();
-		$this->dataMode    = new static::$FQN($str);
+
+		$this->dataMode    = static::getDataModeInterface($str);
 		$this->QRData      = new QRData($options, [$this->dataMode]);
+
 		$bitBuffer         = $this->QRData->getBitBuffer();
 
 		$this::assertLessThanOrEqual($eccLevel->getMaxBitsForVersion($version), $this->QRData->estimateTotalBitLength());
@@ -214,7 +197,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$options->eccLevel = $eccLevel->getLevel();
 
 		/** @phan-suppress-next-line PhanNoopNew */
-		new QRData($options, [new static::$FQN($str1)]);
+		new QRData($options, [static::getDataModeInterface($str1)]);
 	}
 
 	/**
@@ -224,7 +207,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('data exceeds');
 
-		$this->QRData->setData([new static::$FQN(str_repeat(static::$testdata, 1337))]);
+		$this->QRData->setData([static::getDataModeInterface(str_repeat(static::testData, 1337))]);
 	}
 
 	/**
@@ -234,8 +217,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('invalid data');
 
-		/** @phan-suppress-next-line PhanNoopNew */
-		new static::$FQN('##');
+		static::getDataModeInterface('##');
 	}
 
 	/**
@@ -245,8 +227,7 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('invalid data');
 
-		/** @phan-suppress-next-line PhanNoopNew */
-		new static::$FQN('');
+		static::getDataModeInterface('');
 	}
 
 }

+ 9 - 12
tests/Data/ECITest.php

@@ -21,9 +21,10 @@ use PHPUnit\Framework\TestCase;
  */
 final class ECITest extends TestCase{
 
-	protected QRData $QRData;
-	protected string $testdata    = '无可奈何燃花作香';
-	private int      $testCharset = ECICharset::GB18030;
+	private QRData $QRData;
+	private int    $testCharset = ECICharset::GB18030;
+
+	private const testData = '无可奈何燃花作香';
 
 	protected function setUp():void{
 		$this->QRData = new QRData(new QROptions);
@@ -32,11 +33,11 @@ final class ECITest extends TestCase{
 	private function getDataSegments():array{
 		return [
 			new ECI($this->testCharset),
-			new Byte(mb_convert_encoding($this->testdata, ECICharset::MB_ENCODINGS[$this->testCharset], mb_internal_encoding())),
+			/** @phan-suppress-next-line PhanParamSuspiciousOrder */
+			new Byte(mb_convert_encoding(self::testData, ECICharset::MB_ENCODINGS[$this->testCharset], mb_internal_encoding())),
 		];
 	}
 
-	/** @inheritDoc */
 	public function testDataModeInstance():void{
 		$datamode = new ECI($this->testCharset);
 
@@ -50,9 +51,6 @@ final class ECITest extends TestCase{
 		return ['1-9' => [7], '10-26' => [15], '27-40' => [30]];
 	}
 
-	/**
-	 * @inheritDoc
-	 */
 	#[DataProvider('versionBreakpointProvider')]
 	public function testDecodeSegment(int $version):void{
 		$options = new QROptions;
@@ -68,10 +66,9 @@ final class ECITest extends TestCase{
 		// read the first 4 bits
 		$this::assertSame($segments[0]::DATAMODE, $bitBuffer->read(4));
 		// decode the data
-		$this::assertSame($this->testdata, ECI::decodeSegment($bitBuffer, $options->version));
+		$this::assertSame(self::testData, ECI::decodeSegment($bitBuffer, $options->version));
 	}
 
-	/** @inheritDoc */
 	public function testInvalidDataException():void{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('invalid encoding id:');
@@ -82,8 +79,6 @@ final class ECITest extends TestCase{
 	/**
 	 * since the ECI class only accepts integer values,
 	 * we'll use this test to check for the upper end of the accepted input range
-	 *
-	 * @inheritDoc
 	 */
 	public function testInvalidDataOnEmptyException():void{
 		$this->expectException(QRCodeDataException::class);
@@ -128,7 +123,9 @@ final class ECITest extends TestCase{
 		// follow the ECI segment by a non-8bit-byte segment
 		$segments[1] = new Number('1');
 		$bitBuffer   = (new QRData($options, $segments))->getBitBuffer();
+		// verify the ECI mode indicator
 		$this::assertSame(Mode::ECI, $bitBuffer->read(4));
+		// throw
 		ECI::decodeSegment($bitBuffer, $options->version);
 	}
 

+ 14 - 6
tests/Data/HanziTest.php

@@ -11,9 +11,10 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\Hanzi;
-use Generator, Throwable;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\Attributes\Group;
+use Generator, Throwable;
 use function bin2hex, chr, defined, sprintf;
 
 /**
@@ -21,8 +22,11 @@ use function bin2hex, chr, defined, sprintf;
  */
 final class HanziTest extends DataInterfaceTestAbstract{
 
-	protected static string $FQN      = Hanzi::class;
-	protected static string $testdata = '无可奈何燃花作香';
+	protected const testData = '无可奈何燃花作香';
+
+	protected static function getDataModeInterface(string $data):QRDataModeInterface{
+		return new Hanzi($data);
+	}
 
 	/**
 	 * isGB2312() should pass on Hanzi/GB2312 characters and fail on everything else
@@ -50,9 +54,13 @@ final class HanziTest extends DataInterfaceTestAbstract{
 			}
 
 			for($byte2 = 0xa1; $byte2 < 0xff; $byte2++){
-				yield sprintf('0x%X', ($byte1 << 8 | $byte2)) => [
-					mb_convert_encoding(chr($byte1).chr($byte2), 'UTF-8', Hanzi::ENCODING),
-				];
+				$chr = mb_convert_encoding(chr($byte1).chr($byte2), 'UTF-8', Hanzi::ENCODING);
+
+				if($chr === '?'){ // skip unknown glyphs
+					continue;
+				}
+
+				yield sprintf('0x%X', (($byte1 << 8) | $byte2)) => [$chr];
 			}
 
 		}

+ 21 - 5
tests/Data/KanjiTest.php

@@ -11,6 +11,7 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\Kanji;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 use Generator, Throwable;
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\Attributes\Group;
@@ -21,8 +22,11 @@ use function bin2hex, chr, defined, sprintf;
  */
 final class KanjiTest extends DataInterfaceTestAbstract{
 
-	protected static string $FQN      = Kanji::class;
-	protected static string $testdata = '漂う花の香り';
+	protected const testData = '漂う花の香り';
+
+	protected static function getDataModeInterface(string $data):QRDataModeInterface{
+		return new Kanji($data);
+	}
 
 	/**
 	 * isKanji() should pass on Kanji/SJIS characters and fail on everything else
@@ -42,7 +46,7 @@ final class KanjiTest extends DataInterfaceTestAbstract{
 	 * lists the valid SJIS kanji
 	 */
 	public static function kanjiProvider():Generator{
-		$key = fn(int $byte1, int $byte2):string => sprintf('0x%X', ($byte1 << 8 | $byte2));
+		$key = fn(int $byte1, int $byte2):string => sprintf('0x%X', (($byte1 << 8) | $byte2));
 		$val = fn(int $byte1, int $byte2):string => mb_convert_encoding(chr($byte1).chr($byte2), 'UTF-8', Kanji::ENCODING);
 
 		for($byte1 = 0x81; $byte1 < 0xeb; $byte1++){
@@ -61,7 +65,13 @@ final class KanjiTest extends DataInterfaceTestAbstract{
 						continue;
 					}
 
-					yield $key($byte1, $byte2) => [$val($byte1, $byte2)];
+					$chr = $val($byte1, $byte2);
+
+					if($chr === '?'){ // skip unknown glyphs
+						continue;
+					}
+
+					yield $key($byte1, $byte2) => [$chr];
 				}
 
 			}
@@ -69,7 +79,13 @@ final class KanjiTest extends DataInterfaceTestAbstract{
 			else{
 
 				for($byte2 = 0x9f; $byte2 < 0xfd; $byte2++){
-					yield $key($byte1, $byte2) => [$val($byte1, $byte2)];
+					$chr = $val($byte1, $byte2);
+
+					if($chr === '?'){
+						continue;
+					}
+
+					yield $key($byte1, $byte2) => [$chr];
 				}
 
 			}

+ 6 - 2
tests/Data/NumberTest.php

@@ -11,14 +11,18 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\Number;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 
 /**
  * Tests the Number class
  */
 final class NumberTest extends DataInterfaceTestAbstract{
 
-	protected static string $FQN      = Number::class;
-	protected static string $testdata = '0123456789';
+	protected const testData = '0123456789';
+
+	protected static function getDataModeInterface(string $data):QRDataModeInterface{
+		return new Number($data);
+	}
 
 	/**
 	 * isNumber() should pass on any number and fail on anything else

+ 3 - 1
tests/Data/QRDataTest.php

@@ -16,12 +16,14 @@ use chillerlan\QRCode\Data\QRData;
 use chillerlan\QRCode\Output\QRGdImagePNG;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QROptions;
+use chillerlan\QRCodeTest\QRMatrixDebugTrait;
 use PHPUnit\Framework\TestCase;
 
 /**
  *
  */
 final class QRDataTest extends TestCase{
+	use QRMatrixDebugTrait;
 
 	/**
 	 * tests setting the BitBuffer object directly
@@ -56,7 +58,7 @@ final class QRDataTest extends TestCase{
 		$output       = new QRGdImagePNG($options, $matrix);
 		$decodeResult = (new QRCode($options))->readFromBlob($output->dump());
 
-		QRMatrixTest::debugMatrix($matrix);
+		$this->debugMatrix($matrix);
 
 		$this::assertSame($decodeResult->data, 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s');
 	}

+ 3 - 68
tests/Data/QRMatrixTest.php

@@ -10,19 +10,19 @@
 
 namespace chillerlan\QRCodeTest\Data;
 
-use PHPUnit\Framework\Attributes\DataProvider;
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
 use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
-use chillerlan\QRCode\Output\QRStringText;
+use chillerlan\QRCodeTest\QRMatrixDebugTrait;
 use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\Attributes\DataProvider;
 use Generator;
-use function defined;
 
 /**
  * Tests the QRMatrix class
  */
 final class QRMatrixTest extends TestCase{
+	use QRMatrixDebugTrait;
 
 	private const version = 40;
 	private QRMatrix $matrix;
@@ -37,70 +37,6 @@ final class QRMatrixTest extends TestCase{
 		);
 	}
 
-	/**
-	 * Matrix debugging console output
-	 */
-	public static function debugMatrix(QRMatrix $matrix):void{
-
-		/** @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist */
-		if(defined('TEST_IS_CI') && TEST_IS_CI === true){
-			return;
-		}
-
-		$opt = new QROptions;
-		$opt->eol          = "\n";
-		$opt->moduleValues = [
-			// finder
-			QRMatrix::M_FINDER_DARK    => QRStringText::ansi8('██', 124), // dark (true)
-			QRMatrix::M_FINDER         => QRStringText::ansi8('░░', 124), // light (false)
-			QRMatrix::M_FINDER_DOT     => QRStringText::ansi8('██', 124), // finder dot, dark (true)
-			// alignment
-			QRMatrix::M_ALIGNMENT_DARK => QRStringText::ansi8('██', 2),
-			QRMatrix::M_ALIGNMENT      => QRStringText::ansi8('░░', 2),
-			// timing
-			QRMatrix::M_TIMING_DARK    => QRStringText::ansi8('██', 184),
-			QRMatrix::M_TIMING         => QRStringText::ansi8('░░', 184),
-			// format
-			QRMatrix::M_FORMAT_DARK    => QRStringText::ansi8('██', 200),
-			QRMatrix::M_FORMAT         => QRStringText::ansi8('░░', 200),
-			// version
-			QRMatrix::M_VERSION_DARK   => QRStringText::ansi8('██', 21),
-			QRMatrix::M_VERSION        => QRStringText::ansi8('░░', 21),
-			// data
-			QRMatrix::M_DATA_DARK      => QRStringText::ansi8('██', 166),
-			QRMatrix::M_DATA           => QRStringText::ansi8('░░', 166),
-			// dark module
-			QRMatrix::M_DARKMODULE     => QRStringText::ansi8('██', 53),
-			// separator
-			QRMatrix::M_SEPARATOR      => QRStringText::ansi8('░░', 219),
-			// quiet zone
-			QRMatrix::M_QUIETZONE      => QRStringText::ansi8('░░', 195),
-			// logo space
-			QRMatrix::M_LOGO           => QRStringText::ansi8('░░', 105),
-			// empty
-			QRMatrix::M_NULL           => QRStringText::ansi8('░░', 231),
-		];
-
-		$out = (new QRStringText($opt, $matrix))->dump();
-
-		echo "\n\n".$out."\n\n";
-	}
-
-	/**
-	 * debugging shortcut - limit to a single version when using with matrixProvider
-	 *
-	 * @see QRMatrixTest::matrixProvider()
-	 */
-	protected function dm(QRMatrix $matrix):void{
-
-		// limit
-		if($matrix->getVersion()->getVersionNumber() !== 7){
-			return;
-		}
-
-		$this::debugMatrix($matrix);
-	}
-
 	/**
 	 * Validates the QRMatrix instance
 	 */
@@ -599,7 +535,6 @@ final class QRMatrixTest extends TestCase{
 		 */
 		$this::assertSame(0b01111101, $this->matrix->checkNeighbours(5, 5));
 
-
 		/*
 		 * M_TYPE check
 		 *

+ 11 - 4
tests/Output/QREpsTest.php

@@ -10,13 +10,13 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QREps;
+use chillerlan\QRCode\Output\{QREps, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 class QREpsTest extends QROutputTestAbstract{
 
-	protected string $FQN = QREps::class;
-
 	public static function moduleValueProvider():array{
 		return [
 			'valid: 3 int'                   => [[123, 123, 123], true],
@@ -29,6 +29,13 @@ class QREpsTest extends QROutputTestAbstract{
 		];
 	}
 
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QREps($options, $matrix);
+	}
+
 	/**
 	 * @inheritDoc
 	 */
@@ -40,7 +47,7 @@ class QREpsTest extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => [255, 255, 255],
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$this->outputInterface->dump();
 
 		$this::assertTrue(true); // tricking the code coverage

+ 13 - 7
tests/Output/QRFpdfTest.php

@@ -10,10 +10,11 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use FPDF;
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QRFpdf;
-
+use chillerlan\QRCode\Output\{QRFpdf, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
+use FPDF;
 use function class_exists;
 
 /**
@@ -21,8 +22,6 @@ use function class_exists;
  */
 final class QRFpdfTest extends QROutputTestAbstract{
 
-	protected string $FQN = QRFpdf::class;
-
 	/**
 	 * @inheritDoc
 	 */
@@ -35,6 +34,13 @@ final class QRFpdfTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRFpdf($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [
 			'valid: int'                     => [[123, 123, 123], true],
@@ -57,7 +63,7 @@ final class QRFpdfTest extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => [255, 255, 255],
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$this->outputInterface->dump();
 
 		$this::assertTrue(true); // tricking the code coverage
@@ -65,7 +71,7 @@ final class QRFpdfTest extends QROutputTestAbstract{
 
 	public function testOutputGetResource():void{
 		$this->options->returnResource = true;
-		$this->outputInterface         = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface         = $this->getOutputInterface($this->options, $this->matrix);
 
 		$this::assertInstanceOf(FPDF::class, $this->outputInterface->dump());
 	}

+ 10 - 2
tests/Output/QRGdImageBMPTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageBMP;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageBMP, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRGdImageBMPTest extends QRGdImageTestAbstract{
 
-	protected string $FQN = QRGdImageBMP::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRGdImageBMP($options, $matrix);
+	}
 
 }

+ 10 - 2
tests/Output/QRGdImageGIFTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageGIF;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageGIF, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRGdImageGIFTest extends QRGdImageTestAbstract{
 
-	protected string $FQN = QRGdImageGIF::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRGdImageGIF($options, $matrix);
+	}
 
 }

+ 10 - 2
tests/Output/QRGdImageJPGTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageJPEG;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageJPEG, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRGdImageJPGTest extends QRGdImageTestAbstract{
 
-	protected string $FQN  = QRGdImageJPEG::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRGdImageJPEG($options, $matrix);
+	}
 
 }

+ 10 - 2
tests/Output/QRGdImagePNGTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImagePNG;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImagePNG, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRGdImagePNGTest extends QRGdImageTestAbstract{
 
-	protected string $FQN  = QRGdImagePNG::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRGdImagePNG($options, $matrix);
+	}
 
 }

+ 4 - 3
tests/Output/QRGdImageTestAbstract.php

@@ -14,6 +14,7 @@ namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
 use GdImage;
+use function extension_loaded;
 
 /**
  * Tests the QRGdImage output module
@@ -54,7 +55,7 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => [255, 255, 255],
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$this->outputInterface->dump();
 
 		$this::assertTrue(true); // tricking the code coverage
@@ -62,14 +63,14 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{
 
 	public function testOutputGetResource():void{
 		$this->options->returnResource = true;
-		$this->outputInterface         = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface         = $this->getOutputInterface($this->options, $this->matrix);
 
 		$this::assertInstanceOf(GdImage::class, $this->outputInterface->dump());
 	}
 
 	public function testBase64MimeType():void{
 		$this->options->outputBase64 = true;
-		$this->outputInterface       = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface       = $this->getOutputInterface($this->options, $this->matrix);
 
 		$this::assertStringContainsString($this->outputInterface::MIME_TYPE, $this->outputInterface->dump());
 	}

+ 10 - 2
tests/Output/QRGdImageWEBPTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageWEBP;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageWEBP, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRGdImageWEBPTest extends QRGdImageTestAbstract{
 
-	protected string $FQN = QRGdImageWEBP::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRGdImageWEBP($options, $matrix);
+	}
 
 }

+ 13 - 5
tests/Output/QRImagickTest.php

@@ -12,17 +12,18 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QRImagick;
+use chillerlan\QRCode\Output\{QRImagick, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 use Imagick;
+use function extension_loaded;
 
 /**
  * Tests the QRImagick output module
  */
 final class QRImagickTest extends QROutputTestAbstract{
 
-	protected string $FQN = QRImagick::class;
-
 	/**
 	 * @inheritDoc
 	 */
@@ -35,6 +36,13 @@ final class QRImagickTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRImagick($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [
 			'invalid: wrong type'            => [[], false],
@@ -72,7 +80,7 @@ final class QRImagickTest extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => '#ECF9BE',
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$this->outputInterface->dump();
 
 		$this::assertTrue(true); // tricking the code coverage
@@ -80,7 +88,7 @@ final class QRImagickTest extends QROutputTestAbstract{
 
 	public function testOutputGetResource():void{
 		$this->options->returnResource = true;
-		$this->outputInterface         = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface         = $this->getOutputInterface($this->options, $this->matrix);
 
 		$this::assertInstanceOf(Imagick::class, $this->outputInterface->dump());
 	}

+ 10 - 2
tests/Output/QRMarkupHTMLTest.php

@@ -10,13 +10,21 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRMarkupHTML;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRMarkupHTML, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 
-	protected string $FQN = QRMarkupHTML::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRMarkupHTML($options, $matrix);
+	}
 
 }

+ 10 - 2
tests/Output/QRMarkupSVGTest.php

@@ -10,14 +10,22 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRMarkupSVG;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRMarkupSVG, QROutputInterface};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 
-	protected string $FQN = QRMarkupSVG::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRMarkupSVG($options, $matrix);
+	}
 
 	public static function moduleValueProvider():array{
 		return [

+ 1 - 1
tests/Output/QRMarkupTestAbstract.php

@@ -51,7 +51,7 @@ abstract class QRMarkupTestAbstract extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => '#ECF9BE',
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$data = $this->outputInterface->dump();
 		$this::assertStringContainsString('#4A6000', $data);
 		$this::assertStringContainsString('#ECF9BE', $data);

+ 17 - 23
tests/Output/QROutputTestAbstract.php

@@ -10,16 +10,14 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\QRCode;
-use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
 use chillerlan\Settings\SettingsContainerInterface;
 use PHPUnit\Framework\Attributes\DataProvider;
-use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
 use PHPUnit\Framework\TestCase;
-
-use function file_exists, mkdir;
-use function str_replace;
+use ReflectionClass;
+use function file_exists, file_get_contents, mkdir, realpath;
 
 /**
  * Test abstract for the several (built-in) output modules,
@@ -30,30 +28,27 @@ abstract class QROutputTestAbstract extends TestCase{
 	protected SettingsContainerInterface|QROptions $options;
 	protected QROutputInterface                    $outputInterface;
 	protected QRMatrix                             $matrix;
-	protected string                               $builddir = __DIR__.'/../../.build/output-test';
-	protected string                               $FQN;
+
+	protected const buildDir = __DIR__.'/../../.build/output-test/';
 
 	/**
 	 * Attempts to create a directory under /.build and instances several required objects
 	 */
 	protected function setUp():void{
 
-		if(!file_exists($this->builddir)){
-			mkdir($this->builddir, 0777, true);
+		if(!file_exists($this::buildDir)){
+			mkdir($this::buildDir, 0777, true);
 		}
 
 		$this->options                  = new QROptions;
-		$this->options->outputInterface = $this->FQN;
 		$this->matrix                   = (new QRCode($this->options))->addByteSegment('testdata')->getQRMatrix();
-		$this->outputInterface          = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface          = $this->getOutputInterface($this->options, $this->matrix);
 	}
 
-	/**
-	 * Validate the instance of the interface
-	 */
-	public function testInstance():void{
-		$this::assertInstanceOf(QROutputInterface::class, $this->outputInterface);
-	}
+	abstract protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface;
 
 	/**
 	 * Tests if an exception is thrown when trying to write a cache file to an invalid destination
@@ -62,7 +57,6 @@ abstract class QROutputTestAbstract extends TestCase{
 		$this->expectException(QRCodeOutputException::class);
 		$this->expectExceptionMessage('Cannot write data to cache file: /foo/bar.test');
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
 		$this->outputInterface->dump('/foo/bar.test');
 	}
 
@@ -70,8 +64,7 @@ abstract class QROutputTestAbstract extends TestCase{
 
 	#[DataProvider('moduleValueProvider')]
 	public function testValidateModuleValues(mixed $value, bool $expected):void{
-		/** @noinspection PhpUndefinedMethodInspection */
-		$this::assertSame($expected, $this->FQN::moduleValueIsValid($value));
+		$this::assertSame($expected, $this->outputInterface::moduleValueIsValid($value));
 	}
 
 	/*
@@ -88,9 +81,10 @@ abstract class QROutputTestAbstract extends TestCase{
 	 */
 	public function testRenderToCacheFile():void{
 		$this->options->outputBase64 = false;
-		$this->outputInterface       = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface       = $this->getOutputInterface($this->options, $this->matrix);
 		// create the cache file
-		$file = $this->builddir.'/test.output.'.str_replace('chillerlan\\QRCode\\Output\\', '', $this->FQN);
+		$name = (new ReflectionClass($this->outputInterface))->getShortName();
+		$file = realpath($this::buildDir).'test.output.'.$name;
 		$data = $this->outputInterface->dump($file);
 
 		$this::assertSame($data, file_get_contents($file));

+ 11 - 3
tests/Output/QRStringJSONTest.php

@@ -10,7 +10,10 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRStringJSON;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QROutputInterface, QRStringJSON};
+use chillerlan\Settings\SettingsContainerInterface;
 use PHPUnit\Framework\Attributes\DataProvider;
 use function extension_loaded;
 
@@ -19,8 +22,6 @@ use function extension_loaded;
  */
 final class QRStringJSONTest extends QROutputTestAbstract{
 
-	protected string $FQN = QRStringJSON::class;
-
 	/**
 	 * @inheritDoc
 	 */
@@ -33,6 +34,13 @@ final class QRStringJSONTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRStringJSON($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [[null, false]];
 	}

+ 10 - 3
tests/Output/QRStringTEXTTest.php

@@ -10,15 +10,22 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QRStringText;
+use chillerlan\QRCode\Output\{QROutputInterface, QRStringText};
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  *
  */
 final class QRStringTEXTTest extends QROutputTestAbstract{
 
-	protected string $FQN = QRStringText::class;
+	protected function getOutputInterface(
+		SettingsContainerInterface|QROptions $options,
+		QRMatrix                             $matrix
+	):QROutputInterface{
+		return new QRStringText($options, $matrix);
+	}
 
 	public static function moduleValueProvider():array{
 		return [
@@ -40,7 +47,7 @@ final class QRStringTEXTTest extends QROutputTestAbstract{
 			QRMatrix::M_DATA      => 'B',
 		];
 
-		$this->outputInterface = new $this->FQN($this->options, $this->matrix);
+		$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
 		$data                  = $this->outputInterface->dump();
 
 		$this::assertStringContainsString('A', $data);

+ 9 - 2
tests/QRCodeReaderGDTest.php

@@ -10,13 +10,20 @@
 
 namespace chillerlan\QRCodeTest;
 
-use chillerlan\QRCode\Common\GDLuminanceSource;
+use chillerlan\QRCode\Common\{GDLuminanceSource, LuminanceSourceInterface};
+use chillerlan\QRCode\QROptions;
+use chillerlan\Settings\SettingsContainerInterface;
 
 /**
  * Tests the GD based reader
  */
 final class QRCodeReaderGDTest extends QRCodeReaderTestAbstract{
 
-	protected string $FQN = GDLuminanceSource::class;
+	protected function getLuminanceSourceFromFile(
+		string                               $file,
+		SettingsContainerInterface|QROptions $options
+	):LuminanceSourceInterface{
+		return GDLuminanceSource::fromFile($file, $options);
+	}
 
 }

+ 14 - 10
tests/QRCodeReaderImagickTest.php

@@ -10,19 +10,19 @@
 
 namespace chillerlan\QRCodeTest;
 
-use chillerlan\QRCode\Common\IMagickLuminanceSource;
-use chillerlan\QRCode\QRCode;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Common\{IMagickLuminanceSource, LuminanceSourceInterface};
+use chillerlan\QRCode\Decoder\Decoder;
+use chillerlan\Settings\SettingsContainerInterface;
 use PHPUnit\Framework\Attributes\DataProvider;
 use function extension_loaded;
-use const PHP_OS_FAMILY, PHP_VERSION_ID;
+use const PHP_OS_FAMILY;
 
 /**
  * Tests the Imagick based reader
  */
 final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 
-	protected string $FQN = IMagickLuminanceSource::class;
-
 	protected function setUp():void{
 
 		if(!extension_loaded('imagick')){
@@ -34,6 +34,13 @@ final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 		$this->options->readerUseImagickIfAvailable = true;
 	}
 
+	protected function getLuminanceSourceFromFile(
+		string                               $file,
+		SettingsContainerInterface|QROptions $options
+	):LuminanceSourceInterface{
+		return IMagickLuminanceSource::fromFile($file, $options);
+	}
+
 	public static function vectorQRCodeProvider():array{
 		return [
 			// SVG convert only works on windows (Warning: Option --export-png= is deprecated)
@@ -51,12 +58,9 @@ final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 			$this::markTestSkipped('avoid imagick conversion errors (ha ha)');
 		}
 
-		if(PHP_OS_FAMILY === 'Windows' && PHP_VERSION_ID < 80100){
-			$this::markTestSkipped('This test fails on Windows and PHP < 8.1 for whatever reason');
-		}
+		$luminanceSource = $this->getLuminanceSourceFromFile($this::samplesDir.$img, $this->options);
 
-		$this::assertSame($expected, (string)(new QRCode)
-			->readFromSource(IMagickLuminanceSource::fromFile(__DIR__.'/samples/'.$img, $this->options)));
+		$this::assertSame($expected, (string)(new Decoder)->decode($luminanceSource));
 	}
 
 }

+ 17 - 12
tests/QRCodeReaderTestAbstract.php

@@ -12,24 +12,23 @@
 
 namespace chillerlan\QRCodeTest;
 
-use chillerlan\QRCodeTest\Data\QRMatrixTest;
-use PHPUnit\Framework\Attributes\DataProvider;
-use PHPUnit\Framework\Attributes\Group;
 use chillerlan\QRCode\{QRCode, QROptions};
-use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
+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, sprintf, str_repeat, substr;
+use function array_map, defined, realpath, sprintf, str_repeat, substr;
 
 /**
  * Tests the QR Code reader
  */
 abstract class QRCodeReaderTestAbstract extends TestCase{
-	use QRMaxLengthTrait;
+	use QRMaxLengthTrait, QRMatrixDebugTrait;
 
-	// https://www.bobrosslipsum.com/
+	/** @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. '
@@ -38,8 +37,9 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 		.'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 SettingsContainerInterface $options;
-	protected string                     $FQN;
+	protected const samplesDir = __DIR__.'/samples/';
+
+	protected SettingsContainerInterface|QROptions $options;
 
 	protected function setUp():void{
 
@@ -78,6 +78,11 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 		];
 	}
 
+	abstract protected function getLuminanceSourceFromFile(
+		string                               $file,
+		SettingsContainerInterface|QROptions $options
+	):LuminanceSourceInterface;
+
 	#[Group('slow')]
 	#[DataProvider('qrCodeProvider')]
 	public function testReader(string $img, string $expected, bool $grayscale):void{
@@ -87,10 +92,10 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 			$this->options->readerIncreaseContrast = true;
 		}
 
-		/** @noinspection PhpUndefinedMethodInspection */
-		$result = (new QRCode)->readFromSource($this->FQN::fromFile(__DIR__.'/samples/'.$img, $this->options));
+		$luminanceSource = $this->getLuminanceSourceFromFile(realpath($this::samplesDir.$img), $this->options);
+		$result          = (new Decoder)->decode($luminanceSource);
 
-		QRMatrixTest::debugMatrix($result->getQRMatrix());
+		$this->debugMatrix($result->getQRMatrix());
 
 		$this::assertSame($expected, (string)$result);
 	}

+ 3 - 2
tests/QRCodeTest.php

@@ -23,7 +23,8 @@ final class QRCodeTest extends TestCase{
 
 	private QRCode    $qrcode;
 	private QROptions $options;
-	private string    $builddir = __DIR__.'/../.build/output-test';
+
+	private const buildDir = __DIR__.'/../.build/output-test/';
 
 	/**
 	 * invoke test instances
@@ -73,7 +74,7 @@ final class QRCodeTest extends TestCase{
 	 * Tests if a cache file is properly saved in the given path
 	 */
 	public function testRenderToCacheFile():void{
-		$this->options->cachefile    = $this->builddir.'/test.cache.svg';
+		$this->options->cachefile    = $this::buildDir.'test.cache.svg';
 		$this->options->outputBase64 = false;
 		// create the cache file
 		$data = $this->qrcode->setOptions($this->options)->render('test');

+ 94 - 0
tests/QRMatrixDebugTrait.php

@@ -0,0 +1,94 @@
+<?php
+/**
+ * QRMatrixDebugTrait.php
+ *
+ * @created      26.11.2023
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2023 smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCodeTest;
+
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\QRStringText;
+use function defined, printf;
+
+/**
+ * Trait QRMatrixDebugTrait
+ */
+trait QRMatrixDebugTrait{
+
+	/**
+	 * Matrix debugging console output
+	 */
+	protected function debugMatrix(QRMatrix $matrix):void{
+
+		/** @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist */
+		if(defined('TEST_IS_CI') && TEST_IS_CI === true){
+			return;
+		}
+
+		$options = new QROptions;
+
+		$options->eol          = "\n";
+		$options->moduleValues = [
+			// finder
+			QRMatrix::M_FINDER_DARK      => QRStringText::ansi8('██', 124),
+			QRMatrix::M_FINDER           => QRStringText::ansi8('░░', 124),
+			QRMatrix::M_FINDER_DOT       => QRStringText::ansi8('██', 124),
+			QRMatrix::M_FINDER_DOT_LIGHT => QRStringText::ansi8('░░', 124),
+			// alignment
+			QRMatrix::M_ALIGNMENT_DARK   => QRStringText::ansi8('██', 2),
+			QRMatrix::M_ALIGNMENT        => QRStringText::ansi8('░░', 2),
+			// timing
+			QRMatrix::M_TIMING_DARK      => QRStringText::ansi8('██', 184),
+			QRMatrix::M_TIMING           => QRStringText::ansi8('░░', 184),
+			// format
+			QRMatrix::M_FORMAT_DARK      => QRStringText::ansi8('██', 200),
+			QRMatrix::M_FORMAT           => QRStringText::ansi8('░░', 200),
+			// version
+			QRMatrix::M_VERSION_DARK     => QRStringText::ansi8('██', 21),
+			QRMatrix::M_VERSION          => QRStringText::ansi8('░░', 21),
+			// data
+			QRMatrix::M_DATA_DARK        => QRStringText::ansi8('██', 166),
+			QRMatrix::M_DATA             => QRStringText::ansi8('░░', 166),
+			// dark module
+			QRMatrix::M_DARKMODULE       => QRStringText::ansi8('██', 53),
+			QRMatrix::M_DARKMODULE_LIGHT => QRStringText::ansi8('░░', 53),
+			// separator
+			QRMatrix::M_SEPARATOR_DARK   => QRStringText::ansi8('██', 219),
+			QRMatrix::M_SEPARATOR        => QRStringText::ansi8('░░', 219),
+			// quiet zone
+			QRMatrix::M_QUIETZONE_DARK   => QRStringText::ansi8('██', 195),
+			QRMatrix::M_QUIETZONE        => QRStringText::ansi8('░░', 195),
+			// logo space
+			QRMatrix::M_LOGO_DARK        => QRStringText::ansi8('██', 105),
+			QRMatrix::M_LOGO             => QRStringText::ansi8('░░', 105),
+			// empty
+			QRMatrix::M_NULL             => QRStringText::ansi8('░░', 231),
+		];
+
+		$out = (new QRStringText($options, $matrix))->dump();
+
+		printf("\n\n%s\n\n", $out) ;
+	}
+
+	/**
+	 * debugging shortcut - limit to a single version when using with matrixProvider
+	 *
+	 * @see QRMatrixTest::matrixProvider()
+	 */
+	protected function dm(QRMatrix $matrix):void{
+
+		// limit
+		/** @noinspection PhpUndefinedConstantInspection - see phpunit.xml.dist */
+		if(!defined('MATRIX_DEBUG_VERSION') || $matrix->getVersion()->getVersionNumber() !== MATRIX_DEBUG_VERSION){
+			return;
+		}
+
+		$this->debugMatrix($matrix);
+	}
+
+}

+ 2 - 4
tests/QROptionsTest.php

@@ -12,10 +12,8 @@
 
 namespace chillerlan\QRCodeTest;
 
-use chillerlan\QRCode\Common\EccLevel;
-use chillerlan\QRCode\QRCodeException;
-use chillerlan\QRCode\QROptions;
-use chillerlan\QRCode\Common\Version;
+use chillerlan\QRCode\{QRCodeException, QROptions};
+use chillerlan\QRCode\Common\{EccLevel, Version};
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\TestCase;