PageRenderTime 62ms CodeModel.GetById 14ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 1ms

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