ECITest.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. /**
  3. * ECITest.php
  4. *
  5. * @created 12.03.2023
  6. * @author smiley <smiley@chillerlan.net>
  7. * @copyright 2023 smiley
  8. * @license MIT
  9. */
  10. namespace chillerlan\QRCodeTest\Data;
  11. use chillerlan\QRCode\QROptions;
  12. use chillerlan\QRCode\Common\{BitBuffer, ECICharset, MaskPattern, Mode};
  13. use chillerlan\QRCode\Data\{Byte, ECI, Number, QRCodeDataException, QRData, QRDataModeInterface, QRMatrix};
  14. /**
  15. * Tests the ECI class
  16. */
  17. final class ECITest extends DataInterfaceTestAbstract{
  18. protected string $FQN = ECI::class;
  19. protected string $testdata = '无可奈何燃花作香';
  20. private int $testCharset = ECICharset::GB18030;
  21. private function getDataSegments():array{
  22. return [
  23. new $this->FQN($this->testCharset),
  24. new Byte(mb_convert_encoding($this->testdata, ECICharset::MB_ENCODINGS[$this->testCharset], mb_internal_encoding())),
  25. ];
  26. }
  27. public static function stringValidateProvider():array{
  28. return [];
  29. }
  30. /** @inheritDoc */
  31. public function testDataModeInstance():void{
  32. $datamode = new $this->FQN($this->testCharset);
  33. $this::assertInstanceOf(QRDataModeInterface::class, $datamode);
  34. }
  35. /**
  36. * @inheritDoc
  37. * @dataProvider maskPatternProvider
  38. */
  39. public function testInitMatrix(int $maskPattern):void{
  40. $segments = $this->getDataSegments();
  41. $this->QRData->setData($segments);
  42. $matrix = $this->QRData->writeMatrix(new MaskPattern($maskPattern));
  43. $this::assertInstanceOf(QRMatrix::class, $matrix);
  44. $this::assertSame($maskPattern, $matrix->maskPattern()->getPattern());
  45. }
  46. /** @inheritDoc */
  47. public function testGetMinimumVersion():void{
  48. /** @noinspection PhpUnitTestFailedLineInspection */
  49. $this::markTestSkipped('N/A (ECI mode)');
  50. }
  51. /** @inheritDoc */
  52. public function testBinaryStringInvalid():void{
  53. /** @noinspection PhpUnitTestFailedLineInspection */
  54. $this::markTestSkipped('N/A (ECI mode)');
  55. }
  56. /**
  57. * @inheritDoc
  58. * @dataProvider versionBreakpointProvider
  59. */
  60. public function testDecodeSegment(int $version):void{
  61. $options = new QROptions;
  62. $options->version = $version;
  63. /** @var \chillerlan\QRCode\Data\QRDataModeInterface[] $segments */
  64. $segments = $this->getDataSegments();
  65. // invoke a QRData instance and write data
  66. $this->QRData = new QRData($options, $segments);
  67. // get the filled bitbuffer
  68. $bitBuffer = $this->QRData->getBitBuffer();
  69. // read the first 4 bits
  70. $this::assertSame($segments[0]->getDataMode(), $bitBuffer->read(4));
  71. // decode the data
  72. /** @noinspection PhpUndefinedMethodInspection */
  73. $this::assertSame($this->testdata, $this->FQN::decodeSegment($bitBuffer, $options->version));
  74. }
  75. /** @inheritDoc */
  76. public function testGetMinimumVersionException():void{
  77. /** @noinspection PhpUnitTestFailedLineInspection */
  78. $this::markTestSkipped('N/A (ECI mode)');
  79. }
  80. /** @inheritDoc */
  81. public function testCodeLengthOverflowException():void{
  82. /** @noinspection PhpUnitTestFailedLineInspection */
  83. $this::markTestSkipped('N/A (ECI mode)');
  84. }
  85. /** @inheritDoc */
  86. public function testInvalidDataException():void{
  87. $this->expectException(QRCodeDataException::class);
  88. $this->expectExceptionMessage('invalid encoding id:');
  89. /** @phan-suppress-next-line PhanNoopNew */
  90. new $this->FQN(-1);
  91. }
  92. /**
  93. * since the ECI class only accepts integer values,
  94. * we'll use this test to check for the upper end of the accepted input range
  95. *
  96. * @inheritDoc
  97. */
  98. public function testInvalidDataOnEmptyException():void{
  99. $this->expectException(QRCodeDataException::class);
  100. $this->expectExceptionMessage('invalid encoding id:');
  101. /** @phan-suppress-next-line PhanNoopNew */
  102. new $this->FQN(1000000);
  103. }
  104. public static function eciCharsetIdProvider():array{
  105. return [
  106. [ 0, 8],
  107. [ 127, 8],
  108. [ 128, 16],
  109. [ 16383, 16],
  110. [ 16384, 24],
  111. [999999, 24],
  112. ];
  113. }
  114. /**
  115. * @dataProvider eciCharsetIdProvider
  116. */
  117. public function testReadWrite(int $id, int $lengthInBits):void{
  118. $bitBuffer = new BitBuffer;
  119. $eci = (new $this->FQN($id))->write($bitBuffer, 1);
  120. $this::assertSame($lengthInBits, $eci->getLengthInBits());
  121. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  122. /** @noinspection PhpUndefinedMethodInspection */
  123. $this::assertSame($id, $this->FQN::parseValue($bitBuffer)->getID());
  124. }
  125. /**
  126. * Tests if and exception is thrown when the ECI segment is followed by a mode that is not 8-bit byte
  127. */
  128. public function testDecodeECISegmentFollowedByInvalidModeException():void{
  129. $this->expectException(QRCodeDataException::class);
  130. $this->expectExceptionMessage('ECI designator followed by invalid mode:');
  131. $options = new QROptions;
  132. $options->version = 5;
  133. /** @var \chillerlan\QRCode\Data\QRDataModeInterface[] $segments */
  134. $segments = $this->getDataSegments();
  135. // follow the ECI segment by a non-8bit-byte segment
  136. $segments[1] = new Number('1');
  137. $bitBuffer = (new QRData($options, $segments))->getBitBuffer();
  138. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  139. /** @noinspection PhpUndefinedMethodInspection */
  140. $this->FQN::decodeSegment($bitBuffer, $options->version);
  141. }
  142. public function unknownEncodingDataProvider():array{
  143. return [
  144. 'CP437' => [0, "\x41\x42\x43"],
  145. 'ISO_IEC_8859_1_GLI' => [1, "\x41\x42\x43"],
  146. ];
  147. }
  148. /**
  149. * Tests detection of an unknown character set
  150. *
  151. * @dataProvider unknownEncodingDataProvider
  152. */
  153. public function testConvertUnknownEncoding(int $id, string $data):void{
  154. $options = new QROptions;
  155. $options->version = 5;
  156. $segments = [new $this->FQN($id), new Byte($data)];
  157. $bitBuffer = (new QRData($options, $segments))->getBitBuffer();
  158. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  159. /** @noinspection PhpUndefinedMethodInspection */
  160. $this::assertSame($data, $this->FQN::decodeSegment($bitBuffer, $options->version));
  161. }
  162. }