PageRenderTime 2ms CodeModel.GetById 57ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

/codes-php/phpjakarta/tests/functional/WindowsAzure/Table/TableServiceFunctionalQueryTest.php

http://bukuphpjs.codeplex.com
PHP | 671 lines | 484 code | 101 blank | 86 comment | 45 complexity | 634a67adc6378456b91b77e150ff5b14 MD5 | raw file
  1<?php
  2
  3/**
  4 * LICENSE: Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 * http://www.apache.org/licenses/LICENSE-2.0
  8 *
  9 * Unless required by applicable law or agreed to in writing, software
 10 * distributed under the License is distributed on an "AS IS" BASIS,
 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 * See the License for the specific language governing permissions and
 13 * limitations under the License.
 14 * 
 15 * PHP version 5
 16 *
 17 * @category  Microsoft
 18 * @package   Tests\Functional\WindowsAzure\Table
 19 * @author    Azure PHP SDK <azurephpsdk@microsoft.com>
 20 * @copyright 2012 Microsoft Corporation
 21 * @license   http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 22 * @link      https://github.com/windowsazure/azure-sdk-for-php
 23 */
 24
 25namespace Tests\Functional\WindowsAzure\Table;
 26
 27use Tests\Framework\TestResources;
 28use WindowsAzure\Common\ServiceException;
 29use WindowsAzure\Table\Models\BatchOperations;
 30use WindowsAzure\Table\Models\EdmType;
 31use WindowsAzure\Table\Models\Entity;
 32use WindowsAzure\Table\Models\Query;
 33use WindowsAzure\Table\Models\QueryEntitiesOptions;
 34use WindowsAzure\Table\Models\Filters\Filter;
 35
 36class TableServiceFunctionalQueryTest extends FunctionalTestBase
 37{
 38    private static $entitiesInTable;
 39    private static $Partitions = array('Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo');
 40    private static $curPartition;
 41    private static $curRowKey;
 42
 43    public static function setUpBeforeClass()
 44    {
 45        parent::setUpBeforeClass();
 46
 47        $table = TableServiceFunctionalTestData::$testTableNames[0];
 48        self::$entitiesInTable = self::getEntitiesToQueryOver();
 49        $baseWithRestProxy = new FunctionalTestBase();
 50        $baseWithRestProxy->setUp();
 51        $parts = array();
 52        foreach(self::$entitiesInTable as $entity)  {
 53            if (array_key_exists($entity->getPartitionKey(), $parts) === false) {
 54                $parts[$entity->getPartitionKey()] = array();
 55            }
 56            array_push($parts[$entity->getPartitionKey()], $entity);
 57        }
 58        foreach($parts as $part) {
 59            $batch = new BatchOperations();
 60            foreach($part as $entity)  {
 61                $batch->addInsertEntity($table, $entity);
 62            }
 63            $baseWithRestProxy->restProxy->batch($batch);
 64        }
 65    }
 66
 67    private static function getNewEntity()
 68    {
 69        if (is_null(self::$curPartition) || self::$curPartition == count(self::$Partitions) - 1) {
 70            self::$curPartition = 0;
 71            self::$curRowKey = TableServiceFunctionalTestData::getNewKey();
 72        } else {
 73            self::$curPartition++;
 74        }
 75
 76        $entity = new Entity();
 77        $entity->setPartitionKey(self::$Partitions[self::$curPartition]);
 78        $entity->setRowKey(self::$curRowKey);
 79        return $entity;
 80    }
 81
 82    private static function getEntitiesToQueryOver()
 83    {
 84        $ret = array();
 85
 86        array_push($ret, self::getNewEntity());
 87
 88        $entity = self::getNewEntity();
 89        $entity->addProperty('BOOLEAN', EdmType::BOOLEAN, true);
 90        array_push($ret, $entity);
 91
 92        $entity = self::getNewEntity();
 93        $entity->addProperty('BOOLEAN', EdmType::BOOLEAN, false);
 94        array_push($ret, $entity);
 95
 96        $entity = self::getNewEntity();
 97        $entity->addProperty('DATETIME', EdmType::DATETIME, new \DateTime());
 98        array_push($ret, $entity);
 99
100        $entity = self::getNewEntity();
101        $entity->addProperty('DATETIME', EdmType::DATETIME, new \DateTime('2012-01-02'));
102        array_push($ret, $entity);
103
104        $entity = self::getNewEntity();
105        $entity->addProperty('DOUBLE', EdmType::DOUBLE, 2.71828183);
106        array_push($ret, $entity);
107
108        $entity = self::getNewEntity();
109        $entity->addProperty('DOUBLE', EdmType::DOUBLE, 3.14159265);
110        array_push($ret, $entity);
111
112        $entity = self::getNewEntity();
113        $entity->addProperty('GUID', EdmType::GUID, '90ab64d6-d3f8-49ec-b837-b8b5b6367b74');
114        array_push($ret, $entity);
115
116        $entity = self::getNewEntity();
117        $entity->addProperty('GUID', EdmType::GUID, '00000000-1111-2222-3333-444444444444');
118        array_push($ret, $entity);
119
120        $entity = self::getNewEntity();
121        $entity->addProperty('INT32', EdmType::INT32, 23);
122        array_push($ret, $entity);
123
124        $entity = self::getNewEntity();
125        $entity->addProperty('INT32', EdmType::INT32, 42);
126        array_push($ret, $entity);
127
128        $entity = self::getNewEntity();
129        $entity->addProperty('INT64', EdmType::INT64, '-1');
130        array_push($ret, $entity);
131
132        $entity = self::getNewEntity();
133        $entity->addProperty('INT64', EdmType::INT64, strval(TableServiceFunctionalTestData::LONG_BIG_VALUE));
134        array_push($ret, $entity);
135
136        $entity = self::getNewEntity();
137        $entity->addProperty('STRING', EdmType::STRING, 'foo');
138        array_push($ret, $entity);
139
140        $entity = self::getNewEntity();
141        $entity->addProperty('STRING', EdmType::STRING, 'o hai');
142        array_push($ret, $entity);
143
144        $entity = self::getNewEntity();
145
146        $e = self::getNewEntity();
147        $e->addProperty('test', EdmType::BOOLEAN, true);
148        $e->addProperty('test2', EdmType::STRING, 'value');
149        $e->addProperty('test3', EdmType::INT32, 3);
150        $e->addProperty('test4', EdmType::INT64, '12345678901');
151        $e->addProperty('test5', EdmType::DATETIME, new \DateTime());
152        array_push($ret, $e);
153
154        $booleans = TableServiceFunctionalTestData::getInterestingGoodBooleans();
155        $dates = TableServiceFunctionalTestData::getInterestingGoodDates();
156        $doubles = TableServiceFunctionalTestData::getInterestingGoodDoubles();
157        $guids = TableServiceFunctionalTestData::getInterestingGoodGuids();
158        $ints = TableServiceFunctionalTestData::getInterestingGoodInts();
159        $longs = TableServiceFunctionalTestData::getInterestingGoodLongs();
160        $binaries = TableServiceFunctionalTestData::getInterestingGoodBinaries();
161        $strings = TableServiceFunctionalTestData::getInterestingGoodStrings();
162
163        // The random here is not to generate random values, but to
164        // get a good mix of values in the table entities.
165        mt_srand(123);
166
167        for ($i = 0; $i < 20; $i++) {
168            $e = self::getNewEntity();
169            TableServiceFunctionalTestData::addProperty($e, 'BINARY', EdmType::BINARY, $binaries);
170            TableServiceFunctionalTestData::addProperty($e, 'BOOLEAN', EdmType::BOOLEAN, $booleans);
171            TableServiceFunctionalTestData::addProperty($e, 'DATETIME', EdmType::DATETIME, $dates);
172            TableServiceFunctionalTestData::addProperty($e, 'DOUBLE', EdmType::DOUBLE, $doubles);
173            TableServiceFunctionalTestData::addProperty($e, 'GUID', EdmType::GUID, $guids);
174            TableServiceFunctionalTestData::addProperty($e, 'INT32', EdmType::INT32, $ints);
175            TableServiceFunctionalTestData::addProperty($e, 'INT64', EdmType::INT64, $longs);
176            TableServiceFunctionalTestData::addProperty($e, 'STRING', EdmType::STRING, $strings);
177            array_push($ret, $e);
178        }
179
180        return $ret;
181    }
182
183    public static function getInterestingQueryEntitiesOptions()
184    {
185        $ret = array();
186        $e = self::$entitiesInTable[count(self::$entitiesInTable) - 3];
187
188        $options = new QueryEntitiesOptions();
189        array_push($ret, $options);
190
191        $options = new QueryEntitiesOptions();
192        $query = new Query();
193        $options->setQuery($query);
194        array_push($ret, $options);
195
196        $options = new QueryEntitiesOptions();
197        $query = new Query();
198        $query->setTop(2);
199        $options->setQuery($query);
200        array_push($ret, $options);
201
202        $options = new QueryEntitiesOptions();
203        $query = new Query();
204        $query->setTop(-2);
205        $options->setQuery($query);
206        array_push($ret, $options);
207
208        $options = new QueryEntitiesOptions();
209        $query = new Query();
210        $query->addSelectField('TableName');
211        $options->setQuery($query);
212        array_push($ret, $options);
213
214        $options = new QueryEntitiesOptions();
215        $query = new Query();
216        $filter = Filter::applyPropertyName('BOOLEAN');
217        $query->setFilter($filter);
218        $options->setQuery($query);
219        array_push($ret, $options);
220
221        $options = new QueryEntitiesOptions();
222        $query = new Query();
223        $filter = Filter::applyConstant(false, EdmType::BOOLEAN);
224        $query->setFilter($filter);
225        $options->setQuery($query);
226        array_push($ret, $options);
227
228        $options = new QueryEntitiesOptions();
229        $query = new Query();
230        $filter = Filter::applyEq(Filter::applyConstant(23, EdmType::INT32), Filter::applyPropertyName('INT32'));
231        $query->setFilter($filter);
232        $options->setQuery($query);
233        array_push($ret, $options);
234
235        $options = new QueryEntitiesOptions();
236        $query = new Query();
237        $filter = Filter::applyNe(Filter::applyConstant(23, EdmType::INT32), Filter::applyPropertyName('INT32'));
238        $query->setFilter($filter);
239        $options->setQuery($query);
240        array_push($ret, $options);
241
242        $options = new QueryEntitiesOptions();
243        $query = new Query();
244        $filter = Filter::applyNot(Filter::applyEq(Filter::applyConstant(23, EdmType::INT32), Filter::applyPropertyName('INT32')));
245        $query->setFilter($filter);
246        $options->setQuery($query);
247        array_push($ret, $options);
248
249        $options = new QueryEntitiesOptions();
250        $options->setNextPartitionKey($e->getPartitionKey());
251        $options->setNextRowKey($e->getRowKey());
252        array_push($ret, $options);
253
254        // Ask for an entity that does not exist.
255        $options = new QueryEntitiesOptions();
256        $options->setNextPartitionKey(self::$Partitions[2] . 'X');
257        $options->setNextRowKey($e->getRowKey() . 'X');
258        array_push($ret, $options);
259
260        return $ret;
261    }
262
263    public static function getInterestingQueryEntitiesOptionsOfDepth($depth)
264    {
265        $ret = array();
266
267        // The random here is not to generate random values, but to
268        // get a good mix of values in the table entities.
269        mt_srand(456 + $depth);
270        for ($i = 1; $i < 20; $i++) {
271            $filter = self::generateFilterWithBooleanParameters($depth, 0);
272            $options = new QueryEntitiesOptions();
273            $query = new Query();
274            $query->setFilter($filter);
275            $options->setQuery($query);
276            array_push($ret, $options);
277        }
278
279        return $ret;
280    }
281
282    private static function generateFilterWithBooleanParameters($targetDepth, $depth)
283    {
284        // Use the filter grammar to construct a tree.
285        // The random here is not to generate random values, but to
286        // get a good mix of values in the table entities.
287
288        // TODO: Treat raw string special
289        if ($depth == $targetDepth) {
290            switch (mt_rand(0,2)) {
291                case 0:
292                    return self::generateBinaryFilterWithAnyParameters();
293                case 1:
294                    return Filter::applyConstant(mt_rand(0,1) == 1, EdmType::BOOLEAN);
295                case 2:
296                    $e = self::getEntityFromTable();
297                    $boolPropNames = array();
298                    foreach($e->getProperties() as $key => $p)  {
299                        if ($p->getEdmType() == EdmType::BOOLEAN) {
300                            array_push($boolPropNames, $key);
301                        }
302                    }
303                    if (count($boolPropNames) == 0) {
304                        return Filter::applyConstant(mt_rand(0,1) == 1, EdmType::BOOLEAN);
305                    } else {
306                        $key = $boolPropNames[mt_rand(0, count($boolPropNames) - 1)];
307                        return Filter::applyPropertyName($key);
308                    }
309                default:
310                    return null;
311            }
312        } else {
313            switch (mt_rand(0,8)) {
314                case 0:
315                case 1:
316                case 2:
317                case 3:
318                    return Filter::applyAnd(
319                            self::generateFilterWithBooleanParameters($targetDepth, $depth + 1),
320                            self::generateFilterWithBooleanParameters($targetDepth, $depth + 1));
321                case 4:
322                case 5:
323                case 6:
324                case 7:
325                    return Filter::applyOr(
326                            self::generateFilterWithBooleanParameters($targetDepth, $depth + 1),
327                            self::generateFilterWithBooleanParameters($targetDepth, $depth + 1));
328                case 8:
329                    return Filter::applyNot(self::generateFilterWithBooleanParameters($targetDepth, $depth + 1));
330                default:
331                    return null;
332            }
333        }
334    }
335
336    private static function generateBinaryFilterWithAnyParameters()
337    {
338        // get a good mix of values in the table entities.
339
340        // Pull out one of the constants.
341        $e = self::getEntityFromTable();
342        $keys = array_keys($e->getProperties());
343        $propId = mt_rand(0, count($keys) - 1);
344        $key = $keys[$propId];
345
346        $prop = $e->getProperty($key);
347        $f1 = Filter::applyConstant($prop->getValue(), $prop->getEdmType());
348        $f2 = Filter::applyPropertyName($key);
349
350        if (mt_rand(0,1) == 1) {
351            // Try swapping.
352            $t = $f1;
353            $f1 = $f2;
354            $f2 = $t;
355        }
356
357        return self::getBinaryFilterFromIndex(mt_rand(0, 5), $f1, $f2);
358    }
359
360    private static function getEntityFromTable()
361    {
362        $entId = mt_rand(0, count(self::$entitiesInTable) - 1);
363        $e = self::$entitiesInTable[$entId];
364        return $e;
365    }
366
367    private static function getBinaryFilterFromIndex($index, $f1, $f2)
368    {
369        switch ($index) {
370            case 0: return Filter::applyEq($f1, $f2);
371            case 1: return Filter::applyGe($f1, $f2);
372            case 2: return Filter::applyGt($f1, $f2);
373            case 3: return Filter::applyLe($f1, $f2);
374            case 4: return Filter::applyLt($f1, $f2);
375            case 5: return Filter::applyNe($f1, $f2);
376            default: return null;
377        }
378    }
379
380    /**
381    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
382    */
383    public function testQueryEntities()
384    {
385        // The emulator has problems with non-standard queries tested here.
386        $this->skipIfEmulated();
387
388        $interestingqueryEntitiesOptions = self::getInterestingQueryEntitiesOptions();
389        foreach($interestingqueryEntitiesOptions as $options)  {
390            $this->queryEntitiesWorker($options);
391        }
392    }
393
394    /**
395    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
396    */
397    public function testQueryEntitiesBooleanLevel1()
398    {
399        // The emulator has problems with non-standard queries tested here.
400        $this->skipIfEmulated();
401
402        $interestingqueryEntitiesOptions = self::addBinaryFilter('BOOLEAN', EdmType::BOOLEAN, TableServiceFunctionalTestData::getInterestingGoodBooleans());
403        foreach($interestingqueryEntitiesOptions as $options)  {
404            $this->queryEntitiesWorker($options);
405        }
406    }
407
408    /**
409    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
410    */
411    public function testQueryEntitiesDateTimeLevel1()
412    {
413        // The emulator has problems with non-standard queries tested here.
414        $this->skipIfEmulated();
415
416        $interestingqueryEntitiesOptions = self::addBinaryFilter('DATETIME', EdmType::DATETIME, TableServiceFunctionalTestData::getInterestingGoodDates());
417        foreach($interestingqueryEntitiesOptions as $options)  {
418            $this->queryEntitiesWorker($options);
419        }
420    }
421
422    /**
423    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
424    */
425    public function testQueryEntitiesDoubleLevel1()
426    {
427        // The emulator has problems with non-standard queries tested here.
428        $this->skipIfEmulated();
429
430        $interestingqueryEntitiesOptions = self::addBinaryFilter('DOUBLE', EdmType::DOUBLE, TableServiceFunctionalTestData::getInterestingGoodDoubles());
431        foreach($interestingqueryEntitiesOptions as $options)  {
432            $this->queryEntitiesWorker($options);
433        }
434    }
435
436    /**
437    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
438    */
439    public function testQueryEntitiesGuidLevel1()
440    {
441        // The emulator has problems with non-standard queries tested here.
442        $this->skipIfEmulated();
443
444        $interestingqueryEntitiesOptions = self::addBinaryFilter('GUID', EdmType::GUID, TableServiceFunctionalTestData::getInterestingGoodGuids());
445        foreach($interestingqueryEntitiesOptions as $options)  {
446            $this->queryEntitiesWorker($options);
447        }
448    }
449
450    /**
451    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
452    */
453    public function testQueryEntitiesIntLevel1()
454    {
455        // The emulator has problems with non-standard queries tested here.
456        $this->skipIfEmulated();
457
458         $interestingqueryEntitiesOptions = self::addBinaryFilter('INT32', EdmType::INT32, TableServiceFunctionalTestData::getInterestingGoodInts());
459        foreach($interestingqueryEntitiesOptions as $options)  {
460            $this->queryEntitiesWorker($options);
461        }
462    }
463
464    /**
465    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
466    */
467    public function testQueryEntitiesLongLevel1()
468    {
469        // The emulator has problems with non-standard queries tested here.
470        $this->skipIfEmulated();
471
472        $interestingqueryEntitiesOptions = self::addBinaryFilter('INT64', EdmType::INT64, TableServiceFunctionalTestData::getInterestingGoodLongs());
473        foreach($interestingqueryEntitiesOptions as $options)  {
474            $this->queryEntitiesWorker($options);
475        }
476    }
477
478    /**
479    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
480    */
481    public function testQueryEntitiesStringLevel1()
482    {
483        // The emulator has problems with non-standard queries tested here.
484        $this->skipIfEmulated();
485
486        $interestingqueryEntitiesOptions = self::addBinaryFilter('STRING', EdmType::STRING, TableServiceFunctionalTestData::getInterestingGoodStrings());
487        foreach($interestingqueryEntitiesOptions as $options)  {
488            $this->queryEntitiesWorker($options);
489        }
490    }
491
492    /**
493    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
494    */
495    public function testQueryEntitiesBinaryLevel1()
496    {
497        // The emulator has problems with non-standard queries tested here.
498        $this->skipIfEmulated();
499
500        $interestingqueryEntitiesOptions = self::addBinaryFilter('BINARY', EdmType::BINARY, TableServiceFunctionalTestData::getInterestingGoodBinaries());
501        foreach($interestingqueryEntitiesOptions as $options)  {
502            $this->queryEntitiesWorker($options);
503        }
504    }
505
506    /**
507    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
508    */
509    public function testQueryEntitiesLevel2()
510    {
511        // The emulator has problems with non-standard queries tested here.
512        $this->skipIfEmulated();
513
514        $interestingqueryEntitiesOptions = self::getInterestingQueryEntitiesOptionsOfDepth(2);
515        foreach($interestingqueryEntitiesOptions as $options)  {
516            $this->queryEntitiesWorker($options);
517        }
518    }
519
520    /**
521    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
522    */
523    public function testQueryEntitiesLevel3()
524    {
525        // The emulator has problems with non-standard queries tested here.
526        $this->skipIfEmulated();
527
528        $interestingqueryEntitiesOptions = self::getInterestingQueryEntitiesOptionsOfDepth(3);
529        foreach($interestingqueryEntitiesOptions as $options)  {
530            $this->queryEntitiesWorker($options);
531        }
532    }
533
534    /**
535    * @covers WindowsAzure\Table\TableRestProxy::queryEntities
536    */
537    private function queryEntitiesWorker($options)
538    {
539        $table = TableServiceFunctionalTestData::$testTableNames[0];
540
541        try {
542            $ret = (is_null($options) ? $this->restProxy->queryEntities($table) : $this->restProxy->queryEntities($table, $options));
543
544            if (is_null($options)) {
545                $options = new QueryEntitiesOptions();
546            }
547
548            if (!is_null($options->getQuery()) && !is_null($options->getQuery()->getTop()) && $options->getQuery()->getTop() <= 0) {
549                $this->assertTrue(false, 'Expect non-positive Top in $options->query to throw');
550            }
551
552            $this->verifyqueryEntitiesWorker($ret, $options);
553
554            // In principle, should check if there is a continuation, then use it.
555            // However, the user cannot easily control when this happens, so I'm
556            // not sure how useful it is.
557            // To test that scenario, set NextTable in the $options.
558        } catch (ServiceException $e) {
559            if (!is_null($options->getQuery()) && !is_null($options->getQuery()->getTop()) && $options->getQuery()->getTop() <= 0) {
560                $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode');
561            } else {
562                $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode');
563            }
564        }
565    }
566
567    private function verifyqueryEntitiesWorker($ret, $options)
568    {
569        $this->assertNotNull($ret->getEntities(), 'getTables');
570
571        $expectedData = array();
572        foreach(self::$entitiesInTable as $e)  {
573            array_push($expectedData, $e);
574        }
575
576        sort($expectedData);
577
578        $projected = false;
579
580        if (!is_null($options->getNextPartitionKey()) && !is_null($options->getNextRowKey())) {
581            $expectedDataTmp = array();
582            foreach($expectedData as $e)  {
583                if ( ($e->getPartitionKey() >  $options->getNextPartitionKey()) ||
584                    (($e->getPartitionKey() == $options->getNextPartitionKey()) &&
585                     ($e->getRowKey()       >= $options->getNextRowKey()))) {
586                    array_push($expectedDataTmp, $e);
587                }
588            }
589            $expectedData = $expectedDataTmp;
590        }
591
592        $q = $options->getQuery();
593        $expectedFilter = $q->getFilter();
594        $projected = (count($q->getSelectFields()) != 0);
595
596        $expectedData = TableServiceFunctionalTestUtils::filterEntityList($expectedFilter, $expectedData);
597
598        if (!is_null($q->getTop()) && $q->getTop() < count($expectedData)) {
599            $expectedDataTmp = array();
600            for ($i = 0; $i < $q->getTop(); $i++) {
601                array_push($expectedDataTmp, $expectedData[$i]);
602            }
603            $expectedData = $expectedDataTmp;
604        }
605
606        $this->compareEntityLists($ret->getEntities(), $expectedData, $projected);
607    }
608
609    private function compareEntityLists($actualData, $expectedData, $projected)
610    {
611        // Need to sort the lists.
612        $actualData = self::sortEntitiesByCompositeKey($actualData);
613        $expectedData = self::sortEntitiesByCompositeKey($expectedData);
614
615        $this->assertEquals(count($expectedData), count($actualData), 'count(getEntities)');
616        for ($i = 0; $i < count($expectedData); $i++) {
617            $e1 = $expectedData[$i];
618            $e2 = $actualData[$i];
619            if (!$projected) {
620                $this->assertTrue(
621                    ($e1->getPartitionKey() == $e2->getPartitionKey()) && ($e1->getRowKey() == $e2->getRowKey()),
622                    '(' . $e1->getPartitionKey() . ',' . $e1->getRowKey() . ') == (' . $e2->getPartitionKey() . ',' . $e2->getRowKey() . ')');
623            }
624            // Don't need to verify the whole entities, done elsewhere
625        }
626    }
627
628    private static function addBinaryFilter($name, $edmType, $values)
629    {
630        $counter = 0;
631        $ret = array();
632        foreach($values as $o)  {
633            $f = self::getBinaryFilterFromIndex($counter, Filter::applyPropertyName($name), Filter::applyConstant($o, $edmType));
634            $q = new Query();
635            $q->setFilter($f);
636            $qeo = new QueryEntitiesOptions();
637            $qeo->setQuery($q);
638            array_push($ret, $qeo);
639            $counter = ($counter + 1) % 6;
640        }
641        return $ret;
642    }
643
644    public static function sortEntitiesByCompositeKey($originalArray)
645    {
646        $tmpArray = array();
647        $isordered = true;
648        $prevIndex = '/';
649        foreach($originalArray as $e) {
650            $index = $e->getPartitionKey() . '/' . $e->getRowKey();
651            $tmpArray[$index] = $e;
652            if ($isordered) {
653                $isordered = $prevIndex <= $index;
654            }
655            $prevIndex = $index;
656        }
657
658        if ($isordered) {
659            return $originalArray;
660        }
661
662        ksort($tmpArray);
663        $ret = array();
664        foreach($tmpArray as $e) {
665            array_push($ret, $e);
666        }
667        return $ret;
668    }
669}
670
671