Explorar el Código

:bath: test cleanup

smiley hace 2 años
padre
commit
6239074411

+ 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\TestCase;
 
 /**
@@ -31,15 +31,17 @@ 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());
 	}
 

+ 19 - 19
tests/Common/MaskPatternTest.php

@@ -129,36 +129,36 @@ final class MaskPatternTest extends TestCase{
 
 	public function testPenaltyRule1():void{
 		// horizontal
-		$this::assertSame(0, MaskPattern::testRule1([[0, 0, 0, 0]], 1, 4));
-		$this::assertSame(3, MaskPattern::testRule1([[0, 0, 0, 0, 0, 1]], 1, 6));
-		$this::assertSame(4, MaskPattern::testRule1([[0, 0, 0, 0, 0, 0]], 1, 6));
+		$this::assertSame(0, MaskPattern::testRule1([[false, false, false, false]], 1, 4));
+		$this::assertSame(3, MaskPattern::testRule1([[false, false, false, false, false, true]], 1, 6));
+		$this::assertSame(4, MaskPattern::testRule1([[false, false, false, false, false, false]], 1, 6));
 		// vertical
-		$this::assertSame(0, MaskPattern::testRule1([[0], [0], [0], [0]], 4, 1));
-		$this::assertSame(3, MaskPattern::testRule1([[0], [0], [0], [0], [0], [1]], 6, 1));
-		$this::assertSame(4, MaskPattern::testRule1([[0], [0], [0], [0], [0], [0]], 6, 1));
+		$this::assertSame(0, MaskPattern::testRule1([[false], [false], [false], [false]], 4, 1));
+		$this::assertSame(3, MaskPattern::testRule1([[false], [false], [false], [false], [false], [true]], 6, 1));
+		$this::assertSame(4, MaskPattern::testRule1([[false], [false], [false], [false], [false], [false]], 6, 1));
 	}
 
 	public function testPenaltyRule2():void{
-		$this::assertSame(0, MaskPattern::testRule2([[0]], 1, 1));
-		$this::assertSame(0, MaskPattern::testRule2([[0, 0], [0, 1]], 2, 2));
-		$this::assertSame(3, MaskPattern::testRule2([[0, 0], [0, 0]], 2, 2));
-		$this::assertSame(12, MaskPattern::testRule2([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 3, 3));
+		$this::assertSame(0, MaskPattern::testRule2([[false]], 1, 1));
+		$this::assertSame(0, MaskPattern::testRule2([[false, false], [false, true]], 2, 2));
+		$this::assertSame(3, MaskPattern::testRule2([[false, false], [false, false]], 2, 2));
+		$this::assertSame(12, MaskPattern::testRule2([[false, false, false], [false, false, false], [false, false, false]], 3, 3));
 	}
 
 	public function testPenaltyRule3():void{
 		// horizontal
-		$this::assertSame(40, MaskPattern::testRule3([[0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1]], 1, 11));
-		$this::assertSame(40, MaskPattern::testRule3([[1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0]], 1, 11));
-		$this::assertSame(0, MaskPattern::testRule3([[1, 0, 1, 1, 1, 0, 1]], 1, 7));
+		$this::assertSame(40, MaskPattern::testRule3([[false, false, false, false, true, false, true, true, true, false, true]], 1, 11));
+		$this::assertSame(40, MaskPattern::testRule3([[true, false, true, true, true, false, true, false, false, false, false]], 1, 11));
+		$this::assertSame(0, MaskPattern::testRule3([[true, false, true, true, true, false, true]], 1, 7));
 		// vertical
-		$this::assertSame(40, MaskPattern::testRule3([[0], [0], [0], [0], [1], [0], [1], [1], [1], [0], [1]], 11, 1));
-		$this::assertSame(40, MaskPattern::testRule3([[1], [0], [1], [1], [1], [0], [1], [0], [0], [0], [0]], 11, 1));
-		$this::assertSame(0, MaskPattern::testRule3([[1], [0], [1], [1], [1], [0], [1]], 7, 1));
+		$this::assertSame(40, MaskPattern::testRule3([[false], [false], [false], [false], [true], [false], [true], [true], [true], [false], [true]], 11, 1));
+		$this::assertSame(40, MaskPattern::testRule3([[true], [false], [true], [true], [true], [false], [true], [false], [false], [false], [false]], 11, 1));
+		$this::assertSame(0, MaskPattern::testRule3([[true], [false], [true], [true], [true], [false], [true]], 7, 1));
 	}
 
 	public function testPenaltyRule4():void{
-		$this::assertSame(100, MaskPattern::testRule4([[0]], 1, 1));
-		$this::assertSame(0, MaskPattern::testRule4([[0, 1]], 1, 2));
-		$this::assertSame(30, MaskPattern::testRule4([[0, 1, 1, 1, 1, 0]], 1, 6));
+		$this::assertSame(100, MaskPattern::testRule4([[false]], 1, 1));
+		$this::assertSame(0, MaskPattern::testRule4([[false, true]], 1, 2));
+		$this::assertSame(30, MaskPattern::testRule4([[false, true, true, true, true, false]], 1, 6));
 	}
 }

+ 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

+ 31 - 46
tests/Data/DataInterfaceTestAbstract.php

@@ -14,17 +14,9 @@ use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Mode, Version};
 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\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
@@ -34,26 +26,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);
 
@@ -86,18 +67,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')));
 	}
 
 	/**
@@ -111,13 +90,12 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	 * Tests decoding a data segment from a given BitBuffer
 	 *
 	 * @dataProvider versionBreakpointProvider
+	 * @phan-suppress PhanAccessClassConstantInternal
 	 */
 	public function testDecodeSegment(int $version):void{
 		$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
@@ -125,7 +103,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));
 	}
 
 	/**
@@ -138,18 +116,21 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	 *   - the maximum allowed character length
 	 *
 	 * @throws \Exception
+	 * @phan-suppress PhanAccessClassConstantInternal
 	 */
 	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
@@ -170,13 +151,16 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 
 	/**
 	 * @dataProvider maxLengthProvider
+	 * @phan-suppress PhanAccessClassConstantInternal
 	 */
 	public function testMaxLength(Version $version, EccLevel $eccLevel, string $str):void{
 		$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,13 +171,16 @@ abstract class DataInterfaceTestAbstract extends TestCase{
 	 * Tests getting the minimum QR version for the given data
 	 *
 	 * @dataProvider maxLengthProvider
+	 * @phan-suppress PhanAccessClassConstantInternal
 	 */
 	public function testGetMinimumVersion(Version $version, EccLevel $eccLevel, string $str):void{
 		$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());
@@ -220,7 +207,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)]);
 	}
 
 	/**
@@ -230,7 +217,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))]);
 	}
 
 	/**
@@ -240,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('##');
 	}
 
 	/**
@@ -251,8 +237,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 - 9
tests/Data/ECITest.php

@@ -20,9 +20,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);
@@ -31,11 +32,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);
 
@@ -67,10 +68,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:');
@@ -81,8 +81,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);
@@ -129,7 +127,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);
 	}
 

+ 13 - 5
tests/Data/HanziTest.php

@@ -11,6 +11,7 @@
 namespace chillerlan\QRCodeTest\Data;
 
 use chillerlan\QRCode\Data\Hanzi;
+use chillerlan\QRCode\Data\QRDataModeInterface;
 use Generator, Throwable;
 use function bin2hex, chr, defined, sprintf;
 
@@ -19,8 +20,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
@@ -48,9 +52,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 function bin2hex, chr, defined, sprintf;
 
@@ -19,8 +20,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
@@ -40,7 +44,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++){
@@ -59,7 +63,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];
 				}
 
 			}
@@ -67,7 +77,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

+ 5 - 5
tests/Data/QRDataTest.php

@@ -13,16 +13,17 @@ namespace chillerlan\QRCodeTest\Data;
 use chillerlan\QRCode\Common\BitBuffer;
 use chillerlan\QRCode\Common\MaskPattern;
 use chillerlan\QRCode\Data\QRData;
-use chillerlan\QRCode\Output\QRGdImage;
-use chillerlan\QRCode\Output\QROutputInterface;
+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
@@ -51,14 +52,13 @@ final class QRDataTest extends TestCase{
 		$this::assertSame(3, $matrix->getVersion()->getVersionNumber());
 
 		// attempt to read
-		$options->outputType                  = QROutputInterface::GDIMAGE_PNG;
 		$options->outputBase64                = false;
 		$options->readerUseImagickIfAvailable = false;
 
-		$output       = new QRGdImage($options, $matrix);
+		$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');
 	}

+ 11 - 83
tests/Data/QRMatrixTest.php

@@ -13,15 +13,15 @@ namespace chillerlan\QRCodeTest\Data;
 use chillerlan\QRCode\{QRCode, QROptions};
 use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
 use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
-use chillerlan\QRCode\Output\{QROutputInterface, QRString};
+use chillerlan\QRCodeTest\QRMatrixDebugTrait;
 use PHPUnit\Framework\TestCase;
 use Generator;
-use function defined;
 
 /**
  * Tests the QRMatrix class
  */
 final class QRMatrixTest extends TestCase{
+	use QRMatrixDebugTrait;
 
 	private const version = 40;
 	private QRMatrix $matrix;
@@ -36,103 +36,31 @@ 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->outputType  = QROutputInterface::STRING_TEXT;
-		$opt->eol         = "\n";
-		$opt->moduleValues = [
-			// finder
-			QRMatrix::M_FINDER_DARK    => QRString::ansi8('██', 124), // dark (true)
-			QRMatrix::M_FINDER         => QRString::ansi8('░░', 124), // light (false)
-			QRMatrix::M_FINDER_DOT     => QRString::ansi8('██', 124), // finder dot, dark (true)
-			// alignment
-			QRMatrix::M_ALIGNMENT_DARK => QRString::ansi8('██', 2),
-			QRMatrix::M_ALIGNMENT      => QRString::ansi8('░░', 2),
-			// timing
-			QRMatrix::M_TIMING_DARK    => QRString::ansi8('██', 184),
-			QRMatrix::M_TIMING         => QRString::ansi8('░░', 184),
-			// format
-			QRMatrix::M_FORMAT_DARK    => QRString::ansi8('██', 200),
-			QRMatrix::M_FORMAT         => QRString::ansi8('░░', 200),
-			// version
-			QRMatrix::M_VERSION_DARK   => QRString::ansi8('██', 21),
-			QRMatrix::M_VERSION        => QRString::ansi8('░░', 21),
-			// data
-			QRMatrix::M_DATA_DARK      => QRString::ansi8('██', 166),
-			QRMatrix::M_DATA           => QRString::ansi8('░░', 166),
-			// dark module
-			QRMatrix::M_DARKMODULE     => QRString::ansi8('██', 53),
-			// separator
-			QRMatrix::M_SEPARATOR      => QRString::ansi8('░░', 219),
-			// quiet zone
-			QRMatrix::M_QUIETZONE      => QRString::ansi8('░░', 195),
-			// logo space
-			QRMatrix::M_LOGO           => QRString::ansi8('░░', 105),
-			// empty
-			QRMatrix::M_NULL           => QRString::ansi8('░░', 231),
-		];
-
-		$out = (new QRString($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
-	 */
-	public function testInstance():void{
-		$this::assertInstanceOf(QRMatrix::class, $this->matrix);
-	}
-
 	/**
 	 * Tests if size() returns the actual matrix size/count
 	 */
-	public function testSize():void{
+	public function testGetSize():void{
 		$this::assertCount($this->matrix->getSize(), $this->matrix->getMatrix(true));
 	}
 
 	/**
 	 * Tests if version() returns the current (given) version
 	 */
-	public function testVersion():void{
+	public function testGetVersion():void{
 		$this::assertSame($this::version, $this->matrix->getVersion()->getVersionNumber());
 	}
 
 	/**
 	 * Tests if eccLevel() returns the current (given) ECC level
 	 */
-	public function testECC():void{
+	public function testGetECC():void{
 		$this::assertSame(EccLevel::L, $this->matrix->getEccLevel()->getLevel());
 	}
 
 	/**
 	 * Tests if maskPattern() returns the current (or default) mask pattern
 	 */
-	public function testMaskPattern():void{
+	public function testGetMaskPattern():void{
 		// set via matrix evaluation
 		$matrix = (new QRCode)->addByteSegment('testdata')->getQRMatrix();
 
@@ -401,7 +329,7 @@ final class QRMatrixTest extends TestCase{
 	/**
 	 * Tests if the logo space is drawn square if one of the dimensions is omitted
 	 */
-	public function testSetLogoSpaceOmitHeight():void{
+	public function testSetLogoSpaceOmitDimension():void{
 		$o = new QROptions;
 		$o->version         = 2;
 		$o->eccLevel        = EccLevel::H;
@@ -411,7 +339,7 @@ final class QRMatrixTest extends TestCase{
 
 		$matrix = (new QRCode($o))->addByteSegment('testdata')->getQRMatrix();
 
-		$this::debugMatrix($matrix);
+		$this->debugMatrix($matrix);
 
 		$this::assertFalse($matrix->checkType(9, 9, QRMatrix::M_LOGO));
 		$this::assertTrue($matrix->checkType(10, 10, QRMatrix::M_LOGO));
@@ -433,7 +361,7 @@ final class QRMatrixTest extends TestCase{
 		// also testing size adjustment to uneven numbers
 		$matrix->setLogoSpace(20, 14);
 
-		$this::debugMatrix($matrix);
+		$this->debugMatrix($matrix);
 
 		// NW corner
 		$this::assertFalse($matrix->checkType(17, 20, QRMatrix::M_LOGO));
@@ -457,7 +385,7 @@ final class QRMatrixTest extends TestCase{
 		$matrix = (new QRCode($o))->addByteSegment('testdata')->getQRMatrix();
 
 		$matrix->setLogoSpace(21, 21, -10, -10);
-		$this::debugMatrix($matrix);
+		$this->debugMatrix($matrix);
 		$this::assertSame(QRMatrix::M_QUIETZONE, $matrix->get(9, 9));
 		$this::assertSame(QRMatrix::M_LOGO, $matrix->get(10, 10));
 		$this::assertSame(QRMatrix::M_LOGO, $matrix->get(20, 20));
@@ -466,7 +394,7 @@ final class QRMatrixTest extends TestCase{
 		// I just realized that setLogoSpace() could be called multiple times
 		// on the same instance, and I'm not going to do anything about it :P
 		$matrix->setLogoSpace(21, 21, 45, 45);
-		$this::debugMatrix($matrix);
+		$this->debugMatrix($matrix);
 		$this::assertNotSame(QRMatrix::M_LOGO, $matrix->get(54, 54));
 		$this::assertSame(QRMatrix::M_LOGO, $matrix->get(55, 55));
 		$this::assertSame(QRMatrix::M_QUIETZONE, $matrix->get(67, 67));

+ 6 - 2
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\QROutputInterface;
 
 class QREpsTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QREps::class;
 	protected string $type = QROutputInterface::EPS;
 
 	public static function moduleValueProvider():array{
@@ -31,6 +31,10 @@ class QREpsTest extends QROutputTestAbstract{
 		];
 	}
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QREps($options, $matrix);
+	}
+
 	/**
 	 * @inheritDoc
 	 */
@@ -42,7 +46,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

+ 8 - 4
tests/Output/QRFpdfTest.php

@@ -10,9 +10,10 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use FPDF;
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRFpdf, QROutputInterface};
+use FPDF;
 
 use function class_exists;
 
@@ -21,7 +22,6 @@ use function class_exists;
  */
 final class QRFpdfTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QRFpdf::class;
 	protected string $type = QROutputInterface::FPDF;
 
 	/**
@@ -36,6 +36,10 @@ final class QRFpdfTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRFpdf($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [
 			'valid: int'                     => [[123, 123, 123], true],
@@ -58,7 +62,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
@@ -66,7 +70,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());
 	}

+ 7 - 3
tests/Output/QRGdImageBMPTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageBMP;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageBMP, QROutputInterface};
 
 /**
  *
@@ -19,6 +20,9 @@ use chillerlan\QRCode\Output\QROutputInterface;
 final class QRGdImageBMPTest extends QRGdImageTestAbstract{
 
 	protected string $type = QROutputInterface::GDIMAGE_BMP;
-	protected string $FQN  = QRGdImageBMP::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRGdImageBMP($options, $matrix);
+	}
 
 }

+ 7 - 3
tests/Output/QRGdImageGIFTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageGIF;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageGIF, QROutputInterface};
 
 /**
  *
@@ -19,6 +20,9 @@ use chillerlan\QRCode\Output\QROutputInterface;
 final class QRGdImageGIFTest extends QRGdImageTestAbstract{
 
 	protected string $type = QROutputInterface::GDIMAGE_GIF;
-	protected string $FQN  = QRGdImageGIF::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRGdImageGIF($options, $matrix);
+	}
 
 }

+ 7 - 3
tests/Output/QRGdImageJPGTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageJPEG;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageJPEG, QROutputInterface};
 
 /**
  *
@@ -19,6 +20,9 @@ use chillerlan\QRCode\Output\QROutputInterface;
 final class QRGdImageJPGTest extends QRGdImageTestAbstract{
 
 	protected string $type = QROutputInterface::GDIMAGE_JPG;
-	protected string $FQN  = QRGdImageJPEG::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRGdImageJPEG($options, $matrix);
+	}
 
 }

+ 7 - 3
tests/Output/QRGdImagePNGTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImagePNG;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImagePNG, QROutputInterface};
 
 /**
  *
@@ -19,6 +20,9 @@ use chillerlan\QRCode\Output\QROutputInterface;
 final class QRGdImagePNGTest extends QRGdImageTestAbstract{
 
 	protected string $type = QROutputInterface::GDIMAGE_PNG;
-	protected string $FQN  = QRGdImagePNG::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRGdImagePNG($options, $matrix);
+	}
 
 }

+ 7 - 4
tests/Output/QRGdImageTestAbstract.php

@@ -6,11 +6,14 @@
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2017 Smiley
  * @license      MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
  */
 
 namespace chillerlan\QRCodeTest\Output;
 
 use chillerlan\QRCode\Data\QRMatrix;
+use function extension_loaded;
 use const PHP_MAJOR_VERSION;
 
 /**
@@ -52,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
@@ -63,7 +66,7 @@ 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);
 
 		$actual = $this->outputInterface->dump();
 
@@ -75,8 +78,8 @@ abstract class QRGdImageTestAbstract extends QROutputTestAbstract{
 
 	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);
+		/** @phan-suppress-next-line PhanAccessClassConstantInternal */
 		$this::assertStringContainsString($this->outputInterface::MIME_TYPE, $this->outputInterface->dump());
 	}
 

+ 7 - 3
tests/Output/QRGdImageWEBPTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QRGdImageWEBP;
-use chillerlan\QRCode\Output\QROutputInterface;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QRGdImageWEBP, QROutputInterface};
 
 /**
  *
@@ -19,6 +20,9 @@ use chillerlan\QRCode\Output\QROutputInterface;
 final class QRGdImageWEBPTest extends QRGdImageTestAbstract{
 
 	protected string $type = QROutputInterface::GDIMAGE_WEBP;
-	protected string $FQN  = QRGdImageWEBP::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRGdImageWEBP($options, $matrix);
+	}
 
 }

+ 8 - 3
tests/Output/QRImagickTest.php

@@ -13,16 +13,17 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRImagick, QROutputInterface};
 use Imagick;
+use function extension_loaded;
 
 /**
  * Tests the QRImagick output module
  */
 final class QRImagickTest extends QROutputTestAbstract{
 
-	protected string $FQN  = QRImagick::class;
 	protected string $type = QROutputInterface::IMAGICK;
 
 	/**
@@ -37,6 +38,10 @@ final class QRImagickTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRImagick($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [
 			'invalid: wrong type'            => [[], false],
@@ -74,7 +79,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
@@ -82,7 +87,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());
 	}

+ 6 - 1
tests/Output/QRMarkupHTMLTest.php

@@ -10,6 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRMarkupHTML, QROutputInterface};
 
 /**
@@ -17,7 +19,10 @@ use chillerlan\QRCode\Output\{QRMarkupHTML, QROutputInterface};
  */
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 
-	protected string $FQN  = QRMarkupHTML::class;
 	protected string $type = QROutputInterface::MARKUP_HTML;
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRMarkupHTML($options, $matrix);
+	}
+
 }

+ 6 - 1
tests/Output/QRMarkupSVGTest.php

@@ -10,6 +10,8 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Output\{QRMarkupSVG, QROutputInterface};
 
 /**
@@ -17,9 +19,12 @@ use chillerlan\QRCode\Output\{QRMarkupSVG, QROutputInterface};
  */
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 
-	protected string $FQN  = QRMarkupSVG::class;
 	protected string $type = QROutputInterface::MARKUP_SVG;
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRMarkupSVG($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
 		return [
 			// css colors from parent

+ 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);

+ 13 - 20
tests/Output/QROutputTestAbstract.php

@@ -10,13 +10,12 @@
 
 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 PHPUnit\Framework\TestCase;
-
-use function file_exists, mkdir;
+use ReflectionClass;
+use function file_exists, file_get_contents, mkdir, realpath;
 
 /**
  * Test abstract for the several (built-in) output modules,
@@ -28,31 +27,26 @@ abstract class QROutputTestAbstract extends TestCase{
 	protected QROptions         $options;
 	protected QROutputInterface $outputInterface;
 	protected QRMatrix          $matrix;
-	protected string            $builddir = __DIR__.'/../../.build/output_test';
-	protected string            $FQN;
+
 	protected string            $type;
+	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->outputType = $this->type;
 		$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(QROptions $options, QRMatrix $matrix):QROutputInterface;
 
 	/**
 	 * Tests if an exception is thrown when trying to write a cache file to an invalid destination
@@ -61,7 +55,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');
 	}
 
@@ -74,8 +67,7 @@ abstract class QROutputTestAbstract extends TestCase{
 	 * @dataProvider moduleValueProvider
 	 */
 	public function testValidateModuleValues($value, bool $expected):void{
-		/** @noinspection PhpUndefinedMethodInspection */
-		$this::assertSame($expected, $this->FQN::moduleValueIsValid($value));
+		$this::assertSame($expected, $this->outputInterface::moduleValueIsValid($value));
 	}
 
 	/*
@@ -92,9 +84,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.'.$this->type;
+		$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));

+ 18 - 4
tests/Output/QRStringJSONTest.php

@@ -10,8 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
-use chillerlan\QRCode\Output\QROutputInterface;
-use chillerlan\QRCode\Output\QRStringJSON;
+use chillerlan\QRCode\QROptions;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\{QROutputInterface, QRStringJSON};
 use function extension_loaded;
 
 /**
@@ -20,7 +21,6 @@ use function extension_loaded;
 final class QRStringJSONTest extends QROutputTestAbstract{
 
 	protected string $type = QROutputInterface::STRING_JSON;
-	protected string $FQN  = QRStringJSON::class;
 
 	/**
 	 * @inheritDoc
@@ -34,8 +34,22 @@ final class QRStringJSONTest extends QROutputTestAbstract{
 		parent::setUp();
 	}
 
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRStringJSON($options, $matrix);
+	}
+
 	public static function moduleValueProvider():array{
-		return [];
+		return [[null, false]];
+	}
+
+	/**
+	 * @param mixed $value
+	 * @param bool  $expected
+	 *
+	 * @dataProvider moduleValueProvider
+	 */
+	public function testValidateModuleValues($value, bool $expected):void{
+		$this::markTestSkipped('N/A (JSON test)');
 	}
 
 	public function testSetModuleValues():void{

+ 7 - 4
tests/Output/QRStringTEXTTest.php

@@ -10,9 +10,9 @@
 
 namespace chillerlan\QRCodeTest\Output;
 
+use chillerlan\QRCode\QROptions;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QROutputInterface;
-use chillerlan\QRCode\Output\QRStringText;
+use chillerlan\QRCode\Output\{QROutputInterface, QRStringText};
 
 /**
  *
@@ -20,7 +20,10 @@ use chillerlan\QRCode\Output\QRStringText;
 final class QRStringTEXTTest extends QROutputTestAbstract{
 
 	protected string $type = QROutputInterface::STRING_TEXT;
-	protected string $FQN  = QRStringText::class;
+
+	protected function getOutputInterface(QROptions $options, QRMatrix $matrix):QROutputInterface{
+		return new QRStringText($options, $matrix);
+	}
 
 	public static function moduleValueProvider():array{
 		return [
@@ -42,7 +45,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);

+ 5 - 2
tests/QRCodeReaderGDTest.php

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

+ 10 - 6
tests/QRCodeReaderImagickTest.php

@@ -10,8 +10,9 @@
 
 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 function extension_loaded;
 use const PHP_OS_FAMILY, PHP_VERSION_ID;
 
@@ -20,8 +21,6 @@ use const PHP_OS_FAMILY, PHP_VERSION_ID;
  */
 final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 
-	protected string $FQN = IMagickLuminanceSource::class;
-
 	protected function setUp():void{
 
 		if(!extension_loaded('imagick')){
@@ -33,6 +32,10 @@ final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 		$this->options->readerUseImagickIfAvailable = true;
 	}
 
+	protected function getLuminanceSourceFromFile(string $file, 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)
@@ -56,8 +59,9 @@ final class QRCodeReaderImagickTest extends QRCodeReaderTestAbstract{
 			$this::markTestSkipped('This test fails on Windows and PHP < 8.1 for whatever reason');
 		}
 
-		$this::assertSame($expected, (string)(new QRCode)
-			->readFromSource(IMagickLuminanceSource::fromFile(__DIR__.'/samples/'.$img, $this->options)));
+		$luminanceSource = $this->getLuminanceSourceFromFile($this::samplesDir.$img, $this->options);
+
+		$this::assertSame($expected, (string)(new Decoder)->decode($luminanceSource));
 	}
 
 }

+ 13 - 11
tests/QRCodeReaderTestAbstract.php

@@ -12,22 +12,21 @@
 
 namespace chillerlan\QRCodeTest;
 
-use chillerlan\QRCodeTest\Data\QRMatrixTest;
 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\QROutputInterface;
-use chillerlan\Settings\SettingsContainerInterface;
 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. '
@@ -36,8 +35,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 QROptions $options;
 
 	protected function setUp():void{
 
@@ -76,6 +76,8 @@ abstract class QRCodeReaderTestAbstract extends TestCase{
 		];
 	}
 
+	abstract protected function getLuminanceSourceFromFile(string $file, QROptions $options):LuminanceSourceInterface;
+
 	/**
 	 * @group slow
 	 * @dataProvider qrCodeProvider
@@ -87,10 +89,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);
 	}

+ 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);
+	}
+
+}