PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/test/php/net/xp_framework/unittest/annotations/AnnotationParsingTest.class.php

https://github.com/treuter/xp-framework
PHP | 556 lines | 261 code | 48 blank | 247 comment | 0 complexity | e41a9ace00674d73d16a793e9321205b MD5 | raw file
  1. <?php
  2. /* This class is part of the XP framework
  3. *
  4. * $Id$
  5. */
  6. uses('unittest.TestCase');
  7. /**
  8. * Tests the XP Framework's annotation parsing implementation
  9. *
  10. * @see rfc://0016
  11. * @see xp://lang.XPClass#parseAnnotations
  12. * @see http://bugs.xp-framework.net/show_bug.cgi?id=38
  13. * @see https://github.com/xp-framework/xp-framework/issues/14
  14. * @see https://github.com/xp-framework/xp-framework/pull/56
  15. * @see https://gist.github.com/1240769
  16. */
  17. class AnnotationParsingTest extends TestCase {
  18. /**
  19. * Helper
  20. *
  21. * @param string input
  22. * @return [:var]
  23. */
  24. protected function parse($input) {
  25. return XPClass::parseAnnotations($input, $this->getClassName());
  26. }
  27. /**
  28. * Tests simple annotation without a value
  29. *
  30. */
  31. #[@test]
  32. public function noValue() {
  33. $this->assertEquals(
  34. array(0 => array('hello' => NULL), 1 => array()),
  35. $this->parse("#[@hello]")
  36. );
  37. }
  38. /**
  39. * Tests simple annotation with string value
  40. *
  41. */
  42. #[@test]
  43. public function sqStringValue() {
  44. $this->assertEquals(
  45. array(0 => array('hello' => 'World'), 1 => array()),
  46. $this->parse("#[@hello('World')]")
  47. );
  48. }
  49. /**
  50. * Tests simple annotation with string value
  51. *
  52. */
  53. #[@test]
  54. public function sqStringValueWithEqualsSign() {
  55. $this->assertEquals(
  56. array(0 => array('hello' => 'World=Welt'), 1 => array()),
  57. $this->parse("#[@hello('World=Welt')]")
  58. );
  59. }
  60. /**
  61. * Test string with at sign inside
  62. *
  63. */
  64. #[@test]
  65. public function sqStringValueWithAtSign() {
  66. $this->assertEquals(
  67. array(0 => array('hello' => '@World'), 1 => array()),
  68. $this->parse("#[@hello('@World')]")
  69. );
  70. }
  71. /**
  72. * Test string with an annotation inside a string
  73. *
  74. */
  75. #[@test]
  76. public function sqStringValueWithAnnotation() {
  77. $this->assertEquals(
  78. array(0 => array('hello' => '@hello("World")'), 1 => array()),
  79. $this->parse("#[@hello('@hello(\"World\")')]")
  80. );
  81. }
  82. /**
  83. * Tests simple annotation with string value
  84. *
  85. */
  86. #[@test]
  87. public function sqStringValueWithDoubleQuotes() {
  88. $this->assertEquals(
  89. array(0 => array('hello' => 'said "he"'), 1 => array()),
  90. $this->parse("#[@hello('said \"he\"')]")
  91. );
  92. }
  93. /**
  94. * Tests simple annotation with string value
  95. *
  96. */
  97. #[@test]
  98. public function sqStringValueWithEscapedSingleQuotes() {
  99. $this->assertEquals(
  100. array(0 => array('hello' => "said 'he'"), 1 => array()),
  101. $this->parse("#[@hello('said \'he\'')]")
  102. );
  103. }
  104. /**
  105. * Tests simple annotation with string value
  106. *
  107. */
  108. #[@test]
  109. public function dqStringValue() {
  110. $this->assertEquals(
  111. array(0 => array('hello' => 'World'), 1 => array()),
  112. $this->parse('#[@hello("World")]')
  113. );
  114. }
  115. /**
  116. * Tests simple annotation with string value
  117. *
  118. */
  119. #[@test]
  120. public function dqStringValueWithSingleQuote() {
  121. $this->assertEquals(
  122. array(0 => array('hello' => 'Beck\'s'), 1 => array()),
  123. $this->parse('#[@hello("Beck\'s")]')
  124. );
  125. }
  126. /**
  127. * Tests simple annotation with string value
  128. *
  129. */
  130. #[@test]
  131. public function dqStringValueWithEscapedDoubleQuotes() {
  132. $this->assertEquals(
  133. array(0 => array('hello' => 'said "he"'), 1 => array()),
  134. $this->parse('#[@hello("said \"he\"")]')
  135. );
  136. }
  137. /**
  138. * Tests simple annotation with string value
  139. *
  140. */
  141. #[@test]
  142. public function dqStringValueWithEscapeSequence() {
  143. $this->assertEquals(
  144. array(0 => array('hello' => "World\n"), 1 => array()),
  145. $this->parse('#[@hello("World\n")]')
  146. );
  147. }
  148. /**
  149. * Test string with at sign inside
  150. *
  151. */
  152. #[@test]
  153. public function dqStringValueWithAtSign() {
  154. $this->assertEquals(
  155. array(0 => array('hello' => '@World'), 1 => array()),
  156. $this->parse('#[@hello("@World")]')
  157. );
  158. }
  159. /**
  160. * Test string with an annotation inside a string
  161. *
  162. */
  163. #[@test]
  164. public function dqStringValueWithAnnotation() {
  165. $this->assertEquals(
  166. array(0 => array('hello' => '@hello(\'World\')'), 1 => array()),
  167. $this->parse('#[@hello("@hello(\'World\')")]')
  168. );
  169. }
  170. /**
  171. * Tests simple annotation with an int value
  172. *
  173. */
  174. #[@test]
  175. public function intValue() {
  176. $this->assertEquals(
  177. array(0 => array('answer' => 42), 1 => array()),
  178. $this->parse('#[@answer(42)]')
  179. );
  180. }
  181. /**
  182. * Tests simple annotation with a double value
  183. *
  184. */
  185. #[@test]
  186. public function doubleValue() {
  187. $this->assertEquals(
  188. array(0 => array('version' => 3.5), 1 => array()),
  189. $this->parse('#[@version(3.5)]')
  190. );
  191. }
  192. /**
  193. * Tests simple annotation with multiple values
  194. *
  195. * @deprecated
  196. */
  197. #[@test]
  198. public function multiValueBackwardsCompatibility() {
  199. $this->assertEquals(
  200. array(0 => array('xmlmapping' => array('hw_server', 'server')), 1 => array()),
  201. $this->parse("#[@xmlmapping('hw_server', 'server')]")
  202. );
  203. xp::gc();
  204. }
  205. /**
  206. * Tests simple annotation with multiple values
  207. *
  208. * @deprecated
  209. */
  210. #[@test]
  211. public function multiValueBackwardsCompatibilityNoWhitespace() {
  212. $this->assertEquals(
  213. array(0 => array('xmlmapping' => array('hw_server', 'server')), 1 => array()),
  214. $this->parse("#[@xmlmapping('hw_server','server')]")
  215. );
  216. xp::gc();
  217. }
  218. /**
  219. * Tests simple annotation with multiple values
  220. *
  221. * @deprecated
  222. */
  223. #[@test]
  224. public function multiValueBackwardsCompatibilityMixedValue() {
  225. $this->assertEquals(
  226. array(0 => array('xmlmapping' => array('hw_server', TRUE)), 1 => array()),
  227. $this->parse("#[@xmlmapping('hw_server', TRUE)]")
  228. );
  229. xp::gc();
  230. }
  231. /**
  232. * Tests simple annotation with multiple values
  233. *
  234. */
  235. #[@test]
  236. public function multiValueUsingShortArray() {
  237. $this->assertEquals(
  238. array(0 => array('xmlmapping' => array('hw_server', 'server')), 1 => array()),
  239. $this->parse("#[@xmlmapping(['hw_server', 'server'])]")
  240. );
  241. }
  242. /**
  243. * Tests simple annotation with an array value
  244. *
  245. */
  246. #[@test]
  247. public function arrayValue() {
  248. $this->assertEquals(
  249. array(0 => array('versions' => array(3.4, 3.5)), 1 => array()),
  250. $this->parse('#[@versions(array(3.4, 3.5))]')
  251. );
  252. }
  253. /**
  254. * Tests simple annotation with an array value
  255. *
  256. */
  257. #[@test]
  258. public function arrayValueWithNestedArray() {
  259. $this->assertEquals(
  260. array(0 => array('versions' => array(array(3))), 1 => array()),
  261. $this->parse('#[@versions(array(array(3)))]')
  262. );
  263. }
  264. /**
  265. * Tests simple annotation with an array value
  266. *
  267. */
  268. #[@test]
  269. public function arrayValueWithNestedArrays() {
  270. $this->assertEquals(
  271. array(0 => array('versions' => array(array(3), array(4))), 1 => array()),
  272. $this->parse('#[@versions(array(array(3), array(4)))]')
  273. );
  274. }
  275. /**
  276. * Tests simple annotation with an array value
  277. *
  278. */
  279. #[@test]
  280. public function arrayValueWithStringsContainingBraces() {
  281. $this->assertEquals(
  282. array(0 => array('versions' => array('(3..4]')), 1 => array()),
  283. $this->parse('#[@versions(array("(3..4]"))]')
  284. );
  285. }
  286. /**
  287. * Tests simple annotation with a bool value
  288. *
  289. */
  290. #[@test]
  291. public function boolValue() {
  292. $this->assertEquals(
  293. array(0 => array('supported' => TRUE), 1 => array()),
  294. $this->parse('#[@supported(TRUE)]')
  295. );
  296. }
  297. /**
  298. * Tests different value types
  299. *
  300. */
  301. #[@test]
  302. public function keyValuePairsAnnotationValue() {
  303. $this->assertEquals(
  304. array(0 => array('config' => array('key' => 'value', 'times' => 5, 'disabled' => FALSE, 'null' => NULL, 'list' => array(1, 2))), 1 => array()),
  305. $this->parse("#[@config(key = 'value', times= 5, disabled= FALSE, null = NULL, list= array(1, 2))]")
  306. );
  307. }
  308. /**
  309. * Tests multi-line annotations
  310. *
  311. */
  312. #[@test]
  313. public function multiLineAnnotation() {
  314. $this->assertEquals(
  315. array(0 => array('interceptors' => array('classes' => array(
  316. 'net.xp_framework.unittest.core.FirstInterceptor',
  317. 'net.xp_framework.unittest.core.SecondInterceptor',
  318. ))), 1 => array()),
  319. $this->parse("
  320. #[@interceptors(classes= array(
  321. 'net.xp_framework.unittest.core.FirstInterceptor',
  322. 'net.xp_framework.unittest.core.SecondInterceptor',
  323. ))]
  324. ")
  325. );
  326. }
  327. /**
  328. * Tests simple xpath annotations
  329. *
  330. */
  331. #[@test]
  332. public function simpleXPathAnnotation() {
  333. $this->assertEquals(
  334. array(0 => array('fromXml' => array('xpath' => '/parent/child/@attribute')), 1 => array()),
  335. $this->parse("#[@fromXml(xpath= '/parent/child/@attribute')]")
  336. );
  337. }
  338. /**
  339. * Tests complex xpath annotations
  340. *
  341. */
  342. #[@test]
  343. public function complexXPathAnnotation() {
  344. $this->assertEquals(
  345. array(0 => array('fromXml' => array('xpath' => '/parent[@attr="value"]/child[@attr1="val1" and @attr2="val2"]')), 1 => array()),
  346. $this->parse("#[@fromXml(xpath= '/parent[@attr=\"value\"]/child[@attr1=\"val1\" and @attr2=\"val2\"]')]")
  347. );
  348. }
  349. /**
  350. * Tests string default with "="
  351. *
  352. */
  353. #[@test]
  354. public function stringWithEqualSigns() {
  355. $this->assertEquals(
  356. array(0 => array('permission' => 'rn=login, rt=config'), 1 => array()),
  357. $this->parse("#[@permission('rn=login, rt=config')]")
  358. );
  359. }
  360. /**
  361. * Test string assignment without whitespace is parsed correctly.
  362. *
  363. */
  364. #[@test]
  365. public function stringAssignedWithoutWhitespace() {
  366. $this->assertEquals(
  367. array(0 => array('arg' => array('name' => 'verbose', 'short' => 'v')), 1 => array()),
  368. $this->parse("#[@arg(name= 'verbose', short='v')]")
  369. );
  370. }
  371. /**
  372. * Test annotation with mulitple values containing equal signs
  373. * is parsed correctly.
  374. *
  375. */
  376. #[@test]
  377. public function multipleValuesWithStringsAndEqualSigns() {
  378. $this->assertEquals(
  379. array(0 => array('permission' => array('names' => array('rn=login, rt=config1', 'rn=login, rt=config2'))), 1 => array()),
  380. $this->parse("#[@permission(names= array('rn=login, rt=config1', 'rn=login, rt=config2'))]")
  381. );
  382. }
  383. /**
  384. * Test unittest annotations
  385. *
  386. * @see xp://unittest.TestCase
  387. */
  388. #[@test]
  389. public function unittestAnnotation() {
  390. $this->assertEquals(
  391. array(0 => array('test' => NULL, 'ignore' => NULL, 'limit' => array('time' => 0.1, 'memory' => 100)), 1 => array()),
  392. $this->parse("#[@test, @ignore, @limit(time = 0.1, memory = 100)]")
  393. );
  394. }
  395. /**
  396. * Test overloaded annotations
  397. *
  398. * @see xp://lang.reflect.Proxy
  399. */
  400. #[@test]
  401. public function overloadedAnnotation() {
  402. $this->assertEquals(
  403. array(0 => array('overloaded' => array('signatures' => array(array('string'), array('string', 'string')))), 1 => array()),
  404. $this->parse('#[@overloaded(signatures= array(array("string"), array("string", "string")))]')
  405. );
  406. }
  407. /**
  408. * Test webmethod annotation
  409. *
  410. */
  411. #[@test]
  412. public function webMethodWithParameterAnnotations() {
  413. $this->assertEquals(
  414. array(
  415. 0 => array('webmethod' => array('verb' => 'GET', 'path' => '/greet/{name}')),
  416. 1 => array('$name' => array('path' => NULL), '$greeting' => array('param' => NULL))
  417. ),
  418. $this->parse('#[@webmethod(verb= "GET", path= "/greet/{name}"), @$name: path, @$greeting: param]')
  419. );
  420. }
  421. /**
  422. * Test broken annotation
  423. *
  424. */
  425. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Expecting @/')]
  426. public function missingAnnotationAfterCommaAndValue() {
  427. $this->parse('#[@ignore("Test"), ]');
  428. }
  429. /**
  430. * Test broken annotation
  431. *
  432. */
  433. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Expecting @/')]
  434. public function missingAnnotationAfterComma() {
  435. $this->parse('#[@ignore, ]');
  436. }
  437. /**
  438. * Test broken annotation
  439. *
  440. */
  441. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Expecting @/')]
  442. public function missingAnnotationAfterSecondComma() {
  443. $this->parse('#[@ignore, @test, ]');
  444. }
  445. /**
  446. * Test broken annotation
  447. *
  448. */
  449. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Unterminated or malformed string/')]
  450. public function unterminatedString() {
  451. $this->parse('#[@ignore("Test)]');
  452. }
  453. /**
  454. * Test broken annotation
  455. *
  456. */
  457. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Unterminated array/')]
  458. public function unterminatedArray() {
  459. $this->parse('#[@ignore(array(1]');
  460. }
  461. /**
  462. * Test broken annotation
  463. *
  464. */
  465. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Unterminated array/')]
  466. public function unterminatedArrayKey() {
  467. $this->parse('#[@ignore(name = array(1]');
  468. }
  469. /**
  470. * Test broken annotation
  471. *
  472. */
  473. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Malformed array/')]
  474. public function malformedArray() {
  475. $this->parse('#[@ignore(array(1 ,, 2))]');
  476. }
  477. /**
  478. * Test broken annotation
  479. *
  480. */
  481. #[@test, @expect(class= 'lang.ClassFormatException', withMessage= '/Malformed array/')]
  482. public function malformedArrayKey() {
  483. $this->parse('#[@ignore(name= array(1 ,, 2))]');
  484. }
  485. /**
  486. * Test short array syntax
  487. *
  488. */
  489. #[@test]
  490. public function shortArraySyntaxAsValue() {
  491. $this->assertEquals(
  492. array(0 => array('permissions' => array('rn=login, rt=config', 'rn=admin, rt=config')), 1 => array()),
  493. $this->parse("#[@permissions(['rn=login, rt=config', 'rn=admin, rt=config'])]")
  494. );
  495. }
  496. /**
  497. * Test short array syntax
  498. *
  499. */
  500. #[@test]
  501. public function shortArraySyntaxAsKey() {
  502. $this->assertEquals(
  503. array(0 => array('permissions' => array('names' => array('rn=login, rt=config', 'rn=admin, rt=config'))), 1 => array()),
  504. $this->parse("#[@permissions(names = ['rn=login, rt=config', 'rn=admin, rt=config'])]")
  505. );
  506. }
  507. }
  508. ?>