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

:bath: QRDataModeInterface: validate data on init, test cleanup

codemasher 4 лет назад
Родитель
Сommit
48b6c21090

+ 3 - 17
src/Data/AlphaNum.php

@@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, Mode};
 
-use function array_flip, ceil, ord, sprintf, str_split;
+use function array_flip, ceil, str_split;
 
 /**
  * Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -75,30 +75,16 @@ final class AlphaNum extends QRDataModeAbstract{
 
 		// encode 2 characters in 11 bits
 		for($i = 0; $i + 1 < $len; $i += 2){
-			$bitBuffer->put($this->getCharCode($this->data[$i]) * 45 + $this->getCharCode($this->data[$i + 1]), 11);
+			$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[$i + 1]], 11);
 		}
 
 		// encode a remaining character in 6 bits
 		if($i < $len){
-			$bitBuffer->put($this->getCharCode($this->data[$i]), 6);
+			$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
 		}
 
 	}
 
-	/**
-	 * get the code for the given character
-	 *
-	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
-	 */
-	private function getCharCode(string $chr):int{
-
-		if(isset(self::CHAR_TO_ORD[$chr])){
-			return self::CHAR_TO_ORD[$chr];
-		}
-
-		throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr)));
-	}
-
 	/**
 	 * @inheritDoc
 	 *

+ 2 - 2
src/Data/Byte.php

@@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, Mode};
 
-use function ord;
+use function chr, ord;
 
 /**
  * Byte mode, ISO-8859-1 or UTF-8
@@ -76,7 +76,7 @@ final class Byte extends QRDataModeAbstract{
 		$readBytes = '';
 
 		for($i = 0; $i < $length; $i++){
-			$readBytes .= \chr($bitBuffer->read(8));
+			$readBytes .= chr($bitBuffer->read(8));
 		}
 
 		return $readBytes;

+ 4 - 0
src/Data/Kanji.php

@@ -57,6 +57,10 @@ final class Kanji extends QRDataModeAbstract{
 		$i   = 0;
 		$len = strlen($string);
 
+		if($len < 2){
+			return false;
+		}
+
 		while($i + 1 < $len){
 			$c = ((0xff & ord($string[$i])) << 8) | (0xff & ord($string[$i + 1]));
 

+ 2 - 4
src/Data/Number.php

@@ -98,14 +98,12 @@ final class Number extends QRDataModeAbstract{
 		$num = 0;
 
 		foreach(str_split($string) as $chr){
-			$c = ord($chr);
 
 			if(!isset(self::NUMBER_TO_ORD[$chr])){
-				throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
+				throw new QRCodeDataException(sprintf('illegal char: "%s"', $chr));
 			}
 
-			$c   = $c - 48; // ord('0')
-			$num = $num * 10 + $c;
+			$num = $num * 10 + ord($chr) - 48;
 		}
 
 		return $num;

+ 4 - 7
src/Data/QRData.php

@@ -193,19 +193,16 @@ final class QRData{
 		// The message bit stream shall then be extended to fill the data capacity of the symbol
 		// corresponding to the Version and Error Correction Level, by the addition of the Pad
 		// Codewords 11101100 and 00010001 alternately.
-		while(true){
-
-			if($this->bitBuffer->getLength() >= $MAX_BITS){
-				break;
-			}
+		$alternate = false;
 
-			$this->bitBuffer->put(0b11101100, 8);
+		while(true){
 
 			if($this->bitBuffer->getLength() >= $MAX_BITS){
 				break;
 			}
 
-			$this->bitBuffer->put(0b00010001, 8);
+			$this->bitBuffer->put($alternate ? 0b00010001 : 0b11101100, 8);
+			$alternate = !$alternate;
 		}
 
 	}

+ 7 - 0
src/Data/QRDataModeAbstract.php

@@ -26,8 +26,15 @@ abstract class QRDataModeAbstract implements QRDataModeInterface{
 
 	/**
 	 * QRDataModeAbstract constructor.
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
 	 */
 	public function __construct(string $data){
+
+		if(!$this::validateString($data)){
+			throw new QRCodeDataException('invalid data');
+		}
+
 		$this->data = $data;
 	}
 

+ 3 - 32
tests/Data/AlphaNumTest.php

@@ -10,43 +10,14 @@
 
 namespace chillerlan\QRCodeTest\Data;
 
-use chillerlan\QRCode\Data\{AlphaNum, QRCodeDataException};
+use chillerlan\QRCode\Data\AlphaNum;
 
 /**
  * Tests the AlphaNum class
  */
 final class AlphaNumTest extends DatainterfaceTestAbstract{
 
-	/** @internal */
-	protected array $testdata = [AlphaNum::class, '0 $%*+-./:'];
-
-	/** @internal */
-	protected array  $expected  = [
-		32, 80, 36, 212, 252, 15, 175, 251,
-		176, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		112, 43, 9, 248, 200, 194, 75, 25,
-		205, 173, 154, 68, 191, 16, 128,
-		92, 112, 20, 198, 27
-	];
-
-	/**
-	 * Tests if an exception is thrown when an invalid character is encountered
-	 */
-	public function testGetCharCodeException():void{
-		$this->expectException(QRCodeDataException::class);
-		$this->expectExceptionMessage('illegal char: "#" [35]');
-
-		$this->testdata = [AlphaNum::class, '#'];
-
-		$this->setTestData();
-	}
+	protected string $FQN      = AlphaNum::class;
+	protected string $testdata = '0 $%*+-./:';
 
 }

+ 8 - 18
tests/Data/ByteTest.php

@@ -17,24 +17,14 @@ use chillerlan\QRCode\Data\Byte;
  */
 final class ByteTest extends DatainterfaceTestAbstract{
 
-	/** @internal */
-	protected array $testdata = [Byte::class, '[¯\_(ツ)_/¯]'];
+	protected string $FQN      = Byte::class;
+	protected string $testdata = '[¯\_(ツ)_/¯]';
 
-	/** @internal */
-	protected array  $expected = [
-		64, 245, 188, 42, 245, 197, 242, 142,
-		56, 56, 66, 149, 242, 252, 42, 245,
-		208, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		79, 89, 226, 48, 209, 89, 151, 1,
-		12, 73, 42, 163, 11, 34, 255, 205,
-		21, 47, 250, 101
-	];
+	/**
+	 * @inheritDoc
+	 */
+	public function testInvalidDataException():void{
+		$this->markTestSkipped('N/A');
+	}
 
 }

+ 30 - 42
tests/Data/DatainterfaceTestAbstract.php

@@ -24,28 +24,16 @@ use function str_repeat;
  */
 abstract class DatainterfaceTestAbstract extends TestCase{
 
-	/** @internal */
 	protected ReflectionClass $reflection;
-	/** @internal */
-	protected QRData $dataInterface;
-	/** @internal */
-	protected array $testdata;
-	/** @internal */
-	protected array $expected;
+	protected QRData          $dataInterface;
+	protected string          $FQN;
+	protected string          $testdata;
 
-	/**
-	 * @internal
-	 */
 	protected function setUp():void{
-		$this->dataInterface = new QRData(new QROptions(['version' => 4]), []);
+		$this->dataInterface = new QRData(new QROptions(['version' => 4]));
 		$this->reflection    = new ReflectionClass($this->dataInterface);
 	}
 
-	protected function setTestData():void{
-		[$class, $data] = $this->testdata;
-		$this->dataInterface->setData([new $class($data)]);
-	}
-
 	/**
 	 * Verifies the data interface instance
 	 */
@@ -53,38 +41,21 @@ abstract class DatainterfaceTestAbstract extends TestCase{
 		$this::assertInstanceOf(QRData::class, $this->dataInterface);
 	}
 
-	/**
-	 * Tests ecc masking and verifies against a sample
-	 */
-/*	public function testMaskEcc():void{
-		$this->dataInterface->setData([$this->testdata]);
-
-		$maskECC = $this->reflection->getMethod('maskECC');
-		$maskECC->setAccessible(true);
-
-		$bitBuffer = $this->reflection->getProperty('bitBuffer');
-		$bitBuffer->setAccessible(true);
-		$bb = $bitBuffer->getValue($this->dataInterface);
-
-		$this::assertSame($this->expected, $maskECC->invokeArgs($this->dataInterface, [$bb->getBuffer()]));
-	}*/
-
 	/**
 	 * @see testInitMatrix()
-	 * @internal
 	 * @return int[][]
 	 */
-	public function MaskPatternProvider():array{
+	public function maskPatternProvider():array{
 		return [[0], [1], [2], [3], [4], [5], [6], [7]];
 	}
 
 	/**
 	 * Tests initializing the data matrix
 	 *
-	 * @dataProvider MaskPatternProvider
+	 * @dataProvider maskPatternProvider
 	 */
 	public function testInitMatrix(int $maskPattern):void{
-		$this->setTestData();
+		$this->dataInterface->setData([new $this->FQN($this->testdata)]);
 
 		$matrix = $this->dataInterface->writeMatrix(new MaskPattern($maskPattern));
 
@@ -96,7 +67,7 @@ abstract class DatainterfaceTestAbstract extends TestCase{
 	 * Tests getting the minimum QR version for the given data
 	 */
 	public function testGetMinimumVersion():void{
-		$this->setTestData();
+		$this->dataInterface->setData([new $this->FQN($this->testdata)]);
 
 		$getMinimumVersion = $this->reflection->getMethod('getMinimumVersion');
 		$getMinimumVersion->setAccessible(true);
@@ -110,11 +81,10 @@ abstract class DatainterfaceTestAbstract extends TestCase{
 	public function testGetMinimumVersionException():void{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('data exceeds');
-		[$class, $data] = $this->testdata;
 
 		$this->dataInterface = new QRData(
 			new QROptions(['version' => QRCode::VERSION_AUTO]),
-			[new $class(str_repeat($data, 1337))]
+			[new $this->FQN(str_repeat($this->testdata, 1337))]
 		);
 	}
 
@@ -124,10 +94,28 @@ abstract class DatainterfaceTestAbstract extends TestCase{
 	public function testCodeLengthOverflowException():void{
 		$this->expectException(QRCodeDataException::class);
 		$this->expectExceptionMessage('code length overflow');
-		[$class, $data] = $this->testdata;
-		$this->testdata = [$class, str_repeat($data, 1337)];
 
-		$this->setTestData();
+		$this->dataInterface->setData([new $this->FQN(str_repeat($this->testdata, 1337))]);
+	}
+
+	/**
+	 * Tests if an exception is thrown when an invalid character is encountered
+	 */
+	public function testInvalidDataException():void{
+		$this->expectException(QRCodeDataException::class);
+		$this->expectExceptionMessage('invalid data');
+
+		$this->dataInterface->setData([new $this->FQN('##')]);
+	}
+
+	/**
+	 * Tests if an exception is thrown if the given string is empty
+	 */
+	public function testInvalidDataOnEmptyException():void{
+		$this->expectException(QRCodeDataException::class);
+		$this->expectExceptionMessage('invalid data');
+
+		$this->dataInterface->setData([new $this->FQN('')]);
 	}
 
 }

+ 3 - 44
tests/Data/KanjiTest.php

@@ -10,55 +10,14 @@
 
 namespace chillerlan\QRCodeTest\Data;
 
-use chillerlan\QRCode\Data\{Kanji, QRCodeDataException};
+use chillerlan\QRCode\Data\Kanji;
 
 /**
  * Tests the Kanji class
  */
 final class KanjiTest extends DatainterfaceTestAbstract{
 
-	/** @internal */
-	protected array $testdata = [Kanji::class, '茗荷茗荷茗荷茗荷茗荷'];
-
-	/** @internal */
-	protected array  $expected = [
-		128, 173, 85, 26, 95, 85, 70, 151,
-		213, 81, 165, 245, 84, 105, 125, 85,
-		26, 92, 0, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		195, 11, 221, 91, 141, 220, 163, 46,
-		165, 37, 163, 176, 79, 0, 64, 68,
-		96, 113, 54, 191
-	];
-
-	/**
-	 * Tests if an exception is thrown when an invalid character is encountered
-	 */
-	public function testIllegalCharException1():void{
-		$this->expectException(QRCodeDataException::class);
-		$this->expectExceptionMessage('illegal char at 1 [16191]');
-
-		$this->testdata = [Kanji::class, 'ÃÃ'];
-
-		$this->setTestData();
-	}
-
-	/**
-	 * Tests if an exception is thrown when an invalid character is encountered
-	 */
-	public function testIllegalCharException2():void{
-		$this->expectException(QRCodeDataException::class);
-		$this->expectExceptionMessage('illegal char at 1');
-
-		$this->testdata = [Kanji::class, 'Ã'];
-
-		$this->setTestData();
-	}
+	protected string $FQN      = Kanji::class;
+	protected string $testdata = '茗荷茗荷茗荷茗荷茗荷';
 
 }

+ 3 - 32
tests/Data/NumberTest.php

@@ -10,43 +10,14 @@
 
 namespace chillerlan\QRCodeTest\Data;
 
-use chillerlan\QRCode\Data\{Number, QRCodeDataException};
+use chillerlan\QRCode\Data\Number;
 
 /**
  * Tests the Number class
  */
 final class NumberTest extends DatainterfaceTestAbstract{
 
-	/** @internal */
-	protected array $testdata  = [Number::class, '0123456789'];
-
-	/** @internal */
-	protected array $expected = [
-		16, 40, 12, 86, 106, 105, 0, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		17, 236, 17, 236, 17, 236, 17, 236,
-		201, 141, 102, 116, 238, 162, 239, 230,
-		222, 37, 79, 192, 42, 109, 188, 72,
-		89, 63, 168, 151
-	];
-
-	/**
-	 * Tests if an exception is thrown when an invalid character is encountered
-	 */
-	public function testGetCharCodeException():void{
-		$this->expectException(QRCodeDataException::class);
-		$this->expectExceptionMessage('illegal char: "#" [35]');
-
-		$this->testdata = [Number::class, '#'];
-
-		$this->setTestData();
-	}
+	protected string $FQN      = Number::class;
+	protected string $testdata = '0123456789';
 
 }

+ 2 - 5
tests/QRCodeReaderTest.php

@@ -106,12 +106,9 @@ class QRCodeReaderTest extends TestCase{
 
 			foreach([EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H] as $ecc){
 				$eccLevel = new EccLevel($ecc);
+				$expected = substr($str, 0, $version->getMaxLengthForMode(Mode::BYTE, $eccLevel) ?? '');
 
-				yield 'version: '.$version.$eccLevel => [
-					$version,
-					$eccLevel,
-					substr($str, 0, $version->getMaxLengthForMode(Mode::BYTE, $eccLevel) ?? '')
-				];
+				yield 'version: '.$version.$eccLevel => [$version, $eccLevel, $expected];
 			}
 		}