ECITest.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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, Mode};
  13. use chillerlan\QRCode\Data\{Byte, ECI, Number, QRCodeDataException, QRData, QRDataModeInterface};
  14. use PHPUnit\Framework\TestCase;
  15. /**
  16. * Tests the ECI class
  17. */
  18. final class ECITest extends TestCase{
  19. protected QRData $QRData;
  20. protected string $testdata = '无可奈何燃花作香';
  21. private int $testCharset = ECICharset::GB18030;
  22. protected function setUp():void{
  23. $this->QRData = new QRData(new QROptions);
  24. }
  25. private function getDataSegments():array{
  26. return [
  27. new ECI($this->testCharset),
  28. new Byte(mb_convert_encoding($this->testdata, ECICharset::MB_ENCODINGS[$this->testCharset], mb_internal_encoding())),
  29. ];
  30. }
  31. /** @inheritDoc */
  32. public function testDataModeInstance():void{
  33. $datamode = new ECI($this->testCharset);
  34. $this::assertInstanceOf(QRDataModeInterface::class, $datamode);
  35. }
  36. /**
  37. * returns versions within the version breakpoints 1-9, 10-26 and 27-40
  38. */
  39. public static function versionBreakpointProvider():array{
  40. return ['1-9' => [7], '10-26' => [15], '27-40' => [30]];
  41. }
  42. /**
  43. * @inheritDoc
  44. * @dataProvider versionBreakpointProvider
  45. */
  46. public function testDecodeSegment(int $version):void{
  47. $options = new QROptions;
  48. $options->version = $version;
  49. /** @var \chillerlan\QRCode\Data\QRDataModeInterface[] $segments */
  50. $segments = $this->getDataSegments();
  51. // invoke a QRData instance and write data
  52. $this->QRData = new QRData($options, $segments);
  53. // get the filled bitbuffer
  54. $bitBuffer = $this->QRData->getBitBuffer();
  55. // read the first 4 bits
  56. $this::assertSame($segments[0]::DATAMODE, $bitBuffer->read(4));
  57. // decode the data
  58. $this::assertSame($this->testdata, ECI::decodeSegment($bitBuffer, $options->version));
  59. }
  60. /** @inheritDoc */
  61. public function testInvalidDataException():void{
  62. $this->expectException(QRCodeDataException::class);
  63. $this->expectExceptionMessage('invalid encoding id:');
  64. /** @phan-suppress-next-line PhanNoopNew */
  65. new ECI(-1);
  66. }
  67. /**
  68. * since the ECI class only accepts integer values,
  69. * we'll use this test to check for the upper end of the accepted input range
  70. *
  71. * @inheritDoc
  72. */
  73. public function testInvalidDataOnEmptyException():void{
  74. $this->expectException(QRCodeDataException::class);
  75. $this->expectExceptionMessage('invalid encoding id:');
  76. /** @phan-suppress-next-line PhanNoopNew */
  77. new ECI(1000000);
  78. }
  79. public static function eciCharsetIdProvider():array{
  80. return [
  81. [ 0, 8],
  82. [ 127, 8],
  83. [ 128, 16],
  84. [ 16383, 16],
  85. [ 16384, 24],
  86. [999999, 24],
  87. ];
  88. }
  89. /**
  90. * @dataProvider eciCharsetIdProvider
  91. */
  92. public function testReadWrite(int $id, int $lengthInBits):void{
  93. $bitBuffer = new BitBuffer;
  94. $eci = (new ECI($id))->write($bitBuffer, 1);
  95. $this::assertSame($lengthInBits, $eci->getLengthInBits());
  96. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  97. $this::assertSame($id, ECI::parseValue($bitBuffer)->getID());
  98. }
  99. /**
  100. * Tests if and exception is thrown when the ECI segment is followed by a mode that is not 8-bit byte
  101. */
  102. public function testDecodeECISegmentFollowedByInvalidModeException():void{
  103. $this->expectException(QRCodeDataException::class);
  104. $this->expectExceptionMessage('ECI designator followed by invalid mode:');
  105. $options = new QROptions;
  106. $options->version = 5;
  107. /** @var \chillerlan\QRCode\Data\QRDataModeInterface[] $segments */
  108. $segments = $this->getDataSegments();
  109. // follow the ECI segment by a non-8bit-byte segment
  110. $segments[1] = new Number('1');
  111. $bitBuffer = (new QRData($options, $segments))->getBitBuffer();
  112. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  113. ECI::decodeSegment($bitBuffer, $options->version);
  114. }
  115. public function unknownEncodingDataProvider():array{
  116. return [
  117. 'CP437' => [0, "\x41\x42\x43"],
  118. 'ISO_IEC_8859_1_GLI' => [1, "\x41\x42\x43"],
  119. ];
  120. }
  121. /**
  122. * Tests detection of an unknown character set
  123. *
  124. * @dataProvider unknownEncodingDataProvider
  125. */
  126. public function testConvertUnknownEncoding(int $id, string $data):void{
  127. $options = new QROptions;
  128. $options->version = 5;
  129. $segments = [new ECI($id), new Byte($data)];
  130. $bitBuffer = (new QRData($options, $segments))->getBitBuffer();
  131. $this::assertSame(Mode::ECI, $bitBuffer->read(4));
  132. $this::assertSame($data, ECI::decodeSegment($bitBuffer, $options->version));
  133. }
  134. }