ParserTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <?php
  2. declare(strict_types=1);
  3. namespace JsonMachineTest;
  4. use JsonMachine\Exception\JsonMachineException;
  5. use JsonMachine\Exception\PathNotFoundException;
  6. use JsonMachine\Exception\SyntaxErrorException;
  7. use JsonMachine\Exception\UnexpectedEndSyntaxErrorException;
  8. use JsonMachine\JsonDecoder\ExtJsonDecoder;
  9. use JsonMachine\Parser;
  10. use JsonMachine\StringChunks;
  11. use JsonMachine\Tokens;
  12. use JsonMachine\TokensWithDebugging;
  13. /**
  14. * @covers \JsonMachine\Parser
  15. */
  16. class ParserTest extends \PHPUnit_Framework_TestCase
  17. {
  18. /**
  19. * @dataProvider data_testSyntax
  20. *
  21. * @param string $jsonPointer
  22. * @param string $json
  23. * @param array $expectedResult
  24. */
  25. public function testSyntax($jsonPointer, $json, $expectedResult)
  26. {
  27. $result = [];
  28. foreach ($this->createParser($json, $jsonPointer) as $key => $value) {
  29. $result[] = [$key => $value];
  30. }
  31. $this->assertSame($expectedResult, $result);
  32. }
  33. public function data_testSyntax()
  34. {
  35. return [
  36. ['', '{}', []],
  37. ['', '{"a": "b"}', [['a' => 'b']]],
  38. ['', '{"a":{"b":{"c":1}}}', [['a' => ['b' => ['c' => 1]]]]],
  39. ['', '[]', []],
  40. ['', '[null,true,false,"a",0,1,42.5]', [[0 => null], [1 => true], [2 => false], [3 => 'a'], [4 => 0], [5 => 1], [6 => 42.5]]],
  41. ['', '[{"c":1}]', [[['c' => 1]]]],
  42. ['', '[{"c":1},"string",{"d":2},false]', [[0 => ['c' => 1]], [1 => 'string'], [2 => ['d' => 2]], [3 => false]]],
  43. ['', '[false,{"c":1},"string",{"d":2}]', [[0 => false], [1 => ['c' => 1]], [2 => 'string'], [3 => ['d' => 2]]]],
  44. ['', '[{"c":1,"d":2}]', [[['c' => 1, 'd' => 2]]]],
  45. 'ISSUE-108' => [
  46. '',
  47. '["https://click.justwatch.com/a?cx=eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uanVzdHdhdGNoL2NsaWNrb3V0X2NvbnRleHQvanNvbnNjaGVtYS8xLTItMCIsImRhdGEiOnsicHJvdmlkZXIiOiJBcHBsZSBUViIsIm1vbmV0aXphdGlvblR5cGUiOiJidXkiLCJwcmVzZW50YXRpb25UeXBlIjoiaGQiLCJjdXJyZW5jeSI6IlVTRCIsInByaWNlIjo1MTkuNzQsIm9yaWdpbmFsUHJpY2UiOjAsImF1ZGlvTGFuZ3VhZ2UiOiIiLCJzdWJ0aXRsZUxhbmd1YWdlIjoiIiwiY2luZW1hSWQiOjAsInNob3d0aW1lIjoiIiwiaXNGYXZvcml0ZUNpbmVtYSI6ZmFsc2UsInBhcnRuZXJJZCI6MTI3MCwicHJvdmlkZXJJZCI6MiwiY2xpY2tvdXRUeXBlIjoianctY29udGVudC1wYXJ0bmVyLWFwaSJ9fSx7InNjaGVtYSI6ImlnbHU6Y29tLmp1c3R3YXRjaC90aXRsZV9jb250ZXh0L2pzb25zY2hlbWEvMS0wLTAiLCJkYXRhIjp7InRpdGxlSWQiOjIwOTgxLCJvYmplY3RUeXBlIjoic2hvdyIsImp3RW50aXR5SWQiOiJ0czIwOTgxIn19XX0\u0026r=https%3A%2F%2Ftv.apple.com%2Fus%2Fshow%2Fsurvivor%2Fumc.cmc.6ozd0mt09a86bpa19l885jv4z\u0026uct_country=us"]',
  48. [['https://click.justwatch.com/a?cx=eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uanVzdHdhdGNoL2NsaWNrb3V0X2NvbnRleHQvanNvbnNjaGVtYS8xLTItMCIsImRhdGEiOnsicHJvdmlkZXIiOiJBcHBsZSBUViIsIm1vbmV0aXphdGlvblR5cGUiOiJidXkiLCJwcmVzZW50YXRpb25UeXBlIjoiaGQiLCJjdXJyZW5jeSI6IlVTRCIsInByaWNlIjo1MTkuNzQsIm9yaWdpbmFsUHJpY2UiOjAsImF1ZGlvTGFuZ3VhZ2UiOiIiLCJzdWJ0aXRsZUxhbmd1YWdlIjoiIiwiY2luZW1hSWQiOjAsInNob3d0aW1lIjoiIiwiaXNGYXZvcml0ZUNpbmVtYSI6ZmFsc2UsInBhcnRuZXJJZCI6MTI3MCwicHJvdmlkZXJJZCI6MiwiY2xpY2tvdXRUeXBlIjoianctY29udGVudC1wYXJ0bmVyLWFwaSJ9fSx7InNjaGVtYSI6ImlnbHU6Y29tLmp1c3R3YXRjaC90aXRsZV9jb250ZXh0L2pzb25zY2hlbWEvMS0wLTAiLCJkYXRhIjp7InRpdGxlSWQiOjIwOTgxLCJvYmplY3RUeXBlIjoic2hvdyIsImp3RW50aXR5SWQiOiJ0czIwOTgxIn19XX0&r=https%3A%2F%2Ftv.apple.com%2Fus%2Fshow%2Fsurvivor%2Fumc.cmc.6ozd0mt09a86bpa19l885jv4z&uct_country=us']],
  49. ],
  50. ['/', '{"":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  51. ['/~0', '{"~":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  52. ['/~1', '{"/":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  53. ['/~01', '{"~1":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  54. ['/~00', '{"~0":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  55. ['/path', '{"path":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  56. ['/path', '{"no":[null], "path":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
  57. ['/0', '[{"c":1,"d":2}, [null]]', [['c' => 1], ['d' => 2]]],
  58. ['/0/path', '[{"path":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
  59. ['/1/path', '[[null], {"path":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
  60. ['/path/0', '{"path":[{"c":1,"d":2}, [null]]}', [['c' => 1], ['d' => 2]]],
  61. ['/path/1', '{"path":[null,{"c":1,"d":2}, [null]]}', [['c' => 1], ['d' => 2]]],
  62. ['/path/to', '{"path":{"to":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
  63. ['/path/after-vector', '{"path":{"array":[],"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
  64. ['/path/after-vector', '{"path":{"array":["item"],"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
  65. ['/path/after-vector', '{"path":{"object":{"item":null},"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
  66. ['/path/after-vectors', '{"path":{"array":[],"object":{},"after-vectors":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
  67. ['/0/0', '[{"0":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
  68. ['/1/1', '[0,{"1":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
  69. 'PR-19-FIX' => ['/datafeed/programs/1', file_get_contents(__DIR__.'/PR-19-FIX.json'), [['program_info' => ['id' => 'X1']]]],
  70. 'ISSUE-41-FIX' => ['/path', '{"path":[{"empty":{}},{"value":1}]}', [[['empty' => []]], [1 => ['value' => 1]]]],
  71. ['/-', '[{"one": 1,"two": 2},{"three": 3,"four": 4}]', [['one' => 1], ['two' => 2], ['three' => 3], ['four' => 4]]],
  72. ['/zero/-', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['one' => 1], ['two' => 2], ['three' => 3], ['four' => 4]]],
  73. ['/zero/-/three', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['three' => 3]]],
  74. 'ISSUE-62#1' => ['/-/id', '[ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]', [['id' => 125], ['id' => 785], ['id' => 459], ['id' => 853]]],
  75. 'ISSUE-62#2' => ['/key/-/id', '{"key": [ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]}', [['id' => 125], ['id' => 785], ['id' => 459], ['id' => 853]]],
  76. [
  77. ['/meta_data', '/data/companies'],
  78. '{"meta_data": {"total_rows": 2},"data": {"type": "companies","companies": [{"id": "1","company": "Company 1"},{"id": "2","company": "Company 2"}]}}',
  79. [
  80. ['total_rows' => 2],
  81. ['0' => ['id' => '1', 'company' => 'Company 1']],
  82. ['1' => ['id' => '2', 'company' => 'Company 2']],
  83. ],
  84. ],
  85. [
  86. ['/-/id', '/-/company'],
  87. '[{"id": "1","company": "Company 1"},{"id": "2","company": "Company 2"}]',
  88. [
  89. ['id' => '1'],
  90. ['company' => 'Company 1'],
  91. ['id' => '2'],
  92. ['company' => 'Company 2'],
  93. ],
  94. ],
  95. [
  96. ['/-/id', '/0/company'],
  97. '[{"id": "1","company": "Company 1"},{"id": "2","company": "Company 2"}]',
  98. [
  99. ['id' => '1'],
  100. ['company' => 'Company 1'],
  101. ['id' => '2'],
  102. ],
  103. ],
  104. ];
  105. }
  106. /**
  107. * @dataProvider data_testThrowsOnNotFoundJsonPointer
  108. *
  109. * @param string $json
  110. * @param string $jsonPointer
  111. */
  112. public function testThrowsOnNotFoundJsonPointer($json, $jsonPointer)
  113. {
  114. $parser = $this->createParser($json, $jsonPointer);
  115. $this->expectException(PathNotFoundException::class);
  116. $this->expectExceptionMessage("Paths '".implode(', ', (array) $jsonPointer)."' were not found in json stream.");
  117. iterator_to_array($parser);
  118. }
  119. public function data_testThrowsOnNotFoundJsonPointer()
  120. {
  121. return [
  122. 'non existing pointer' => ['{}', '/not/found'],
  123. "empty string should not match '0'" => ['{"0":[]}', '/'],
  124. 'empty string should not match 0 index' => ['[[]]', '/'],
  125. '0 should not match empty string' => ['{"":[]}', '/0'],
  126. ];
  127. }
  128. /**
  129. * @dataProvider data_testSyntaxError
  130. *
  131. * @param string $malformedJson
  132. */
  133. public function testSyntaxError($malformedJson)
  134. {
  135. $this->expectException(SyntaxErrorException::class);
  136. iterator_to_array($this->createParser($malformedJson));
  137. }
  138. public function data_testSyntaxError()
  139. {
  140. return [
  141. ['[}'],
  142. ['{]'],
  143. ['null'],
  144. ['true'],
  145. ['false'],
  146. ['0'],
  147. ['100'],
  148. ['"string"'],
  149. ['}'],
  150. [']'],
  151. [','],
  152. [':'],
  153. [''],
  154. ['[null null]'],
  155. ['["string" "string"]'],
  156. ['[,"string","string"]'],
  157. ['["string",,"string"]'],
  158. ['["string","string",]'],
  159. ['["string",1eeee1]'],
  160. ['{"key\u000Z": "non hex key"}'],
  161. ];
  162. }
  163. /**
  164. * @dataProvider data_testUnexpectedEndError
  165. *
  166. * @param string $malformedJson
  167. */
  168. public function testUnexpectedEndError($malformedJson)
  169. {
  170. $this->expectException(UnexpectedEndSyntaxErrorException::class);
  171. iterator_to_array($this->createParser($malformedJson));
  172. }
  173. public function data_testUnexpectedEndError()
  174. {
  175. return [
  176. ['['],
  177. ['{'],
  178. ['["string"'],
  179. ['["string",'],
  180. ['[{"string":"string"}'],
  181. ['[{"string":"string"},'],
  182. ['[{"string":"string"},{'],
  183. ['[{"string":"string"},{"str'],
  184. ['[{"string":"string"},{"string"'],
  185. ['{"string"'],
  186. ['{"string":'],
  187. ['{"string":"string"'],
  188. ['{"string":["string","string"]'],
  189. ['{"string":["string","string"'],
  190. ['{"string":["string","string",'],
  191. ['{"string":["string","string","str'],
  192. ];
  193. }
  194. public function testGeneratorQuitsAfterFirstFoundCollectionHasBeenFinished()
  195. {
  196. $json = '
  197. {
  198. "results": [1],
  199. "other": [2],
  200. "results": [3]
  201. }
  202. ';
  203. $parser = $this->createParser($json, '/results');
  204. $this->assertSame([1], iterator_to_array($parser));
  205. }
  206. public function testScalarResult()
  207. {
  208. $result = $this->createParser('{"result":{"items": [1,2,3],"count": 3}}', '/result/count');
  209. $this->assertSame([3], iterator_to_array($result));
  210. }
  211. public function testScalarResultInArray()
  212. {
  213. $result = $this->createParser('{"result":[1,2,3]}', '/result/0');
  214. $this->assertSame([1], iterator_to_array($result));
  215. }
  216. public function testGeneratorQuitsAfterFirstScalarHasBeenFound()
  217. {
  218. $json = '
  219. {
  220. "result": "one",
  221. "other": [2],
  222. "result": "three"
  223. }
  224. ';
  225. $parser = $this->createParser($json, '/result');
  226. $this->assertSame(['result' => 'one'], iterator_to_array($parser));
  227. }
  228. public function testGeneratorYieldsNestedValues()
  229. {
  230. $json = '
  231. {
  232. "zero": [
  233. {
  234. "one": "ignored",
  235. "two": [
  236. {
  237. "three": 1
  238. }
  239. ],
  240. "four": [
  241. {
  242. "five": "ignored"
  243. }
  244. ]
  245. },
  246. {
  247. "one": 1,
  248. "two": [
  249. {
  250. "three": 2
  251. },
  252. {
  253. "three": 3
  254. }
  255. ],
  256. "four": [
  257. {
  258. "five": "ignored"
  259. }
  260. ]
  261. }
  262. ]
  263. }
  264. ';
  265. $parser = $this->createParser($json, '/zero/-/two/-/three');
  266. $actual = [];
  267. $expected = ['three' => [1, 2, 3]];
  268. foreach ($parser as $key => $value) {
  269. $actual[$key][] = $value;
  270. }
  271. $this->assertSame($expected, $actual);
  272. }
  273. public function testGeneratorYieldsNestedValuesOfMultiplePaths()
  274. {
  275. $json = '
  276. {
  277. "zero": [
  278. {
  279. "one": "hello",
  280. "two": [
  281. {
  282. "three": 1
  283. }
  284. ],
  285. "four": [
  286. {
  287. "five": "ignored"
  288. }
  289. ]
  290. },
  291. {
  292. "one": "bye",
  293. "two": [
  294. {
  295. "three": 2
  296. },
  297. {
  298. "three": 3
  299. }
  300. ],
  301. "four": [
  302. {
  303. "five": "ignored"
  304. }
  305. ]
  306. }
  307. ]
  308. }
  309. ';
  310. $parser = $this->createParser($json, ['/zero/-/one', '/zero/-/two/-/three']);
  311. $actual = [];
  312. $expected = ['one' => ['hello', 'bye'], 'three' => [1, 2, 3]];
  313. foreach ($parser as $key => $value) {
  314. $actual[$key][] = $value;
  315. }
  316. $this->assertSame($expected, $actual);
  317. }
  318. private function createParser($json, $jsonPointer = '')
  319. {
  320. return new Parser(new Tokens(new \ArrayIterator([$json])), $jsonPointer, new ExtJsonDecoder(true));
  321. }
  322. public function testDefaultDecodingStructureIsObject()
  323. {
  324. $items = new Parser(new Tokens(new StringChunks('[{"key": "value"}]')));
  325. foreach ($items as $item) {
  326. $this->assertEquals((object) ['key' => 'value'], $item);
  327. }
  328. }
  329. /**
  330. * @dataProvider data_testGetCurrentJsonPointer
  331. */
  332. public function testGetCurrentJsonPointer($jsonPointer, string $json, array $currentJsonPointers)
  333. {
  334. $parser = $this->createParser($json, $jsonPointer);
  335. $i = 0;
  336. foreach ($parser as $value) {
  337. $this->assertEquals($currentJsonPointers[$i++], $parser->getCurrentJsonPointer());
  338. }
  339. }
  340. public function data_testGetCurrentJsonPointer()
  341. {
  342. return [
  343. ['', '{"c":1,"d":2}', ['', '']],
  344. ['/', '{"":{"c":1,"d":2}}', ['/', '/']],
  345. ['/~0', '{"~":{"c":1,"d":2}}', ['/~0', '/~0']],
  346. ['/~1', '{"/":{"c":1,"d":2}}', ['/~1', '/~1']],
  347. ['/~01', '{"~1":{"c":1,"d":2}}', ['/~01', '/~01']],
  348. ['/~00', '{"~0":{"c":1,"d":2}}', ['/~00', '/~00']],
  349. ['/~1/c', '{"/":{"c":[1,2],"d":2}}', ['/~1/c', '/~1/c']],
  350. ['/0', '[{"c":1,"d":2}, [null]]', ['/0', '/0']],
  351. ['/-', '[{"one": 1,"two": 2},{"three": 3,"four": 4}]', ['/0', '/0', '/1', '/1']],
  352. [
  353. ['/two', '/four'],
  354. '{"one": [1,11], "two": [2,22], "three": [3,33], "four": [4,44]}',
  355. ['/two', '/two', '/four', '/four'],
  356. ],
  357. [
  358. ['/-/two', '/-/one'],
  359. '[{"one": 1, "two": 2}, {"one": 1, "two": 2}]',
  360. ['/0/one', '/0/two', '/1/one', '/1/two'],
  361. ],
  362. ];
  363. }
  364. /**
  365. * @dataProvider data_testGetMatchedJsonPointer
  366. */
  367. public function testGetMatchedJsonPointer($jsonPointer, string $json, array $matchedJsonPointers)
  368. {
  369. $parser = $this->createParser($json, $jsonPointer);
  370. $i = 0;
  371. foreach ($parser as $value) {
  372. $this->assertEquals($matchedJsonPointers[$i++], $parser->getMatchedJsonPointer());
  373. }
  374. }
  375. public function data_testGetMatchedJsonPointer()
  376. {
  377. return [
  378. ['', '{"c":1,"d":2}', ['', '']],
  379. ['/', '{"":{"c":1,"d":2}}', ['/', '/']],
  380. ['/~0', '{"~":{"c":1,"d":2}}', ['/~0', '/~0']],
  381. ['/~1', '{"/":{"c":1,"d":2}}', ['/~1', '/~1']],
  382. ['/~01', '{"~1":{"c":1,"d":2}}', ['/~01', '/~01']],
  383. ['/~00', '{"~0":{"c":1,"d":2}}', ['/~00', '/~00']],
  384. ['/~1/c', '{"/":{"c":[1,2],"d":2}}', ['/~1/c', '/~1/c']],
  385. ['/0', '[{"c":1,"d":2}, [null]]', ['/0', '/0']],
  386. ['/-', '[{"one": 1,"two": 2},{"three": 3,"four": 4}]', ['/-', '/-', '/-', '/-']],
  387. [
  388. ['/two', '/four'],
  389. '{"one": [1,11], "two": [2,22], "three": [3,33], "four": [4,44]}',
  390. ['/two', '/two', '/four', '/four'],
  391. ],
  392. [
  393. ['/-/two', '/-/one'],
  394. '[{"one": 1, "two": 2}, {"one": 1, "two": 2}]',
  395. ['/-/one', '/-/two', '/-/one', '/-/two'],
  396. ],
  397. ];
  398. }
  399. public function testGetCurrentJsonPointerThrowsWhenCalledOutsideOfALoop()
  400. {
  401. $this->expectException(JsonMachineException::class);
  402. $this->expectExceptionMessage('must be called inside a loop');
  403. $parser = $this->createParser('[]');
  404. $parser->getCurrentJsonPointer();
  405. }
  406. public function testGetCurrentJsonPointerReturnsLiteralJsonPointer()
  407. {
  408. $parser = $this->createParser('{"\"key\\\\":"value"}', ['/\"key\\\\']);
  409. foreach ($parser as $key => $item) {
  410. $this->assertSame('/\"key\\\\', $parser->getCurrentJsonPointer());
  411. }
  412. }
  413. public function testGetMatchedJsonPointerThrowsWhenCalledOutsideOfALoop()
  414. {
  415. $this->expectException(JsonMachineException::class);
  416. $this->expectExceptionMessage('must be called inside a loop');
  417. $parser = $this->createParser('[]');
  418. $parser->getMatchedJsonPointer();
  419. }
  420. public function testGetMatchedJsonPointerReturnsLiteralMatch()
  421. {
  422. $parser = $this->createParser('{"\"key\\\\":"value"}', ['/\"key\\\\']);
  423. foreach ($parser as $key => $item) {
  424. $this->assertSame('/\"key\\\\', $parser->getMatchedJsonPointer());
  425. }
  426. }
  427. public function testGetJsonPointers()
  428. {
  429. $parser = $this->createParser('{}', ['/one', '/two']);
  430. $this->assertSame(['/one', '/two'], $parser->getJsonPointers());
  431. $parser = $this->createParser('{}');
  432. $this->assertSame([''], $parser->getJsonPointers());
  433. }
  434. public function testJsonPointerReferenceTokenMatchesJsonMemberNameLiterally()
  435. {
  436. $parser = $this->createParser('{"\\"key":"value"}', ['/\\"key']);
  437. foreach ($parser as $key => $item) {
  438. $this->assertSame('"key', $key);
  439. $this->assertSame('value', $item);
  440. }
  441. }
  442. public function testGetPositionReturnsCorrectPositionWithDebugEnabled()
  443. {
  444. $parser = new Parser(new TokensWithDebugging(['[ 1, "two", false ]']));
  445. $expectedPosition = [5, 12, 19];
  446. $this->assertSame(0, $parser->getPosition());
  447. foreach ($parser as $index => $item) {
  448. $this->assertSame($expectedPosition[$index], $parser->getPosition(), "index:$index, item:$item");
  449. }
  450. $this->assertSame(21, $parser->getPosition());
  451. }
  452. public function testGetPositionReturns0WithDebugDisabled()
  453. {
  454. $parser = new Parser(new Tokens(['[ 1, "two", false ]']));
  455. $this->assertSame(0, $parser->getPosition());
  456. foreach ($parser as $index => $item) {
  457. $this->assertSame(0, $parser->getPosition());
  458. }
  459. $this->assertSame(0, $parser->getPosition());
  460. }
  461. public function testGetPositionThrowsIfTokensDoNotSupportGetPosition()
  462. {
  463. $parser = new Parser(new \ArrayObject());
  464. $this->expectException(JsonMachineException::class);
  465. $parser->getPosition();
  466. }
  467. public function testThrowsMeaningfulErrorOnIncorrectTokens()
  468. {
  469. $parser = new Parser(new Tokens(['[$P]']));
  470. $this->expectException(SyntaxErrorException::class);
  471. foreach ($parser as $index => $item) {
  472. }
  473. }
  474. }