Daniel Kinzler has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/102466


Change subject: Make ResultBuilder's "indexed mode" work consistently.
......................................................................

Make ResultBuilder's "indexed mode" work consistently.

Change-Id: Ie06743e5409e5fa0d469b4506871bb9b760f6eda
---
M repo/includes/api/RemoveClaims.php
M repo/includes/api/ResultBuilder.php
M repo/tests/phpunit/includes/api/ResultBuilderTest.php
3 files changed, 442 insertions(+), 68 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase 
refs/changes/66/102466/1

diff --git a/repo/includes/api/RemoveClaims.php 
b/repo/includes/api/RemoveClaims.php
index bb83141..ae1a4e4 100644
--- a/repo/includes/api/RemoveClaims.php
+++ b/repo/includes/api/RemoveClaims.php
@@ -70,7 +70,7 @@
 
                $this->saveChanges( $entityContent, $summary );
                $this->getResultBuilder()->markSuccess();
-               $this->getResultBuilder()->addValue( null, $params['claim'], 
'claims', 'claim' );
+               $this->getResultBuilder()->setList( null, 'claims', 
$params['claim'], 'claim' );
 
                wfProfileOut( __METHOD__ );
        }
diff --git a/repo/includes/api/ResultBuilder.php 
b/repo/includes/api/ResultBuilder.php
index 47d78eb..d8aaec6 100644
--- a/repo/includes/api/ResultBuilder.php
+++ b/repo/includes/api/ResultBuilder.php
@@ -40,10 +40,16 @@
         * @var SerializerFactory
         */
        protected $serializerFactory;
+
        /**
         * @var EntityTitleLookup
         */
-       private $entityTitleLookup;
+       protected $entityTitleLookup;
+
+       /**
+        * @var SerializationOptions
+        */
+       protected $options;
 
        /**
         * @param ApiResult $result
@@ -70,6 +76,20 @@
                $this->entityTitleLookup = $entityTitleLookup;
                $this->serializerFactory = $serializerFactory;
                $this->missingEntityCounter = -1;
+
+               $this->options = new SerializationOptions();
+               $this->options->setIndexTags( 
$this->getResult()->getIsRawMode() );
+               $this->options->setOption( EntitySerializer::OPT_SORT_ORDER, 
EntitySerializer::SORT_NONE );
+       }
+
+       /**
+        * Returns the serialization options used by this ResultBuilder.
+        * This can be used to modify the options.
+        *
+        * @return SerializationOptions
+        */
+       public function getOptions() {
+               return $this->options;
        }
 
        /**
@@ -95,32 +115,157 @@
        }
 
        /**
+        * Adds a list of values for the given path and name.
+        * This automatically sets the indexed tag name, if appropriate.
+        *
+        * To set atomic values or records, use setValue() or appendValue().
+        *
         * @see ApiResult::addValue
         * @see ApiResult::setIndexedTagName
+        * @see ResultBuilder::setValue()
+        * @see ResultBuilder::appendValue()
         *
         * @param $path array|string|null
-        * @param $value mixed
         * @param $name string
-        * @param string $tag Tag name
+        * @param $values array
+        * @param string $tag tag name to use for elements of $values
+        *
+        * @throws InvalidArgumentException
         */
-       public function addValue( $path, $value, $name, $tag ){
-               if ( $this->getResult()->getIsRawMode() ) {
-                       $this->getResult()->setIndexedTagName( $value, $tag );
+       public function setList( $path, $name, array $values, $tag ){
+               if ( is_string( $path ) ) {
+                       $path = array( $path );
                }
+
+               if ( !is_array( $path ) && $path !== null ) {
+                       throw new InvalidArgumentException( '$path must be an 
array (or null)' );
+               }
+
+               if ( !is_string( $name ) ) {
+                       throw new InvalidArgumentException( '$name must be a 
string' );
+               }
+
+               if ( !is_string( $tag ) ) {
+                       throw new InvalidArgumentException( '$tag must be a 
string' );
+               }
+
+               if ( $this->options->shouldIndexTags() ) {
+                       $values = array_values( $values );
+               }
+
+               if ( $this->getResult()->getIsRawMode() ) {
+                       $this->getResult()->setIndexedTagName( $values, $tag );
+               }
+
+               $this->getResult()->addValue( $path, $name, $values );
+       }
+
+       /**
+        * Set an atomic value (or record) for the given path and name.
+        * If the value is an array, it should be a record (associative), not a 
list.
+        * For adding lists, use setList().
+        *
+        * @see ResultBuilder::setList()
+        * @see ResultBuilder::appendValue()
+        * @see ApiResult::addValue
+        *
+        * @param $path array|string|null
+        * @param $name string
+        * @param $value mixed
+        *
+        * @throws InvalidArgumentException
+        */
+       public function setValue( $path, $name, $value ){
+               if ( is_string( $path ) ) {
+                       $path = array( $path );
+               }
+
+               if ( !is_array( $path ) && $path !== null ) {
+                       throw new InvalidArgumentException( '$path must be an 
array (or null)' );
+               }
+
+               if ( !is_string( $name ) ) {
+                       throw new InvalidArgumentException( '$name must be a 
string' );
+               }
+
+               if ( is_array( $value ) && isset( $value[0] ) ) {
+                       throw new InvalidArgumentException( '$value must not be 
a list' );
+               }
+
                $this->getResult()->addValue( $path, $name, $value );
+       }
+
+       /**
+        * Appends a value to the list at the given path.
+        * This automatically sets the indexed tag name, if appropriate.
+        *
+        * If the value is an array, it should be associative, not a list.
+        * For adding lists, use setList().
+        *
+        * @see ResultBuilder::setList()
+        * @see ResultBuilder::setValue()
+        * @see ApiResult::addValue
+        * @see ApiResult::setIndexedTagName_internal
+        *
+        * @param $path array|string|null
+        * @param $key int|string|null the key to use when appending, or null 
for automatic.
+        * May be ignored even if given, based on 
$this->options->shouldIndexTags().
+        * @param $value mixed
+        * @param string $tag tag name to use for $value in indexed mode
+        *
+        * @throws InvalidArgumentException
+        */
+       public function appendValue( $path, $key, $value, $tag ){
+               if ( is_string( $path ) ) {
+                       $path = array( $path );
+               }
+
+               if ( !is_array( $path ) && $path !== null ) {
+                       throw new InvalidArgumentException( '$path must be an 
array (or null)' );
+               }
+
+               if ( $key !== null && !is_string( $key ) && !is_int( $key ) ) {
+                       throw new InvalidArgumentException( '$key must be a 
string, int, or null' );
+               }
+
+               if ( !is_string( $tag ) ) {
+                       throw new InvalidArgumentException( '$tag must be a 
string' );
+               }
+
+               if ( is_array( $value ) && isset( $value[0] ) ) {
+                       throw new InvalidArgumentException( '$value must not be 
a list' );
+               }
+
+               if ( $this->options->shouldIndexTags() ) {
+                       $key = null;
+               }
+
+               $this->getResult()->addValue( $path, $key, $value );
+
+               if ( $this->getResult()->getIsRawMode() && !is_string( $key ) ) 
{
+                       $this->getResult()->setIndexedTagName_internal( $path, 
$tag );
+               }
        }
 
        /**
         * Get serialized entity for the EntityRevision and add it to the result
         *
         * @param EntityRevision $entityRevision
-        * @param SerializationOptions $options
+        * @param SerializationOptions|null $options
         * @param array $props
         */
-       public function addEntityRevision( EntityRevision $entityRevision, 
$options, array $props = array() ) {
+       public function addEntityRevision( EntityRevision $entityRevision, 
SerializationOptions $options = null, array $props = array() ) {
                $entity = $entityRevision->getEntity();
                $entityId = $entity->getId();
                $record = array();
+
+               if ( $options ) {
+                       $serializerOptions = new SerializationOptions();
+                       $serializerOptions->merge( $this->options );
+                       $serializerOptions->merge( $options );
+               } else {
+                       $serializerOptions = $this->options;
+               }
 
                //if there are no props defined only return type and id..
                if ( $props === array() ) {
@@ -137,18 +282,13 @@
                        }
 
                        $serializerFactory = new SerializerFactory();
-                       $entitySerializer = 
$serializerFactory->newSerializerForObject( $entity, $options );
+                       $entitySerializer = 
$serializerFactory->newSerializerForObject( $entity, $serializerOptions );
                        $entitySerialization = 
$entitySerializer->getSerialized( $entity );
 
                        $record = array_merge( $record, $entitySerialization );
                }
 
-               // key should be numeric to get the correct behavior
-               // note that this setting depends upon 
"setIndexedTagName_internal"
-               // NOTE see https://bugzilla.wikimedia.org/show_bug.cgi?id=57529
-               $resultName = !$this->getResult()->getIsRawMode() ? 
$entityId->getSerialization() : null;
-
-               $this->getResult()->addValue( array( 'entities' ), $resultName, 
$record );
+               $this->appendValue( array( 'entities' ), 
$entityId->getSerialization(), $record, 'entity' );
        }
 
        /**
@@ -162,11 +302,11 @@
        public function addBasicEntityInformation( EntityId $entityId, $path, 
$forceNumericId = false ){
                if( $forceNumericId ) {
                        //FIXME: this is a very nasty hack as we presume IDs 
are always prefixed by a single letter
-                       $this->getResult()->addValue( $path, 'id', substr( 
$entityId->getSerialization(), 1 ) );
+                       $this->setValue( $path, 'id', substr( 
$entityId->getSerialization(), 1 ) );
                } else {
-                       $this->getResult()->addValue( $path, 'id', 
$entityId->getSerialization() );
+                       $this->setValue( $path, 'id', 
$entityId->getSerialization() );
                }
-               $this->getResult()->addValue( $path, 'type', 
$entityId->getEntityType() );
+               $this->setValue( $path, 'type', $entityId->getEntityType() );
        }
 
        /**
@@ -178,12 +318,10 @@
         * @param array|string $path where the data is located
         */
        public function addLabels( array $labels, $path ) {
-               $options = new SerializationOptions();
-               $options->setIndexTags( $this->getResult()->getIsRawMode() );
-               $labelSerializer = 
$this->serializerFactory->newLabelSerializer( $options );
+               $labelSerializer = 
$this->serializerFactory->newLabelSerializer( $this->options );
 
-               $value = $labelSerializer->getSerialized( $labels );
-               $this->addValue( $path, $value, 'labels', 'label' );
+               $values = $labelSerializer->getSerialized( $labels );
+               $this->setList( $path, 'labels', $values, 'label' );
        }
 
        /**
@@ -195,12 +333,10 @@
         * @param array|string $path where the data is located
         */
        public function addDescriptions( array $descriptions, $path ) {
-               $options = new SerializationOptions();
-               $options->setIndexTags( $this->getResult()->getIsRawMode() );
-               $descriptionSerializer = 
$this->serializerFactory->newDescriptionSerializer( $options );
+               $descriptionSerializer = 
$this->serializerFactory->newDescriptionSerializer( $this->options );
 
-               $value = $descriptionSerializer->getSerialized( $descriptions );
-               $this->addValue( $path, $value, 'descriptions', 'description' );
+               $values = $descriptionSerializer->getSerialized( $descriptions 
);
+               $this->setList( $path, 'descriptions', $values, 'description' );
        }
 
        /**
@@ -212,11 +348,9 @@
         * @param array|string $path where the data is located
         */
        public function addAliases( array $aliases, $path ) {
-               $options = new SerializationOptions();
-               $options->setIndexTags( $this->getResult()->getIsRawMode() );
-               $aliasSerializer = 
$this->serializerFactory->newAliasSerializer( $options );
-               $value = $aliasSerializer->getSerialized( $aliases );
-               $this->addValue( $path, $value, 'aliases', 'alias' );
+               $aliasSerializer = 
$this->serializerFactory->newAliasSerializer( $this->options );
+               $values = $aliasSerializer->getSerialized( $aliases );
+               $this->setList( $path, 'aliases', $values, 'alias' );
        }
 
        /**
@@ -230,8 +364,7 @@
         */
        public function addSiteLinks( array $siteLinks, $path, $options = null 
) {
                $serializerOptions = new SerializationOptions();
-               $serializerOptions->setOption( 
EntitySerializer::OPT_SORT_ORDER, EntitySerializer::SORT_NONE );
-               $serializerOptions->setIndexTags( 
$this->getResult()->getIsRawMode() );
+               $serializerOptions->merge( $this->options );
 
                if ( is_array( $options ) ) {
                        if ( in_array( EntitySerializer::SORT_ASC, $options ) ) 
{
@@ -250,14 +383,10 @@
                }
 
                $siteLinkSerializer = 
$this->serializerFactory->newSiteLinkSerializer( $serializerOptions );
-               $value = $siteLinkSerializer->getSerialized( $siteLinks );
+               $values = $siteLinkSerializer->getSerialized( $siteLinks );
 
-               if ( $value !== array() ) {
-                       if ( $this->getResult()->getIsRawMode() ) {
-                               $this->getResult()->setIndexedTagName( $value, 
'sitelink' );
-                       }
-
-                       $this->getResult()->addValue( $path, 'sitelinks', 
$value );
+               if ( $values !== array() ) {
+                       $this->setList( $path, 'sitelinks', $values, 'sitelink' 
);
                }
        }
 
@@ -270,12 +399,10 @@
         * @param array|string $path where the data is located
         */
        public function addClaims( array $claims, $path ) {
-               $options = new SerializationOptions();
-               $options->setIndexTags( $this->getResult()->getIsRawMode() );
-               $claimsSerializer = 
$this->serializerFactory->newClaimsSerializer( $options );
+               $claimsSerializer = 
$this->serializerFactory->newClaimsSerializer( $this->options );
 
-               $value = $claimsSerializer->getSerialized( new Claims( $claims 
) );
-               $this->addValue( $path, $value, 'claims', 'claim' );
+               $values = $claimsSerializer->getSerialized( new Claims( $claims 
) );
+               $this->setList( $path, 'claims', $values, 'claim' );
        }
 
        /**
@@ -283,10 +410,14 @@
         * @param Claim $claim
         */
        public function addClaim( Claim $claim ) {
-               $options = new SerializationOptions();
-               $serializer = $this->serializerFactory->newClaimSerializer( 
$options );
+               $serializer = $this->serializerFactory->newClaimSerializer( 
$this->options );
+
+               //TODO: this is currently only used to add a Claim as the top 
level structure,
+               //      with a null path and a fixed name. Would be nice to 
also allow claims
+               //      to be added to a list, using a path and a id key or 
index.
+
                $value = $serializer->getSerialized( $claim );
-               $this->addValue( null, $value, 'claim', 'claim' );
+               $this->setValue( null, 'claim', $value );
        }
 
        /**
@@ -294,10 +425,14 @@
         * @param Reference $reference
         */
        public function addReference( Reference $reference ) {
-               $options = new SerializationOptions();
-               $serializer = $this->serializerFactory->newReferenceSerializer( 
$options );
+               $serializer = $this->serializerFactory->newReferenceSerializer( 
$this->options );
+
+               //TODO: this is currently only used to add a Reference as the 
top level structure,
+               //      with a null path and a fixed name. Would be nice to 
also allow references
+               //      to be added to a list, using a path and a id key or 
index.
+
                $value = $serializer->getSerialized( $reference );
-               $this->addValue( null, $value, 'reference', 'reference' );
+               $this->setValue( null, 'reference', $value );
        }
 
        /**
@@ -305,12 +440,13 @@
         * @param array $missingDetails array containing key value pair missing 
details
         */
        public function addMissingEntity( $missingDetails ){
-               //@todo fix Bug 45509 (useless missing attribute in xml...)
-               $this->getResult()->addValue(
+               $this->appendValue(
                        'entities',
-                       (string)$this->missingEntityCounter,
-                       array_merge( $missingDetails, array( 'missing' => "" ) )
+                       $this->missingEntityCounter,
+                       array_merge( $missingDetails, array( 'missing' => "" ) 
),
+                       'entity'
                );
+
                $this->missingEntityCounter--;
        }
 
@@ -320,7 +456,7 @@
         * @param string $name
         */
        public function addNormalizedTitle( $from, $to, $name = 'n' ){
-               $this->getResult()->addValue(
+               $this->setValue(
                        'normalized',
                        $name,
                        array( 'from' => $from, 'to' => $to )
@@ -348,13 +484,14 @@
                        ? $statusValue['revision'] : null;
 
                if ( $revision ) {
-                       $this->getResult()->addValue(
+                       $this->setValue(
                                $path,
                                'lastrevid',
                                intval( $revision->getId() )
                        );
                }
-
        }
 
+       public function addValue() {
+       }
 }
diff --git a/repo/tests/phpunit/includes/api/ResultBuilderTest.php 
b/repo/tests/phpunit/includes/api/ResultBuilderTest.php
index f58eab0..73a41ae 100644
--- a/repo/tests/phpunit/includes/api/ResultBuilderTest.php
+++ b/repo/tests/phpunit/includes/api/ResultBuilderTest.php
@@ -12,12 +12,11 @@
 use Wikibase\DataModel\SimpleSiteLink;
 use Wikibase\EntityRevision;
 use Wikibase\Item;
-use Wikibase\ItemContent;
 use Wikibase\Lib\Serializers\SerializationOptions;
+use Wikibase\Lib\Serializers\SerializerFactory;
 use Wikibase\PropertySomeValueSnak;
 use Wikibase\PropertyValueSnak;
 use Wikibase\Reference;
-use Wikibase\Repo\WikibaseRepo;
 use Wikibase\SnakList;
 
 /**
@@ -25,18 +24,25 @@
  * @todo mock and inject serializers to avoid massive expected output?
  *
  * @group Wikibase
+ * @group WikibaseAPI
  *
  * @licence GNU GPL v2+
  * @author Adam Shorland
  */
 class ResultBuilderTest extends PHPUnit_Framework_TestCase {
 
-       protected function getDefaultResult(){
+       protected function getDefaultResult( $indexedMode = false ){
                $apiMain =  $this->getMockBuilder( 'ApiMain' 
)->disableOriginalConstructor()->getMockForAbstractClass();
-               return new ApiResult( $apiMain );
+               $result = new ApiResult( $apiMain );
+
+               if ( $indexedMode ) {
+                       $result->setRawMode();
+               }
+
+               return $result;
        }
 
-       protected function getResultBuilder( $result ){
+       protected function getResultBuilder( $result, $options = null ){
                $mockTitle = $this->getMockBuilder( '\Title' )
                        ->disableOriginalConstructor()
                        ->getMock();
@@ -55,10 +61,21 @@
                        ->method( 'getTitleForId' )
                        ->will(  $this->returnValue( $mockTitle ) );
 
-               return new ResultBuilder(
+               $serializerFactory = new SerializerFactory();
+
+               $builder = new ResultBuilder(
                        $result,
-                       $mockEntityTitleLookup
+                       $mockEntityTitleLookup,
+                       $serializerFactory
                );
+
+               if ( is_array( $options ) ) {
+                       $builder->getOptions()->setOptions( $options );
+               } elseif ( $options instanceof SerializationOptions ) {
+                       $builder->getOptions()->merge( $options );
+               }
+
+               return $builder;
        }
 
        public function testCanConstruct(){
@@ -526,4 +543,224 @@
 
                $this->assertEquals( $expected, $result->getData() );
        }
+
+       public function provideSetList() {
+               return array(
+                       'null path' => array( null, 'foo', array(), 'letter', 
false, array( 'foo' => array() ) ),
+
+                       'empty path' => array( array(), 'foo', array( 'x', 'y' 
), 'letter', false,
+                               array(
+                                       'foo' => array( 'x', 'y' )
+                       ) ),
+
+                       'string path' => array( 'ROOT', 'foo', array( 'x', 'y' 
), 'letter', false,
+                               array(
+                                       'ROOT' => array(
+                                               'foo' => array( 'x', 'y' ) )
+                               ) ),
+
+                       'actual path' => array( array( 'one', 'two' ), 'foo', 
array( 'X' => 'x', 'Y' => 'y' ), 'letter', false,
+                               array(
+                                       'one' => array(
+                                               'two' => array(
+                                                       'foo' => array( 'X' => 
'x', 'Y' => 'y' ) ) )
+                               ) ),
+
+                       'indexed' => array( 'ROOT', 'foo', array( 'X' => 'x', 
'Y' => 'y' ), 'letter', true,
+                               array(
+                                       'ROOT' => array(
+                                               'foo' => array( 'x', 'y', 
'_element' => 'letter' ) )
+                               ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSetList
+        */
+       public function testSetList( $path, $name, array $values, $tag, 
$indexed, $expected ) {
+               $result = $this->getDefaultResult( $indexed );
+               $builder = $this->getResultBuilder( $result );
+
+               $builder->setList( $path, $name, $values, $tag );
+               $this->assertResultStructure( $expected, $result->getData() );
+       }
+
+       public function provideSetList_InvalidArgument() {
+               return array(
+                       'null name' => array( 'ROOT', null, array( 10, 20 ), 
'Q' ),
+                       'int name' => array( 'ROOT', 6, array( 10, 20 ), 'Q' ),
+                       'array name' => array( 'ROOT', array( 'x' ), array( 10, 
20 ), 'Q' ),
+
+                       'null tag' => array( 'ROOT', 'foo', array( 10, 20 ), 
null ),
+                       'int tag' => array( 'ROOT', 'foo', array( 10, 20 ), 6 ),
+                       'array tag' => array( 'ROOT', 'foo', array( 10, 20 ), 
array( 'x' ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSetList_InvalidArgument
+        */
+       public function testSetList_InvalidArgument( $path, $name, array 
$values, $tag ) {
+               $result = $this->getDefaultResult();
+               $builder = $this->getResultBuilder( $result );
+
+               $this->setExpectedException( 'InvalidArgumentException' );
+               $builder->setList( $path, $name, $values, $tag );
+       }
+
+       public function provideSetValue() {
+               return array(
+                       'null path' => array( null, 'foo', 'value', false, 
array( 'foo' => 'value' ) ),
+
+                       'empty path' => array( array(), 'foo', 'value', false,
+                               array(
+                                       'foo' => 'value'
+                               ) ),
+
+                       'string path' => array( 'ROOT', 'foo', 'value', false,
+                               array(
+                                       'ROOT' => array( 'foo' => 'value' )
+                               ) ),
+
+                       'actual path' => array( array( 'one', 'two' ), 'foo', 
array( 'X' => 'x', 'Y' => 'y' ), true,
+                               array(
+                                       'one' => array(
+                                               'two' => array(
+                                                       'foo' => array( 'X' => 
'x', 'Y' => 'y' ) ) )
+                               ) ),
+
+                       'indexed' => array( 'ROOT', 'foo', 'value', true,
+                               array(
+                                       'ROOT' => array( 'foo' => 'value' )
+                               ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSetValue
+        */
+       public function testSetValue( $path, $name, $value, $indexed, $expected 
) {
+               $result = $this->getDefaultResult( $indexed );
+               $builder = $this->getResultBuilder( $result );
+
+               $builder->setValue( $path, $name, $value );
+               $this->assertResultStructure( $expected, $result->getData() );
+       }
+
+       public function provideSetValue_InvalidArgument() {
+               return array(
+                       'null name' => array( 'ROOT', null, 'X' ),
+                       'int name' => array( 'ROOT', 6, 'X' ),
+                       'array name' => array( 'ROOT', array( 'x' ), 'X' ),
+
+                       'list value' => array( 'ROOT', 'foo', array( 10, 20 ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSetValue_InvalidArgument
+        */
+       public function testSetValue_InvalidArgument( $path, $name, $value ) {
+               $result = $this->getDefaultResult();
+               $builder = $this->getResultBuilder( $result );
+
+               $this->setExpectedException( 'InvalidArgumentException' );
+               $builder->setValue( $path, $name, $value );
+       }
+
+       public function provideAppendValue() {
+               return array(
+                       'null path' => array( null, null, 'value', 'letter', 
false, array( 'value' ) ),
+
+                       'empty path' => array( array(), null, 'value', 
'letter', false,
+                               array( 'value' )
+                       ),
+
+                       'string path' => array( 'ROOT', null, 'value', 
'letter', false,
+                               array(
+                                       'ROOT' => array( 'value' )
+                               ) ),
+
+                       'actual path' => array( array( 'one', 'two' ), null, 
array( 'X' => 'x', 'Y' => 'y' ), 'letter', false,
+                               array(
+                                       'one' => array(
+                                               'two' => array( array( 'X' => 
'x', 'Y' => 'y' ) ) )
+                               ) ),
+
+
+                       'int key' => array( 'ROOT', -2, 'value', 'letter', 
false,
+                               array(
+                                       'ROOT' => array( -2 => 'value' )
+                               ) ),
+
+                       'string key' => array( 'ROOT', 'Q7', 'value', 'letter', 
false,
+                               array(
+                                       'ROOT' => array( 'Q7' => 'value' )
+                               ) ),
+
+
+                       'null key indexed' => array( 'ROOT', null, 'value', 
'letter', true,
+                               array(
+                                       'ROOT' => array( 'value', '_element' => 
'letter' )
+                               ) ),
+
+                       'int key indexed' => array( 'ROOT', -2, 'value', 
'letter', true,
+                               array(
+                                       'ROOT' => array( 'value', '_element' => 
'letter' )
+                               ) ),
+
+                       'string key indexed' => array( 'ROOT', 'Q7', 'value', 
'letter', true,
+                               array(
+                                       'ROOT' => array( 'value', '_element' => 
'letter' )
+                               ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideAppendValue
+        */
+       public function testAppendValue( $path, $key, $value, $tag, $indexed, 
$expected ) {
+               $result = $this->getDefaultResult( $indexed );
+               $builder = $this->getResultBuilder( $result );
+
+               $builder->appendValue( $path, $key, $value, $tag );
+               $this->assertResultStructure( $expected, $result->getData() );
+       }
+
+       public function provideAppendValue_InvalidArgument() {
+               return array(
+                       'list value' => array( 'ROOT', null, array( 1, 2, 3 ), 
'Q' ),
+                       'array key' => array( 'ROOT', array( 'x' ), 'value', 
'Q' ),
+
+                       'null tag' => array( 'ROOT', 'foo', 'value', null ),
+                       'int tag' => array( 'ROOT', 'foo', 'value', 6 ),
+                       'array tag' => array( 'ROOT', 'foo', 'value', array( 
'x' ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideAppendValue_InvalidArgument
+        */
+       public function testAppendValue_InvalidArgument( $path, $key, $value, 
$tag ) {
+               $result = $this->getDefaultResult();
+               $builder = $this->getResultBuilder( $result );
+
+               $this->setExpectedException( 'InvalidArgumentException' );
+               $builder->appendValue( $path, $key, $value, $tag );
+       }
+
+       protected function assertResultStructure( $expected, $actual, $path = 
null ) {
+               foreach ( $expected as $key => $value ) {
+                       $this->assertArrayHasKey( $key, $actual, $path );
+
+                       if ( is_array( $value ) ) {
+                               $this->assertInternalType( 'array', 
$actual[$key], $path );
+
+                               $subKey = $path === null ? $key : $path . '/' . 
$key;
+                               $this->assertResultStructure( $value, 
$actual[$key], $subKey );
+                       } else {
+                               $this->assertEquals( $value, $actual[$key] );
+                       }
+               }
+       }
 }
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/102466
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie06743e5409e5fa0d469b4506871bb9b760f6eda
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to