ExporterTest.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of exporter package.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace SebastianBergmann\Exporter;
  11. use PHPUnit\Framework\TestCase;
  12. /**
  13. * @covers SebastianBergmann\Exporter\Exporter
  14. */
  15. class ExporterTest extends TestCase
  16. {
  17. /**
  18. * @var Exporter
  19. */
  20. private $exporter;
  21. protected function setUp()
  22. {
  23. $this->exporter = new Exporter;
  24. }
  25. public function exportProvider()
  26. {
  27. $obj2 = new \stdClass;
  28. $obj2->foo = 'bar';
  29. $obj3 = (object) [1, 2, "Test\r\n", 4, 5, 6, 7, 8];
  30. $obj = new \stdClass;
  31. //@codingStandardsIgnoreStart
  32. $obj->null = null;
  33. //@codingStandardsIgnoreEnd
  34. $obj->boolean = true;
  35. $obj->integer = 1;
  36. $obj->double = 1.2;
  37. $obj->string = '1';
  38. $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext";
  39. $obj->object = $obj2;
  40. $obj->objectagain = $obj2;
  41. $obj->array = ['foo' => 'bar'];
  42. $obj->self = $obj;
  43. $storage = new \SplObjectStorage;
  44. $storage->attach($obj2);
  45. $storage->foo = $obj2;
  46. return [
  47. 'export null' => [null, 'null'],
  48. 'export boolean true' => [true, 'true'],
  49. 'export boolean false' => [false, 'false'],
  50. 'export int 1' => [1, '1'],
  51. 'export float 1.0' => [1.0, '1.0'],
  52. 'export float 1.2' => [1.2, '1.2'],
  53. 'export stream' => [\fopen('php://memory', 'r'), 'resource(%d) of type (stream)'],
  54. 'export numeric string' => ['1', "'1'"],
  55. 'export multidimentional array' => [[[1, 2, 3], [3, 4, 5]],
  56. <<<EOF
  57. Array &0 (
  58. 0 => Array &1 (
  59. 0 => 1
  60. 1 => 2
  61. 2 => 3
  62. )
  63. 1 => Array &2 (
  64. 0 => 3
  65. 1 => 4
  66. 2 => 5
  67. )
  68. )
  69. EOF
  70. ],
  71. // \n\r and \r is converted to \n
  72. 'export multiline text' => ["this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext",
  73. <<<EOF
  74. 'this\\n
  75. is\\n
  76. a\\n
  77. very\\n
  78. very\\n
  79. very\\n
  80. very\\n
  81. very\\n
  82. very\\r
  83. long\\n\\r
  84. text'
  85. EOF
  86. ],
  87. 'export empty stdclass' => [new \stdClass, 'stdClass Object &%x ()'],
  88. 'export non empty stdclass' => [$obj,
  89. <<<EOF
  90. stdClass Object &%x (
  91. 'null' => null
  92. 'boolean' => true
  93. 'integer' => 1
  94. 'double' => 1.2
  95. 'string' => '1'
  96. 'text' => 'this\\n
  97. is\\n
  98. a\\n
  99. very\\n
  100. very\\n
  101. very\\n
  102. very\\n
  103. very\\n
  104. very\\r
  105. long\\n\\r
  106. text'
  107. 'object' => stdClass Object &%x (
  108. 'foo' => 'bar'
  109. )
  110. 'objectagain' => stdClass Object &%x
  111. 'array' => Array &%d (
  112. 'foo' => 'bar'
  113. )
  114. 'self' => stdClass Object &%x
  115. )
  116. EOF
  117. ],
  118. 'export empty array' => [[], 'Array &%d ()'],
  119. 'export splObjectStorage' => [$storage,
  120. <<<EOF
  121. SplObjectStorage Object &%x (
  122. 'foo' => stdClass Object &%x (
  123. 'foo' => 'bar'
  124. )
  125. '%x' => Array &0 (
  126. 'obj' => stdClass Object &%x
  127. 'inf' => null
  128. )
  129. )
  130. EOF
  131. ],
  132. 'export stdClass with numeric properties' => [$obj3,
  133. <<<EOF
  134. stdClass Object &%x (
  135. 0 => 1
  136. 1 => 2
  137. 2 => 'Test\\r\\n
  138. '
  139. 3 => 4
  140. 4 => 5
  141. 5 => 6
  142. 6 => 7
  143. 7 => 8
  144. )
  145. EOF
  146. ],
  147. [
  148. \chr(0) . \chr(1) . \chr(2) . \chr(3) . \chr(4) . \chr(5),
  149. 'Binary String: 0x000102030405'
  150. ],
  151. [
  152. \implode('', \array_map('chr', \range(0x0e, 0x1f))),
  153. 'Binary String: 0x0e0f101112131415161718191a1b1c1d1e1f'
  154. ],
  155. [
  156. \chr(0x00) . \chr(0x09),
  157. 'Binary String: 0x0009'
  158. ],
  159. [
  160. '',
  161. "''"
  162. ],
  163. 'export Exception without trace' => [
  164. new \Exception('The exception message', 42),
  165. <<<EOF
  166. Exception Object &%x (
  167. 'message' => 'The exception message'
  168. 'string' => ''
  169. 'code' => 42
  170. 'file' => '%s/tests/ExporterTest.php'
  171. 'line' => %d
  172. 'previous' => null
  173. )
  174. EOF
  175. ],
  176. 'export Error without trace' => [
  177. new \Error('The exception message', 42),
  178. <<<EOF
  179. Error Object &%x (
  180. 'message' => 'The exception message'
  181. 'string' => ''
  182. 'code' => 42
  183. 'file' => '%s/tests/ExporterTest.php'
  184. 'line' => %d
  185. 'previous' => null
  186. )
  187. EOF
  188. ],
  189. ];
  190. }
  191. /**
  192. * @dataProvider exportProvider
  193. */
  194. public function testExport($value, $expected)
  195. {
  196. $this->assertStringMatchesFormat(
  197. $expected,
  198. $this->trimNewline($this->exporter->export($value))
  199. );
  200. }
  201. public function testExport2()
  202. {
  203. if (\PHP_VERSION === '5.3.3') {
  204. $this->markTestSkipped('Skipped due to "Nesting level too deep - recursive dependency?" fatal error');
  205. }
  206. $obj = new \stdClass;
  207. $obj->foo = 'bar';
  208. $array = [
  209. 0 => 0,
  210. 'null' => null,
  211. 'boolean' => true,
  212. 'integer' => 1,
  213. 'double' => 1.2,
  214. 'string' => '1',
  215. 'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext",
  216. 'object' => $obj,
  217. 'objectagain' => $obj,
  218. 'array' => ['foo' => 'bar'],
  219. ];
  220. $array['self'] = &$array;
  221. $expected = <<<EOF
  222. Array &%d (
  223. 0 => 0
  224. 'null' => null
  225. 'boolean' => true
  226. 'integer' => 1
  227. 'double' => 1.2
  228. 'string' => '1'
  229. 'text' => 'this\\n
  230. is\\n
  231. a\\n
  232. very\\n
  233. very\\n
  234. very\\n
  235. very\\n
  236. very\\n
  237. very\\r
  238. long\\n\\r
  239. text'
  240. 'object' => stdClass Object &%x (
  241. 'foo' => 'bar'
  242. )
  243. 'objectagain' => stdClass Object &%x
  244. 'array' => Array &%d (
  245. 'foo' => 'bar'
  246. )
  247. 'self' => Array &%d (
  248. 0 => 0
  249. 'null' => null
  250. 'boolean' => true
  251. 'integer' => 1
  252. 'double' => 1.2
  253. 'string' => '1'
  254. 'text' => 'this\\n
  255. is\\n
  256. a\\n
  257. very\\n
  258. very\\n
  259. very\\n
  260. very\\n
  261. very\\n
  262. very\\r
  263. long\\n\\r
  264. text'
  265. 'object' => stdClass Object &%x
  266. 'objectagain' => stdClass Object &%x
  267. 'array' => Array &%d (
  268. 'foo' => 'bar'
  269. )
  270. 'self' => Array &%d
  271. )
  272. )
  273. EOF;
  274. $this->assertStringMatchesFormat(
  275. $expected,
  276. $this->trimNewline($this->exporter->export($array))
  277. );
  278. }
  279. public function shortenedExportProvider()
  280. {
  281. $obj = new \stdClass;
  282. $obj->foo = 'bar';
  283. $array = [
  284. 'foo' => 'bar',
  285. ];
  286. return [
  287. 'shortened export null' => [null, 'null'],
  288. 'shortened export boolean true' => [true, 'true'],
  289. 'shortened export integer 1' => [1, '1'],
  290. 'shortened export float 1.0' => [1.0, '1.0'],
  291. 'shortened export float 1.2' => [1.2, '1.2'],
  292. 'shortened export numeric string' => ['1', "'1'"],
  293. // \n\r and \r is converted to \n
  294. 'shortened export multilinestring' => ["this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", "'this\\nis\\na\\nvery\\nvery\\nvery...\\rtext'"],
  295. 'shortened export empty stdClass' => [new \stdClass, 'stdClass Object ()'],
  296. 'shortened export not empty stdClass' => [$obj, 'stdClass Object (...)'],
  297. 'shortened export empty array' => [[], 'Array ()'],
  298. 'shortened export not empty array' => [$array, 'Array (...)'],
  299. ];
  300. }
  301. /**
  302. * @dataProvider shortenedExportProvider
  303. */
  304. public function testShortenedExport($value, $expected)
  305. {
  306. $this->assertSame(
  307. $expected,
  308. $this->trimNewline($this->exporter->shortenedExport($value))
  309. );
  310. }
  311. /**
  312. * @requires extension mbstring
  313. */
  314. public function testShortenedExportForMultibyteCharacters()
  315. {
  316. $oldMbLanguage = \mb_language();
  317. \mb_language('Japanese');
  318. $oldMbInternalEncoding = \mb_internal_encoding();
  319. \mb_internal_encoding('UTF-8');
  320. try {
  321. $this->assertSame(
  322. "'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくや...しゑひもせす'",
  323. $this->trimNewline($this->exporter->shortenedExport('いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせす'))
  324. );
  325. } catch (\Exception $e) {
  326. \mb_internal_encoding($oldMbInternalEncoding);
  327. \mb_language($oldMbLanguage);
  328. throw $e;
  329. }
  330. \mb_internal_encoding($oldMbInternalEncoding);
  331. \mb_language($oldMbLanguage);
  332. }
  333. public function provideNonBinaryMultibyteStrings()
  334. {
  335. return [
  336. [\implode('', \array_map('chr', \range(0x09, 0x0d))), 9],
  337. [\implode('', \array_map('chr', \range(0x20, 0x7f))), 96],
  338. [\implode('', \array_map('chr', \range(0x80, 0xff))), 128],
  339. ];
  340. }
  341. /**
  342. * @dataProvider provideNonBinaryMultibyteStrings
  343. */
  344. public function testNonBinaryStringExport($value, $expectedLength)
  345. {
  346. $this->assertRegExp(
  347. "~'.{{$expectedLength}}'\$~s",
  348. $this->exporter->export($value)
  349. );
  350. }
  351. public function testNonObjectCanBeReturnedAsArray()
  352. {
  353. $this->assertEquals([true], $this->exporter->toArray(true));
  354. }
  355. private function trimNewline($string)
  356. {
  357. return \preg_replace('/[ ]*\n/', "\n", $string);
  358. }
  359. }