PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/OData Producer for PHP/Tests/UriProcessor/QueryProcessor/OrderByParser/OrderByParserTest.php

#
PHP | 717 lines | 515 code | 39 blank | 163 comment | 8 complexity | ae3327810b6f052fe26f6a32e7d8ed80 MD5 | raw file
  1. <?php
  2. use ODataProducer\Providers\Metadata\ResourceProperty;
  3. use ODataProducer\Configuration\EntitySetRights;
  4. use ODataProducer\Providers\MetadataQueryProviderWrapper;
  5. use ODataProducer\Configuration\DataServiceConfiguration;
  6. use ODataProducer\Common\ODataException;
  7. use ODataProducer\UriProcessor\QueryProcessor\OrderByParser\OrderByParser;
  8. require_once 'PHPUnit\Framework\Assert.php';
  9. require_once 'PHPUnit\Framework\Test.php';
  10. require_once 'PHPUnit\Framework\SelfDescribing.php';
  11. require_once 'PHPUnit\Framework\TestCase.php';
  12. require_once 'PHPUnit\Framework\TestSuite.php';
  13. require_once 'ODataProducer\Common\ClassAutoLoader.php';
  14. require_once (dirname(__FILE__) . "\..\..\..\Resources\NorthWindMetadata.php");
  15. ODataProducer\Common\ClassAutoLoader::register();
  16. class TestOrderByParser extends PHPUnit_Framework_TestCase
  17. {
  18. protected function setUp()
  19. {
  20. }
  21. public function testOrderByWithSynaxError()
  22. {
  23. //If a path segment contains ( should throw synax error
  24. //only asc or desc are allowed as a default segment last segment
  25. //so if asc/desc is last then next should be end or comma
  26. //multiple commas not allowed
  27. }
  28. //All all test case (which are +ve) check the generated function and
  29. /**
  30. * Entities cannot be sorted using bag property
  31. */
  32. public function testOrderByBag()
  33. {
  34. try {
  35. $northWindMetadata = CreateNorthWindMetadata3::Create();
  36. $configuration = new DataServiceConfiguration($northWindMetadata);
  37. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  38. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  39. $northWindMetadata, //IDataServiceMetadataProvider implementation
  40. null, //IDataServiceQueryProvider implementation (set to null)
  41. $configuration, //Service configuuration
  42. false
  43. );
  44. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Employees');
  45. $resourceType = $resourceSetWrapper->getResourceType();
  46. $orderBy = 'Emails';
  47. $exceptionThrown = false;
  48. try {
  49. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  50. } catch (ODataException $odataException) {
  51. $this->assertStringStartsWith('orderby clause does not support Bag property in the path, the property', $odataException->getMessage());
  52. $exceptionThrown = true;
  53. }
  54. if (!$exceptionThrown) {
  55. $this->fail('An expected ODataException for bag property in the path');
  56. }
  57. } catch (\Exception $exception) {
  58. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  59. }
  60. }
  61. /**
  62. * Entities cannot be sorted using complex property
  63. */
  64. public function testOrderByComplex()
  65. {
  66. try {
  67. $northWindMetadata = CreateNorthWindMetadata3::Create();
  68. $configuration = new DataServiceConfiguration($northWindMetadata);
  69. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  70. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  71. $northWindMetadata, //IDataServiceMetadataProvider implementation
  72. null, //IDataServiceQueryProvider implementation (set to null)
  73. $configuration, //Service configuuration
  74. false
  75. );
  76. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
  77. $resourceType = $resourceSetWrapper->getResourceType();
  78. $orderBy = 'Address';
  79. $exceptionThrown = false;
  80. try {
  81. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  82. } catch (ODataException $odataException) {
  83. $this->assertStringStartsWith('Complex property cannot be used as sort key,', $odataException->getMessage());
  84. $exceptionThrown = true;
  85. }
  86. if (!$exceptionThrown) {
  87. $this->fail('An expected ODataException for using complex property as sort key has not been thrown');
  88. }
  89. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
  90. $resourceType = $resourceSetWrapper->getResourceType();
  91. $orderBy = 'Address/Address2';
  92. $exceptionThrown = false;
  93. try {
  94. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  95. } catch (ODataException $odataException) {
  96. $this->assertStringStartsWith('Complex property cannot be used as sort key,', $odataException->getMessage());
  97. $exceptionThrown = true;
  98. }
  99. if (!$exceptionThrown) {
  100. $this->fail('An expected ODataException for using complex property as sort key has not been thrown');
  101. }
  102. } catch (\Exception $exception) {
  103. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  104. }
  105. }
  106. /**
  107. * Entities cannot be sorted using resource set reference property
  108. * even resource set is not allowed in order by path
  109. */
  110. public function testOrderByResourceSetReference()
  111. {
  112. try {
  113. $northWindMetadata = CreateNorthWindMetadata3::Create();
  114. $configuration = new DataServiceConfiguration($northWindMetadata);
  115. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  116. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  117. $northWindMetadata, //IDataServiceMetadataProvider implementation
  118. null, //IDataServiceQueryProvider implementation (set to null)
  119. $configuration, //Service configuuration
  120. false
  121. );
  122. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
  123. $resourceType = $resourceSetWrapper->getResourceType();
  124. $orderBy = 'Orders/OrderID';
  125. $exceptionThrown = false;
  126. try {
  127. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  128. } catch (ODataException $odataException) {
  129. $this->assertStringStartsWith('Navigation property points to a collection cannot be used in orderby clause', $odataException->getMessage());
  130. $exceptionThrown = true;
  131. }
  132. if (!$exceptionThrown) {
  133. $this->fail('An expected ODataException for usage of resource reference set has not been thrown');
  134. }
  135. } catch (\Exception $exception) {
  136. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  137. }
  138. }
  139. /**
  140. * Entities cannot be sorted using resource reference property
  141. */
  142. public function testOrderByResourceReference()
  143. {
  144. try {
  145. $northWindMetadata = CreateNorthWindMetadata3::Create();
  146. $configuration = new DataServiceConfiguration($northWindMetadata);
  147. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  148. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  149. $northWindMetadata, //IDataServiceMetadataProvider implementation
  150. null, //IDataServiceQueryProvider implementation (set to null)
  151. $configuration, //Service configuuration
  152. false
  153. );
  154. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
  155. $resourceType = $resourceSetWrapper->getResourceType();
  156. $orderBy = 'Customer';
  157. $exceptionThrown = false;
  158. try {
  159. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  160. } catch (ODataException $odataException) {
  161. $this->assertStringStartsWith('Navigation property cannot be used as sort key,', $odataException->getMessage());
  162. $exceptionThrown = true;
  163. }
  164. if (!$exceptionThrown) {
  165. $this->fail('An expected ODataException for usage of resource reference as sort key has not been thrown');
  166. }
  167. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Order_Details');
  168. $resourceType = $resourceSetWrapper->getResourceType();
  169. $orderBy = 'Order/Customer';
  170. $exceptionThrown = false;
  171. try {
  172. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  173. } catch (ODataException $odataException) {
  174. $this->assertStringStartsWith('Navigation property cannot be used as sort key,', $odataException->getMessage());
  175. $exceptionThrown = true;
  176. }
  177. if (!$exceptionThrown) {
  178. $this->fail('An expected ODataException for usage of resource reference as sort key has not been thrown');
  179. }
  180. } catch (\Exception $exception) {
  181. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  182. }
  183. }
  184. /**
  185. * A primitive property of a complex type can be used as sort key
  186. */
  187. public function testOrderByPrimitiveInAComplex()
  188. {
  189. try {
  190. $northWindMetadata = CreateNorthWindMetadata3::Create();
  191. $configuration = new DataServiceConfiguration($northWindMetadata);
  192. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  193. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  194. $northWindMetadata, //IDataServiceMetadataProvider implementation
  195. null, //IDataServiceQueryProvider implementation (set to null)
  196. $configuration, //Service configuuration
  197. false
  198. );
  199. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
  200. $resourceType = $resourceSetWrapper->getResourceType();
  201. $orderBy = 'Address/HouseNumber';
  202. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  203. $orderByInfo = $internalOrderInfo->getOrderByInfo();
  204. //There is no navigation (resource reference) property in the orderby path so getNavigationPropertiesUsed should
  205. //return null (not empty array)
  206. $this->assertTrue(is_null($orderByInfo->getNavigationPropertiesUsed()));
  207. //There should be one orderby path segment with two sub path segments
  208. $orderByPathSegments = $orderByInfo->getOrderByPathSegments();
  209. $this->assertEquals(count($orderByPathSegments), 1);
  210. //no sort order, so default to asc
  211. $this->assertTrue($orderByPathSegments[0]->isAscending());
  212. //there are two sub path 'Address' and 'HouseNumber'
  213. $subPathSegments = $orderByPathSegments[0]->getSubPathSegments();
  214. $this->assertTrue(!is_null($subPathSegments));
  215. $this->assertEquals(count($subPathSegments), 2);
  216. $this->assertEquals($subPathSegments[0]->getName(), 'Address');
  217. $this->assertEquals($subPathSegments[1]->getName(), 'HouseNumber');
  218. $this->assertTrue($subPathSegments[0]->getResourceProperty() instanceof ResourceProperty);
  219. $this->assertTrue($subPathSegments[1]->getResourceProperty() instanceof ResourceProperty);
  220. //There is only one sub sorter function
  221. $subSorters = $internalOrderInfo->getSubSorterFunctions();
  222. $this->assertTrue(!is_null($subSorters));
  223. $this->assertEquals(count($subSorters), 1);
  224. //Parmater to this sub sort must be CustomersA, CustomersB
  225. $this->assertEquals($subSorters[0]->getParametersAsString(), '$CustomersA, $CustomersB');
  226. //since there is only one sub sorter, that will be the main sorter
  227. //asset this by comapring the anonymous function names
  228. $subSorterName = $subSorters[0]->getReference();
  229. $sorter = $internalOrderInfo->getSorterFunction();
  230. $this->assertTrue(!is_null($sorter));
  231. $mainSorterName = $sorter->getReference();
  232. $this->assertEquals($subSorterName, $mainSorterName);
  233. //check code inside the anonymous function (see the generated function code)
  234. /**
  235. $flag1 = is_null($CustomersA) || is_null($CustomersA->Address) || is_null($CustomersA->Address->HouseNumber);
  236. $flag2 = is_null($CustomersB) || is_null($CustomersB->Address) || is_null($CustomersB->Address->HouseNumber);
  237. if($flag1 && $flag2) {
  238. return 0;
  239. } else if ($flag1) {
  240. return 1*-1;
  241. } else if ($flag2) {
  242. return 1*1;
  243. }
  244. $result = strcmp($CustomersA->Address->HouseNumber, $CustomersB->Address->HouseNumber);
  245. return 1*$result;
  246. */
  247. $customer1 = new Customer2();
  248. $customer2 = new Customer2();
  249. //When any properties in the orderby path become null for both parameters then they are equal
  250. $customer1->Address = null;
  251. $customer2->Address = null;
  252. $result = $mainSorterName($customer1, $customer2);
  253. $this->assertEquals($result, 0);
  254. //When any properties in the orderby path become null for one parameter
  255. //and if the sort key for second parametr is not null, then second is greater
  256. $customer1->Address = new Address4();
  257. $customer1->Address->HouseNumber = null;
  258. $customer2->Address = new Address4();
  259. $customer2->Address->HouseNumber = '123';
  260. $result = $mainSorterName($customer1, $customer2);
  261. $this->assertEquals($result, -1);
  262. //reverse, second is lesser
  263. $customer1->Address = new Address4();
  264. $customer1->Address->HouseNumber = '123';
  265. $customer2->Address = new Address4();
  266. $customer2->Address->HouseNumber = null;
  267. $result = $mainSorterName($customer1, $customer2);
  268. $this->assertEquals($result, 1);
  269. //Try with not-null value for both sort key
  270. //'ABC' < 'DEF'
  271. $customer1->Address = new Address4();
  272. $customer1->Address->HouseNumber = 'ABC';
  273. $customer2->Address = new Address4();
  274. $customer2->Address->HouseNumber = 'DEF';
  275. $result = $mainSorterName($customer1, $customer2);
  276. $this->assertEquals($result, -1);
  277. //'XYZ' > 'PQR'
  278. $customer1->Address = new Address4();
  279. $customer1->Address->HouseNumber = 'XYZ';
  280. $customer2->Address = new Address4();
  281. $customer2->Address->HouseNumber = 'PQR';
  282. $result = $mainSorterName($customer1, $customer2);
  283. $this->assertEquals($result, 1);
  284. //'MN' == ''MN'
  285. $customer1->Address = new Address4();
  286. $customer1->Address->HouseNumber = 'MN';
  287. $customer2->Address = new Address4();
  288. $customer2->Address->HouseNumber = 'MN';
  289. $result = $mainSorterName($customer1, $customer2);
  290. $this->assertEquals($result, 0);
  291. } catch (\Exception $exception) {
  292. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  293. }
  294. }
  295. /**
  296. * Entities cannot be sorted using binary property
  297. */
  298. public function testOrderByBinaryProperty()
  299. {
  300. try {
  301. $northWindMetadata = CreateNorthWindMetadata3::Create();
  302. $configuration = new DataServiceConfiguration($northWindMetadata);
  303. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  304. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  305. $northWindMetadata, //IDataServiceMetadataProvider implementation
  306. null, //IDataServiceQueryProvider implementation (set to null)
  307. $configuration, //Service configuuration
  308. false
  309. );
  310. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
  311. $resourceType = $resourceSetWrapper->getResourceType();
  312. $orderBy = 'Photo';
  313. $exceptionThrown = false;
  314. try {
  315. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  316. } catch (ODataException $odataException) {
  317. $this->assertStringStartsWith('Binary property is not allowed in orderby', $odataException->getMessage());
  318. $exceptionThrown = true;
  319. }
  320. if (!$exceptionThrown) {
  321. $this->fail('An expected ODataException for usage of binary property has not been thrown');
  322. }
  323. } catch (\Exception $exception) {
  324. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  325. }
  326. }
  327. /**
  328. * A primitive property of a resource reference type can be used as sort key
  329. */
  330. public function testOrderByPrimitiveInAResourceReference()
  331. {
  332. try {
  333. $northWindMetadata = CreateNorthWindMetadata3::Create();
  334. $configuration = new DataServiceConfiguration($northWindMetadata);
  335. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  336. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  337. $northWindMetadata, //IDataServiceMetadataProvider implementation
  338. null, //IDataServiceQueryProvider implementation (set to null)
  339. $configuration, //Service configuuration
  340. false
  341. );
  342. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Order_Details');
  343. $resourceType = $resourceSetWrapper->getResourceType();
  344. $orderBy = 'Order/Customer/Rating';
  345. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  346. //Check the dummy object is initialized properly
  347. $dummyObject = $internalOrderInfo->getDummyObject();
  348. $this->assertTrue(is_object($dummyObject));
  349. $this->assertTrue($dummyObject instanceof Order_Details2);
  350. $this->assertTrue(isset($dummyObject->Order));
  351. $this->assertTrue(is_object($dummyObject->Order));
  352. $this->assertTrue($dummyObject->Order instanceof Order2);
  353. $this->assertTrue(isset($dummyObject->Order->Customer));
  354. $this->assertTrue(is_object($dummyObject->Order->Customer));
  355. $this->assertTrue($dummyObject->Order->Customer instanceof Customer2);
  356. $orderByInfo = $internalOrderInfo->getOrderByInfo();
  357. //There are navigation (resource reference) properties in the orderby path so getNavigationPropertiesUsed should
  358. //not be null
  359. $naviUsed = $orderByInfo->getNavigationPropertiesUsed();
  360. $this->assertFalse(is_null($naviUsed));
  361. //Only one main segment so one element in main collection
  362. $this->assertEquals(count($naviUsed), 1);
  363. $this->assertTrue(is_array($naviUsed[0]));
  364. //two navgations used in first orderby path segment 'Order' and 'Customer'
  365. $this->assertEquals(count($naviUsed[0]), 2);
  366. $this->assertTrue($naviUsed[0][0] instanceof ResourceProperty);
  367. $this->assertTrue($naviUsed[0][1] instanceof ResourceProperty);
  368. //default to library sorting
  369. $this->assertTrue($orderByInfo->requireInternalSorting());
  370. //There should be one orderby path segment with three sub path segments
  371. $orderByPathSegments = $orderByInfo->getOrderByPathSegments();
  372. $this->assertEquals(count($orderByPathSegments), 1);
  373. //no sort order, so default to asc
  374. $this->assertTrue($orderByPathSegments[0]->isAscending());
  375. //there are two sub path 'Order', 'Customer' and 'Rating'
  376. $subPathSegments = $orderByPathSegments[0]->getSubPathSegments();
  377. $this->assertTrue(!is_null($subPathSegments));
  378. $this->assertEquals(count($subPathSegments), 3);
  379. $this->assertEquals($subPathSegments[0]->getName(), 'Order');
  380. $this->assertEquals($subPathSegments[1]->getName(), 'Customer');
  381. $this->assertEquals($subPathSegments[2]->getName(), 'Rating');
  382. $this->assertTrue($subPathSegments[0]->getResourceProperty() instanceof ResourceProperty);
  383. $this->assertTrue($subPathSegments[1]->getResourceProperty() instanceof ResourceProperty);
  384. $this->assertTrue($subPathSegments[2]->getResourceProperty() instanceof ResourceProperty);
  385. //There is only one sub sorter function
  386. $subSorters = $internalOrderInfo->getSubSorterFunctions();
  387. $this->assertTrue(!is_null($subSorters));
  388. $this->assertEquals(count($subSorters), 1);
  389. //Parmater to this sub sort must be Order_DetailsA, Order_DetailsB
  390. $this->assertEquals($subSorters[0]->getParametersAsString(), '$Order_DetailsA, $Order_DetailsB');
  391. //since there is only one sub sorter, that will be the main sorter
  392. //asset this by comapring the anonymous function names
  393. $subSorterName = $subSorters[0]->getReference();
  394. $sorter = $internalOrderInfo->getSorterFunction();
  395. $this->assertTrue(!is_null($sorter));
  396. $mainSorterName = $sorter->getReference();
  397. $this->assertEquals($subSorterName, $mainSorterName);
  398. } catch (\Exception $exception) {
  399. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  400. }
  401. }
  402. /**
  403. * Orderby path cannot contain invisible resource reference property.
  404. */
  405. public function testOrderByWithInvisibleResourceReferencePropertyInThePath()
  406. {
  407. try {
  408. $northWindMetadata = CreateNorthWindMetadata3::Create();
  409. $configuration = new DataServiceConfiguration($northWindMetadata);
  410. //Make 'Orders' visible, make 'Customers' invisible
  411. $configuration->setEntitySetAccessRule('Orders', EntitySetRights::ALL);
  412. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  413. $northWindMetadata, //IDataServiceMetadataProvider implementation
  414. null, //IDataServiceQueryProvider implementation (set to null)
  415. $configuration, //Service configuuration
  416. false
  417. );
  418. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
  419. $resourceType = $resourceSetWrapper->getResourceType();
  420. $orderBy = 'Customer/CustomerID';
  421. $exceptionThrown = false;
  422. try {
  423. OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  424. } catch (ODataException $odataException) {
  425. $exceptionThrown = true;
  426. $this->assertStringEndsWith("(Check the resource set of the navigation property 'Customer' is visible)", $odataException->getMessage());
  427. }
  428. if (!$exceptionThrown) {
  429. $this->fail('An expected ODataException for navigation to invisible resource set has not been thrown');
  430. }
  431. } catch (\Exception $exception) {
  432. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  433. }
  434. }
  435. /**
  436. * test parser with multiple path segment which does not have common ancestors
  437. */
  438. public function testOrderByWithMultiplePathSegment1()
  439. {
  440. try {
  441. $northWindMetadata = CreateNorthWindMetadata3::Create();
  442. $configuration = new DataServiceConfiguration($northWindMetadata);
  443. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  444. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  445. $northWindMetadata, //IDataServiceMetadataProvider implementation
  446. null, //IDataServiceQueryProvider implementation (set to null)
  447. $configuration, //Service configuuration
  448. false
  449. );
  450. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Order_Details');
  451. $resourceType = $resourceSetWrapper->getResourceType();
  452. $orderBy = 'Order/Price desc, Product/ProductName asc';
  453. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  454. //Check the dummy object is initialized properly
  455. $dummyObject = $internalOrderInfo->getDummyObject();
  456. $this->assertTrue(is_object($dummyObject));
  457. $this->assertTrue($dummyObject instanceof Order_Details2);
  458. $this->assertTrue(isset($dummyObject->Order));
  459. $this->assertTrue(is_object($dummyObject->Order));
  460. $this->assertTrue($dummyObject->Order instanceof Order2);
  461. $this->assertTrue(isset($dummyObject->Product));
  462. $this->assertTrue(is_object($dummyObject->Product));
  463. $this->assertTrue($dummyObject->Product instanceof Product2);
  464. $orderByInfo = $internalOrderInfo->getOrderByInfo();
  465. //There are navigation (resource reference) properties in the orderby path so getNavigationPropertiesUsed should
  466. //not be null
  467. $naviUsed = $orderByInfo->getNavigationPropertiesUsed();
  468. $this->assertFalse(is_null($naviUsed));
  469. //two path segment so two element in main collection
  470. $this->assertEquals(count($naviUsed), 2);
  471. $this->assertTrue(is_array($naviUsed[0]));
  472. $this->assertTrue(is_array($naviUsed[1]));
  473. //one navgations used in first orderby 'Order'
  474. $this->assertEquals(count($naviUsed[0]), 1);
  475. //one navgations used in second orderby 'Prodcut'
  476. $this->assertEquals(count($naviUsed[1]), 1);
  477. $this->assertTrue($naviUsed[0][0] instanceof ResourceProperty);
  478. $this->assertTrue($naviUsed[1][0] instanceof ResourceProperty);
  479. //default to library sorting
  480. $this->assertTrue($orderByInfo->requireInternalSorting());
  481. //There should be two orderby path segment with two sub path segments
  482. $orderByPathSegments = $orderByInfo->getOrderByPathSegments();
  483. $this->assertEquals(count($orderByPathSegments), 2);
  484. //sort order specified Order/Price desc
  485. $this->assertFalse($orderByPathSegments[0]->isAscending());
  486. //sort order specified Prodcut/ProductName asc
  487. $this->assertTrue($orderByPathSegments[1]->isAscending());
  488. //there are two sub path 'Address' and 'HouseNumber'
  489. $subPathSegments = $orderByPathSegments[0]->getSubPathSegments();
  490. $this->assertTrue(!is_null($subPathSegments));
  491. $this->assertEquals(count($subPathSegments), 2);
  492. $this->assertEquals($subPathSegments[0]->getName(), 'Order');
  493. $this->assertEquals($subPathSegments[1]->getName(), 'Price');
  494. $this->assertTrue($subPathSegments[0]->getResourceProperty() instanceof ResourceProperty);
  495. $this->assertTrue($subPathSegments[1]->getResourceProperty() instanceof ResourceProperty);
  496. $subPathSegments = $orderByPathSegments[1]->getSubPathSegments();
  497. $this->assertTrue(!is_null($subPathSegments));
  498. $this->assertEquals(count($subPathSegments), 2);
  499. $this->assertEquals($subPathSegments[0]->getName(), 'Product');
  500. $this->assertEquals($subPathSegments[1]->getName(), 'ProductName');
  501. $this->assertTrue($subPathSegments[0]->getResourceProperty() instanceof ResourceProperty);
  502. $this->assertTrue($subPathSegments[1]->getResourceProperty() instanceof ResourceProperty);
  503. //There is two one sub sorter function
  504. $subSorters = $internalOrderInfo->getSubSorterFunctions();
  505. $this->assertTrue(!is_null($subSorters));
  506. $this->assertEquals(count($subSorters), 2);
  507. //Parmater to first sub sort must be $Order_DetailsA, $Order_DetailsB
  508. $this->assertEquals($subSorters[0]->getParametersAsString(), '$Order_DetailsA, $Order_DetailsB');
  509. $this->assertEquals($subSorters[1]->getParametersAsString(), '$Order_DetailsA, $Order_DetailsB');
  510. //generate sub sorter functions with different names
  511. $subSorterName1 = $subSorters[0]->getReference();
  512. $subSorterName2 = $subSorters[1]->getReference();
  513. $this->assertNotEquals($subSorterName1, $subSorterName2);
  514. $sorter = $internalOrderInfo->getSorterFunction();
  515. $this->assertTrue(!is_null($sorter));
  516. $mainSorterName = $sorter->getReference();
  517. //Test the function generated for 'Order/Price desc' path
  518. /**
  519. //Function Name: lambda_1
  520. $flag1 = is_null($Order_DetailsA) || is_null($Order_DetailsA->Order) || is_null($Order_DetailsA->Order->Price);
  521. $flag2 = is_null($Order_DetailsB) || is_null($Order_DetailsB->Order) || is_null($Order_DetailsB->Order->Price);
  522. if($flag1 && $flag2) {
  523. return 0;
  524. } else if ($flag1) {
  525. return -1*-1;
  526. } else if ($flag2) {
  527. return -1*1;
  528. }
  529. $result = $Order_DetailsA->Order->Price > $Order_DetailsB->Order->Price;
  530. return -1*$result;
  531. */
  532. $OrderDetails1 = new Order_Details2();
  533. $OrderDetails2 = new Order_Details2();
  534. //When any properties in the orderby path become null for both parameters then they are equal
  535. $OrderDetails1->Order = new Order2();
  536. $OrderDetails1->Order->Price = null;
  537. $OrderDetails2->Order = null;
  538. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  539. $this->assertEquals($result, 0);
  540. //When any properties in the orderby path become null for one parameter
  541. //and if the sort key for second parametr is not null, then second is considered as lesser for desc
  542. $OrderDetails1->Order = new Order2();
  543. $OrderDetails1->Order->Price = null;
  544. $OrderDetails2->Order = new Order2();
  545. $OrderDetails2->Order->Price = 12;
  546. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  547. $this->assertEquals($result, 1);
  548. //reverse, second is greater
  549. $OrderDetails1->Order = new Order2();
  550. $OrderDetails1->Order->Price = 12;
  551. $OrderDetails2->Order = new Order2();
  552. $OrderDetails2->Order->Price = null;
  553. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  554. $this->assertEquals($result, -1);
  555. //Try with not-null value for both sort key
  556. //12 < 13
  557. $OrderDetails1->Order = new Order2();
  558. $OrderDetails1->Order->Price = 12;
  559. $OrderDetails2->Order = new Order2();
  560. $OrderDetails2->Order->Price = 13;
  561. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  562. $this->assertEquals($result, 1);
  563. //14 > 10
  564. $OrderDetails1->Order = new Order2();
  565. $OrderDetails1->Order->Price = 14;
  566. $OrderDetails2->Order = new Order2();
  567. $OrderDetails2->Order->Price = 10;
  568. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  569. $this->assertEquals($result, -1);
  570. //6 == 6
  571. $OrderDetails1->Order = new Order2();
  572. $OrderDetails1->Order->Price = 6;
  573. $OrderDetails2->Order = new Order2();
  574. $OrderDetails2->Order->Price = 6;
  575. $result = $subSorterName1($OrderDetails1, $OrderDetails2);
  576. $this->assertEquals($result, 0);
  577. //No test for function generated for 'Product/ProductName asc' path, already done in the testcase testOrderByPrimitiveInAComplex
  578. /**
  579. //Function Name: lambda_2
  580. $flag1 = is_null($Order_DetailsA) || is_null($Order_DetailsA->Product) || is_null($Order_DetailsA->Product->ProductName);
  581. $flag2 = is_null($Order_DetailsB) || is_null($Order_DetailsB->Product) || is_null($Order_DetailsB->Product->ProductName);
  582. if($flag1 && $flag2) {
  583. return 0;
  584. } else if ($flag1) {
  585. return 1*-1;
  586. } else if ($flag2) {
  587. return 1*1;
  588. }
  589. $result = strcmp($Order_DetailsA->Product->ProductName, $Order_DetailsB->Product->ProductName);
  590. return 1*$result;
  591. */
  592. //Test the top level function
  593. /**
  594. //Function Name: lambda_3
  595. $result = call_user_func_array(chr(0) . 'lambda_1', array($Order_DetailsA, $Order_DetailsB));
  596. if ($result != 0) {
  597. return $result;
  598. }
  599. $result = call_user_func_array(chr(0) . 'lambda_2', array($Order_DetailsA, $Order_DetailsB));
  600. if ($result != 0) {
  601. return $result;
  602. }
  603. return $result;
  604. */
  605. $OrderDetails1->Order = new Order2();
  606. $OrderDetails1->Product = new Product2();
  607. $OrderDetails1->Order->Price = 6;
  608. $OrderDetails1->Product->ProductName = 'AB';
  609. $OrderDetails2->Order = new Order2();
  610. $OrderDetails2->Product = new Product2();
  611. $OrderDetails2->Order->Price = 6;
  612. $OrderDetails2->Product->ProductName = 'DE';
  613. $result = $mainSorterName($OrderDetails1, $OrderDetails2);
  614. $this->assertEquals($result, -1);
  615. } catch (\Exception $exception) {
  616. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  617. }
  618. }
  619. /**
  620. * test parser with multiple path segment which has common ancestors
  621. */
  622. public function testOrderByWithMultiplePathSegment2()
  623. {
  624. }
  625. /**
  626. * Test whether order by parser identify and remove path duplication
  627. */
  628. public function testOrderByWithPathDuplication()
  629. {
  630. try {
  631. $northWindMetadata = CreateNorthWindMetadata3::Create();
  632. $configuration = new DataServiceConfiguration($northWindMetadata);
  633. $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
  634. $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
  635. $northWindMetadata, //IDataServiceMetadataProvider implementation
  636. null, //IDataServiceQueryProvider implementation (set to null)
  637. $configuration, //Service configuuration
  638. false
  639. );
  640. $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Order_Details');
  641. $resourceType = $resourceSetWrapper->getResourceType();
  642. $orderBy = 'Order/Price desc, Product/ProductName asc, Order/Price asc';
  643. $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
  644. //The orderby path Order/Price appears twice, but parser will consider only first path
  645. $orderByInfo = $internalOrderInfo->getOrderByInfo();
  646. //There are navigation (resource reference) properties in the orderby path so getNavigationPropertiesUsed should
  647. //not be null
  648. $naviUsed = $orderByInfo->getNavigationPropertiesUsed();
  649. $this->assertFalse(is_null($naviUsed));
  650. //3 path segment are there, but last one is duplicate of first one, so parser removes last one
  651. $this->assertEquals(count($naviUsed), 2);
  652. $this->assertTrue(is_array($naviUsed[0]));
  653. $this->assertTrue(is_array($naviUsed[1]));
  654. //one navgations used in first orderby 'Order'
  655. $this->assertEquals(count($naviUsed[0]), 1);
  656. //one navgations used in second orderby 'Prodcut'
  657. $this->assertEquals(count($naviUsed[1]), 1);
  658. $this->assertEquals($naviUsed[0][0]->getName(), 'Order');
  659. $this->assertEquals($naviUsed[1][0]->getName(), 'Product');
  660. $orderByPathSegments = $orderByInfo->getOrderByPathSegments();
  661. $this->assertEquals(count($orderByPathSegments), 2);
  662. } catch (\Exception $exception) {
  663. $this->fail('An unexpected Exception has been raised' . $exception->getMessage());
  664. }
  665. }
  666. protected function tearDown()
  667. {
  668. }
  669. }
  670. ?>